Continuously generate Angular API client from Django REST Framework APIs using OpenAPI spec

Posted by Harald Nezbeda on Tue 12 November 2024

This article is a continuation of Create OpenAPI spec for Django REST Framework APIs and Automatically create an API client from an OpenAPI spec, where creating OpenAPI specs for Django REST Framework APIs and generating Angular API clients from them were discussed.

I recently automated this entire process using GitHub Actions. Here's how it works: whenever a new release is published on the Django backend, the workflow automatically:

  1. Generates the OpenAPI specification
  2. Uploads it to the GitHub release
  3. Generates the Angular API client
  4. Publishes it to NPM

Snypy NPM Publish Workflow

Implementation Details

The workflow requires some secrets:

  • NPM_TOKEN: Must be configured as a repository secret in GitHub. This token is used for publishing the Angular API client to NPM and can be generated from your NPM account settings.
  • GITHUB_TOKEN: Automatically provided by GitHub Actions, used for uploading the OpenAPI spec to the release.

They need to be added to the repository secrets in the GitHub repository settings.

The workflow leverages the ubuntu-latest runner, which comes with Docker pre-installed. This allows us to reuse the Docker image from the previous article for generating the Angular API client.

You can see the complete implementation in these pull requests:

Automated updates

As an added benefit, with dependabot enabled in the repository, it automatically creates pull requests to update the Angular API client in the frontend repository whenever a new version is published.

Full Workflow Code

Since this is probably going to change in future, here is the code of the current workflow:

name: Generate OpenAPI
on:
  release:
    types: [published]

jobs:
  openapi:
    runs-on: ubuntu-latest
    env:
      DEBUG: True
      RUN_MODE: production
      SECRET_KEY: changeme!
      ALLOWED_HOSTS: localhost,127.0.0.1
      DATABASE_URL: sqlite:////tmp/db.sqlite3
      CORS_ORIGIN_WHITELIST: "http://localhost,http://127.0.0.1"
      CSRF_TRUSTED_ORIGINS: "http://localhost"
      REGISTER_VERIFICATION_URL: "http://localhost:4200/verify-user/"
      RESET_PASSWORD_VERIFICATION_URL: "http://localhost:4200/reset-password/"
      REGISTER_EMAIL_VERIFICATION_URL: "http://localhost:4200/verify-email/"

    strategy:
      fail-fast: false

    steps:
    - uses: actions/checkout@v4

    - name: Set up Python
      uses: actions/setup-python@v5
      with:
        python-version: "3.12"

    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install -r requirements/base.txt

    - name: Generate OpenAPI
      run: python manage.py spectacular --file schema.yml
      working-directory: snypy

    - name: Upload to release
      uses: JasonEtco/upload-to-release@master
      with:
        args: ./snypy/schema.yml text/yaml
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

    - name: Get release version
      id: get_version
      run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT

    - name: Set up Node.js
      uses: actions/setup-node@v4
      with:
        node-version: '22'
        registry-url: 'https://registry.npmjs.org'

    - name: Verify Docker installation
      run: |
        docker --version
        docker pull openapitools/openapi-generator-cli

    - name: Generate TypeScript Client
      run: |
        docker run -u $(id -u):$(id -g) --rm -v "${PWD}:/local" openapitools/openapi-generator-cli generate \
          -i https://github.com/snypy/snypy-backend/releases/download/${{ steps.get_version.outputs.VERSION }}/schema.yml \
          -g typescript-angular \
          -o /local/client \
          --additional-properties=npmName=@snypy/rest-client,npmVersion=${{ steps.get_version.outputs.VERSION }},useSingleRequestParameter=true

    - name: Build TypeScript Client
      run: |
        cd client
        npm install --force
        npm run build

    - name: Publish to NPM
      run: |
        cd client/dist
        npm publish --access public
      env:
        NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}