Supercharge Your FastAPI: Master CI/CD with GitHub Actions for Seamless Development

GitHub Actions automates FastAPI CI/CD. Tests, lints, and deploys code. Catches bugs early, ensures deployment readiness. Improves code quality, saves time, enables confident releases.

Supercharge Your FastAPI: Master CI/CD with GitHub Actions for Seamless Development

Setting up a CI/CD pipeline with GitHub Actions for FastAPI is a game-changer for developers. It streamlines your workflow, catches bugs early, and ensures your app is always ready for deployment. Let’s dive into how to make this happen.

First things first, you’ll need a FastAPI project and a GitHub repository. If you don’t have these yet, go ahead and create them. FastAPI is awesome for building APIs quickly, and GitHub is perfect for version control and hosting our CI/CD pipeline.

Now, let’s create a basic FastAPI app to work with. Here’s a simple example:

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def root():
    return {"message": "Hello, World!"}

Save this as main.py in your project root. We’ll use this as our starting point.

Next, we need to set up our GitHub Actions workflow. Create a new file in your repository at .github/workflows/main.yml. This is where we’ll define our CI/CD pipeline.

Here’s a basic workflow to get us started:

name: FastAPI CI/CD

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: Set up Python
      uses: actions/setup-python@v2
      with:
        python-version: '3.9'
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install -r requirements.txt
    - name: Run tests
      run: pytest

This workflow will run every time we push to the main branch or create a pull request. It sets up Python, installs our dependencies, and runs our tests.

Speaking of tests, we should probably write some! Create a new file called test_main.py in your project root:

from fastapi.testclient import TestClient
from main import app

client = TestClient(app)

def test_read_root():
    response = client.get("/")
    assert response.status_code == 200
    assert response.json() == {"message": "Hello, World!"}

This test checks that our root endpoint returns the expected response. Make sure to add pytest and requests to your requirements.txt file.

Now, every time we push changes, GitHub Actions will run our tests automatically. But we can do better than that. Let’s add some more checks to our pipeline.

We can add linting to ensure our code follows best practices. Let’s update our workflow:

name: FastAPI CI/CD

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: Set up Python
      uses: actions/setup-python@v2
      with:
        python-version: '3.9'
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install -r requirements.txt
    - name: Lint with flake8
      run: |
        pip install flake8
        flake8 .
    - name: Run tests
      run: pytest

Don’t forget to add flake8 to your requirements.txt. Now our pipeline will check for code style issues as well as running tests.

But what about deployment? That’s where the CD part of CI/CD comes in. Let’s add a deployment step to our workflow. We’ll use Heroku as an example, but you could adapt this to any cloud platform.

First, we need to add some secrets to our GitHub repository. Go to your repository settings, then to “Secrets”, and add your Heroku API key as HEROKU_API_KEY and your app name as HEROKU_APP_NAME.

Now, let’s update our workflow again:

name: FastAPI CI/CD

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: Set up Python
      uses: actions/setup-python@v2
      with:
        python-version: '3.9'
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install -r requirements.txt
    - name: Lint with flake8
      run: |
        pip install flake8
        flake8 .
    - name: Run tests
      run: pytest

  deploy:
    needs: test
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main' && github.event_name == 'push'
    steps:
    - uses: actions/checkout@v2
    - uses: akhileshns/[email protected]
      with:
        heroku_api_key: ${{secrets.HEROKU_API_KEY}}
        heroku_app_name: ${{secrets.HEROKU_APP_NAME}}
        heroku_email: "[email protected]"

This new job will deploy our app to Heroku, but only if we’re pushing to the main branch and all tests have passed.

Now we’ve got a full CI/CD pipeline! Every time we push to main, our code will be tested, linted, and if everything passes, deployed automatically.

But wait, there’s more we can do to make our pipeline even better. Let’s add some performance testing to make sure our API is fast.

We can use locust for this. First, add locust to your requirements.txt. Then create a new file called locustfile.py in your project root:

from locust import HttpUser, task, between

class QuickstartUser(HttpUser):
    wait_time = between(1, 5)

    @task
    def hello_world(self):
        self.client.get("/")

Now let’s update our workflow one more time:

name: FastAPI CI/CD

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: Set up Python
      uses: actions/setup-python@v2
      with:
        python-version: '3.9'
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install -r requirements.txt
    - name: Lint with flake8
      run: |
        pip install flake8
        flake8 .
    - name: Run tests
      run: pytest
    - name: Run performance tests
      run: |
        pip install locust
        locust --headless -f locustfile.py --host http://localhost:8000 --users 10 --spawn-rate 1 -r 1 --run-time 1m

  deploy:
    needs: test
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main' && github.event_name == 'push'
    steps:
    - uses: actions/checkout@v2
    - uses: akhileshns/[email protected]
      with:
        heroku_api_key: ${{secrets.HEROKU_API_KEY}}
        heroku_app_name: ${{secrets.HEROKU_APP_NAME}}
        heroku_email: "[email protected]"

Now our pipeline is not only testing functionality, but also performance!

One more thing we can add is security scanning. Let’s use bandit for this. Add bandit to your requirements.txt and update the workflow one last time:

name: FastAPI CI/CD

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: Set up Python
      uses: actions/setup-python@v2
      with:
        python-version: '3.9'
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install -r requirements.txt
    - name: Lint with flake8
      run: |
        pip install flake8
        flake8 .
    - name: Run tests
      run: pytest
    - name: Run performance tests
      run: |
        pip install locust
        locust --headless -f locustfile.py --host http://localhost:8000 --users 10 --spawn-rate 1 -r 1 --run-time 1m
    - name: Run security scan
      run: |
        pip install bandit
        bandit -r . -f custom

  deploy:
    needs: test
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main' && github.event_name == 'push'
    steps:
    - uses: actions/checkout@v2
    - uses: akhileshns/[email protected]
      with:
        heroku_api_key: ${{secrets.HEROKU_API_KEY}}
        heroku_app_name: ${{secrets.HEROKU_APP_NAME}}
        heroku_email: "[email protected]"

And there you have it! A comprehensive CI/CD pipeline for your FastAPI project. Every time you push changes, your code will be linted, tested for functionality and performance, scanned for security issues, and if everything passes, automatically deployed.

This setup will save you tons of time and headaches. No more manual deployments or discovering bugs after they’ve made it to production. Your code quality will improve, and you’ll be able to deploy with confidence.

Remember, this is just a starting point. You can customize this pipeline to fit your specific needs. Maybe you want to add code coverage checks, or integrate with a different cloud provider. The possibilities are endless!

One tip I’ve found helpful is to run these checks locally before pushing. You can use pre-commit hooks to run linting and tests automatically before each commit. This catches issues even earlier in the development process.

Also, don’t forget to keep your dependencies up to date. You can add a step to your pipeline to check for outdated packages and create pull requests automatically.

As your project grows, you might want to consider splitting your tests into different jobs. You could have separate jobs for unit tests, integration tests, and end-to-end tests. This allows you to run them in parallel, speeding up your pipeline.

And remember, CI/CD is not just about tools and pipelines. It’s a mindset. Embrace the idea of frequent, small releases. Don’t be afraid to push changes often. With this setup, you’ll catch any issues quickly and be able to roll back easily if needed.

Lastly, make sure to monitor your production environment closely. Set up logging and error tracking to catch any issues that slip through your tests. No matter how good your CI/CD pipeline is, there’s always a chance something could go wrong in production.

Happy coding, and enjoy your new, streamlined development process!