Angular and NestJS Monorepo with GitHub Actions and Docker
In this blog post, we will walk through the process of setting up a monorepo using npm workspaces for an Angular frontend and a NestJS backend. We will also set up a continuous integration and deployment (CI/CD) pipeline using GitHub Actions and Docker.
Setting Up the Monorepo with npm Workspaces
First, let’s set up a monorepo structure using npm workspaces. We’ll have separate directories for our frontend ( Angular), backend (NestJS), and shared code (in my case, DTOs).
Step 1: Create the Project Directory
Create a new directory for your project:
mkdir my-project
cd my-project
Step 2: Initialize npm and Set Up Workspaces
Initialize an npm project and set up workspaces:
npm init -y
Edit the generated package.json
to include the workspaces configuration:
{
"name": "my-project",
"version": "1.0.0",
"private": true,
"workspaces": [
"frontend",
"backend",
"shared"
],
"scripts": {
"build:frontend": "npm run build --workspace frontend",
"build:backend": "npm run build --workspace backend",
"build": "npm run build:frontend && npm run build:backend"
}
}
Step 3: Set Up Angular, NestJS, and Shared Directories
Frontend
Create the Angular project:
npx -p @angular/cli ng new frontend --directory frontend
Backend
Create the NestJS project:
npx -p @nestjs/cli nest new backend --directory backend
Shared Directory
Create the shared directory for common DTOs and interfaces:
mkdir shared
cd shared
npm init -y
tsc --init
Step 4: Add a Shared DTO
Create a shared DTO file:
// shared/dtos/user.dto.ts
export interface UserDto {
id: number;
name: string;
email: string;
}
Step 5: Configure TypeScript Paths
Update tsconfig.json
in both frontend and backend to include the shared directory:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@shared/*": [
"../shared/*"
]
}
}
}
Step 6: Install Shared Package in Frontend and Backend
Run the following commands to install the shared package in both frontend and backend:
cd frontend
npm install ../shared --save
cd ../backend
npm install ../shared --save
cd ..
Setting Up Docker
Next, we will create Dockerfiles for both the frontend and backend applications.
Frontend Dockerfile
Create a Dockerfile
in the frontend
directory:
# Stage 1: Build the Angular application
FROM node:20-alpine AS build
WORKDIR /workspace
# Copy the entire workspace
COPY . .
# Install dependencies using the workspace root package-lock.json
RUN npm ci
# Build the Angular application
RUN npm run build:frontend
# Stage 2: Serve the application with Nginx
FROM nginx:alpine
# Copy the built Angular application from the previous stage
COPY --from=build /workspace/frontend/dist/frontend /usr/share/nginx/html
# Nginx configuration
COPY --from=build /workspace/frontend/nginx.conf /etc/nginx/conf.d/default.conf
# Expose port 80
EXPOSE 80
# Start Nginx
CMD ["nginx", "-g", "daemon off;"]
Backend Dockerfile
Create a Dockerfile
in the backend
directory:
###################
# BUILD FOR PRODUCTION
###################
FROM node:20-alpine AS build
# Set working directory
WORKDIR /workspace
# Copy the entire workspace
COPY . .
# Install dependencies using the workspace root package-lock.json
RUN npm ci
# Build the NestJS application
RUN npm run build:backend
###################
# PRODUCTION
###################
FROM node:20-alpine
# Set working directory
WORKDIR /app
# Copy the bundled code from the build stage to the production image
COPY --from=build /workspace/backend/dist /app/dist
COPY --from=build /workspace/backend/package.json /app/package.json
COPY --from=build /workspace/node_modules /app/node_modules
# Start the server using the production build
CMD ["node", "dist/main.js"]
EXPOSE 8000
Setting Up GitHub Actions for CI/CD
Let’s set up a GitHub Actions workflow to build and deploy both the frontend and backend Docker images.
GitHub Actions Workflow
Create a workflow file at .github/workflows/deploy.yml
:
name: Deploy Frontend and Backend
on:
push:
branches: [ main ]
jobs:
build-and-deploy-frontend:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Docker meta
id: meta-frontend
uses: docker/metadata-action@v4
with:
images: ghcr.io/${{ github.repository }}/frontend
tags: latest
- name: Build and push frontend
uses: docker/build-push-action@v5
with:
context: .
file: ./frontend/Dockerfile
push: true
tags: ${{ steps.meta-frontend.outputs.tags }}
build-and-deploy-backend:
runs-on: ubuntu-latest
needs: build-and-deploy-frontend
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Docker meta
id: meta-backend
uses: docker/metadata-action@v4
with:
images: ghcr.io/${{ github.repository }}/backend
tags: latest
- name: Build and push backend
uses: docker/build-push-action@v5
with:
context: .
file: ./backend/Dockerfile
push: true
tags: ${{ steps.meta-backend.outputs.tags }}
Explanation
-
prepare Job:
- Checks out the code from the repository.
- Logs in to the GitHub Container Registry.
-
build-and-deploy-frontend Job:
- Checks out the code again.
- Generates Docker metadata for the frontend image.
- Builds and pushes the frontend Docker image.
-
build-and-deploy-backend Job:
- Depends on the
prepare
job to ensure the code is checked out and the registry login is successful. - Checks out the code again.
- Generates Docker metadata for the backend image.
- Builds and pushes the backend Docker image.
- Depends on the
Conclusion
In this blog post, we’ve set up a monorepo using npm workspaces for an Angular frontend and a NestJS backend. We created Dockerfiles for both applications and configured a GitHub Actions workflow to build and deploy Docker images for both the frontend and backend. This setup provides a robust CI/CD pipeline for developing and deploying your applications.
Feel free to customize the workflow and Dockerfiles according to your project’s needs.