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!