CI/CDGitHub ActionsMonorepo

Building a CI/CD Pipeline for a Node.js + React Monorepo

Discover how to set up a CI/CD pipeline for your Node.js and React monorepo using GitHub Actions, ensuring efficient builds, tests, and deployment.

Sandaruwan JayasundaraSeptember 6, 202610 min read
Building a CI/CD Pipeline for a Node.js + React Monorepo

Building a CI/CD Pipeline for a Node.js + React Monorepo

In modern web development, having a CI/CD (Continuous Integration/Continuous Deployment) pipeline is crucial for streamlining the process of delivering applications. In this article, we'll walk through setting up a CI/CD pipeline specifically for a Node.js backend and a React frontend that share a monorepo structure. We'll use GitHub Actions as our CI/CD tool, leveraging its capabilities to automate testing and deployment.

Prerequisites

Before we jump in, you should have the following:

  • A basic understanding of Node.js and React.
  • A GitHub account with a repository set up for the monorepo.
  • Familiarity with YAML syntax for configuring GitHub Actions.

Monorepo Structure

First, let's outline our monorepo structure. Here’s a simple example:

/my-monorepo
  /packages
    /backend  (Node.js app)
    /frontend (React app)
  package.json
  yarn.lock

We will be using Yarn Workspaces to manage our dependencies efficiently.

Setting Up Yarn Workspaces

In your root package.json, you should define the workspaces:

{
  "name": "my-monorepo",
  "private": true,
  "workspaces": [
    "packages/*"
  ],
  "scripts": {
    "build": "yarn workspaces run build",
    "test": "yarn workspaces run test"
  }
}

Make sure to initialize both your frontend and backend directories with their own package.json files that define appropriate scripts for build and test.

Configuring GitHub Actions

Now that we have our monorepo structure and package management in place, it's time to set up GitHub Actions.

Creating the Workflow File

In your GitHub repository, create a directory called .github/workflows and add a new file named ci-cd.yml.

Here’s a sample workflow:

name: CI/CD Pipeline

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v2

      - name: Set up Node.js
        uses: actions/setup-node@v2
        with:
          node-version: '16'

      - name: Install dependencies
        run: |
          yarn install

      - name: Run backend tests
        working-directory: ./packages/backend
        run: |
          yarn test

      - name: Run frontend tests
        working-directory: ./packages/frontend
        run: |
          yarn test

      - name: Build applications
        run: |
          yarn build

      - name: Deploy to production
        if: github.ref == 'refs/heads/main'
        run: |
          # Add your deployment script here
          echo "Deploying to production..."

Explanation of the Workflow

  1. Triggers: The pipeline triggers on a push or pull request to the main branch.
  2. Jobs: We define a single job called build that runs on the latest Ubuntu image.
  3. Checkout code: Retrieves the code from the repository.
  4. Set up Node.js: Configures the Node.js version required for the application.
  5. Install dependencies: Uses Yarn to install all dependencies for the projects.
  6. Run tests: Executes tests for both the backend and frontend. Customize these commands based on your test frameworks (like Jest, Mocha, etc.).
  7. Build applications: Builds both applications in preparation for deployment.
  8. Deploy to production: The deployment step is conditional, only running on changes to the main branch.

Deployment Strategy

For the deployment step, you might want to deploy to platforms such as Heroku, AWS, or Vercel. Here’s an example for deploying to Vercel:

  • First, add your Vercel token to the repository secrets in GitHub (Settings -> Secrets and variables). Name it VERCEL_TOKEN.
  • Update the deployment step in your workflow file:
      - name: Deploy to Vercel
        if: github.ref == 'refs/heads/main'
        run: |
          npx vercel --token ${{ secrets.VERCEL_TOKEN }} --prod

This command deploys your app directly to Vercel utilizing the Vercel CLI.

Improving Your CI/CD Pipeline

Caching Dependencies

To speed up the workflow, you can cache your dependencies. Here’s how you can do that in the ci-cd.yml file:

      - name: Cache Node.js modules
        uses: actions/cache@v2
        with:
          path: ~/.npm
          key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-node-

This caching will reduce the time needed for dependency installation by storing the node_modules.

Notifications

Consider adding notifications to your workflow to keep your team updated on the status of the CI/CD process:

      - name: Notify on Failure
        if: failure()
        uses: peter-evans/slack-send@v1
        with:
          slack-webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}
          message: 'CI failed on ${{ github.ref }} at ${{ github.event.head_commit.timestamp }}! Check the logs.'

Conclusion

Setting up a CI/CD pipeline for a Node.js and React monorepo using GitHub Actions streamlines the development workflow, allowing for efficient code integration and deployment. By leveraging the power of GitHub Actions, Yarn Workspaces, and optimizing steps like caching and notifications, we ensure that our applications remain reliable and up-to-date.

Feel free to customize the scripts further to fit your team's needs and explore more features of GitHub Actions for additional improvements. Happy coding!