This project serves as a comprehensive demonstration of building a robust API using cutting-edge technologies, including FastAPI, SQLModel, PostgreSQL, Redis, Next.js, Docker, and Docker Compose. The goal is to showcase the implementation of essential API features, such as rate limiting and pagination, while maintaining a modular and scalable architecture.
-
FastAPI: A modern, fast web framework for building APIs with Python 3.7+ based on standard Python type hints.
-
SQLModel: An SQL database toolkit for FastAPI that provides a simple way to interact with databases using Python models.
-
PostgreSQL: A powerful, open-source relational database system, chosen for its reliability and scalability.
-
Redis: An in-memory data structure store used for caching and as a message broker.
-
Next.js: A React framework for building server-side rendered and statically generated web applications.
-
Docker: Containerization technology to ensure consistent deployment across different environments.
-
Docker Compose: A tool for defining and running multi-container Docker applications.
-
RESTful API Endpoints:
- Implemented CRUD operations for towns and people.
- Defined RESTful API endpoints using FastAPI decorators.
-
Database Integration:
- Integrated with a PostgreSQL database using SQLAlchemy.
- Defined data models for towns and people.
-
Data Pagination:
- Implemented pagination for large datasets for improved performance.
-
Validation and Request Handling:
- Utilized FastAPI's automatic data validation.
- Implemented request validation using Pydantic models.
-
Rate Limiting:
- Implemented rate limiting for specific endpoints to control client requests.
-
Dependency Injection:
- Leveraged FastAPI's dependency injection system for managing database sessions.
-
API Documentation:
- Generated API documentation using Swagger UI and ReDoc.
- Documented request/response models, available endpoints, and usage examples.
-
Testing:
- Wrote unit tests and integration tests using Pytest.
-
Dockerization:
- Dockerized the FastAPI application for consistent deployment environments.
- Used Docker Compose for managing multi-container applications (frontend, postgres, redis).
-
Database Migrations:
- Implemented database migrations using Alembic to manage schema changes.
-
Cross-Origin Resource Sharing (CORS):
- Enabled CORS to control API access from different domains.
-
Environmental Configuration:
- Used environment variables for configuration settings.
-
Implemented frontend:
- Used Nextjs to develop a frontend to interact with the API. Utilized docker compose for communication between frontend and backend.
- Clone the repository:
git clone https://github.com/Nneji123/fastapi-nextjs.git
- Navigate to the project directory:
fastapi-nexjs
- Create and activate a virtual environment:
- Using
venv
on Linux:python3 -m venv env && source env/bin/activate
- Using
venv
on Windows and Git Bash:python3 -m venv env && source env/Scripts/activate
- Using
- Install dependencies:
cd backend && pip install -r api/requirements.txt
- Setup sqlite database:
python api/init_db.py
Alembic is used in this project to handle schema changes in the database(add new columns, remove columns etc without deleting the database).
Assuming we're working in the backend directory:
- Firstly run:
alembic init -t sync migrations
- Within the generated "migrations" folder, import sqlmodel into script.py.mako since this project uses sqlmodel.
- Import sqlmodel into the migrations/env.py file:
from sqlmodel import SQLModel
and also import your database tables in the same file:
from api.public.people.models import Person
from api.public.towns.models import Town
- Set your metadata in the same file:
# target_metadata = mymodel.Base.metadata
target_metadata = SQLModel.metadata
- To generate the first migration file, run:
alembic revision --autogenerate -m "init"
- Apply the migration:
alembic upgrade head
- To make changes to the database edit the models and then run steps 5 and 6 again.
- Ensure you are in the project directory (
backend
). - Run the FastAPI application:
python api/asgi.py
- Access the API at
http://localhost:8000
You should be able to see the swagger docs at http://localhost:8000/docs
- Ensure Docker and Docker Compose are installed.
- Navigate to the project directory:
cd yourproject
- Build and start the containers:
docker-compose up --build
- Access the API at
http://localhost:8000
and the frontend built with nextjs and (https://v0.dev)[v0.dev] athttp://localhost:3000
- Endpoint:
GET /{person_id}
- Description: Retrieves details of a single person by ID.
- Request:
- Method:
GET
- Path:
/{person_id}
(Replace{person_id}
with the actual ID)
- Method:
- Response:
- Status Code:
200 OK
if person is found,404 Not Found
if person with the specified ID does not exist. - Body: Person details in the format specified by
PersonReadWithTown
model.
- Status Code:
- Endpoint:
GET /
- Description: Retrieves a paginated list of all people.
- Request:
- Method:
GET
- Path:
/
- Query Parameters:
skip
(number of items to skip),limit
(maximum number of items to return)
- Method:
- Response:
- Status Code:
200 OK
- Body: Paginated list of people in the format specified by
Page[PersonRead]
model.
- Status Code:
- Endpoint:
POST /
- Description: Creates a new person.
- Request:
- Method:
POST
- Path:
/
- Body: JSON object representing the new person (See
PersonCreate
model)
- Method:
- Response:
- Status Code:
200 OK
if successful,500 Internal Server Error
if there's an exception during person creation. - Body: Created person details in the format specified by
Person
model.
- Status Code:
- Endpoint:
PUT /{person_id}
- Description: Updates details of an existing person by ID.
- Request:
- Method:
PUT
- Path:
/{person_id}
(Replace{person_id}
with the actual ID) - Body: JSON object representing the updated person details (See
PersonUpdate
model)
- Method:
- Response:
- Status Code:
200 OK
if successful,404 Not Found
if person with the specified ID does not exist. - Body: Updated person details in the format specified by
Person
model.
- Status Code:
- Endpoint:
DELETE /{person_id}
- Description: Deletes an existing person by ID.
- Request:
- Method:
DELETE
- Path:
/{person_id}
(Replace{person_id}
with the actual ID)
- Method:
- Response:
- Status Code:
200 OK
if successful,404 Not Found
if person with the specified ID does not exist. - Body: Deleted person details in the format specified by
Person
model.
- Status Code:
Please make sure to replace placeholders like {person_id}
with actual values when making requests. Additionally, provide appropriate details in the request bodies according to your data models (PersonCreate
, PersonUpdate
, etc.).
- Endpoint:
POST /
- Description: Creates a new town.
- Request:
- Method:
POST
- Path:
/
- Body: JSON object representing the new town (See
TownCreate
model)
- Method:
- Response:
- Status Code:
200 OK
if successful,500 Internal Server Error
if there's an exception during town creation. - Body:
{ "status": "success", "msg": "Town created successfully", "data": { /* Town details */ } }
- Status Code:
- Endpoint:
GET /{town_id}
- Description: Retrieves details of a single town by ID.
- Request:
- Method:
GET
- Path:
/{town_id}
(Replace{town_id}
with the actual ID)
- Method:
- Response:
- Status Code:
200 OK
if town is found,404 Not Found
if town with the specified ID does not exist. - Body: Town details in the format specified by
TownReadWithPeople
model.
- Status Code:
- Endpoint:
GET /
- Description: Retrieves a paginated list of all towns.
- Request:
- Method:
GET
- Path:
/
- Query Parameters:
skip
(number of items to skip),limit
(maximum number of items to return)
- Method:
- Response:
- Status Code:
200 OK
- Body: Paginated list of towns in the format specified by
Page[TownRead]
model.
- Status Code:
- Endpoint:
PUT /{town_id}
- Description: Updates details of an existing town by ID.
- Request:
- Method:
PUT
- Path:
/{town_id}
(Replace{town_id}
with the actual ID) - Body: JSON object representing the updated town details (See
TownUpdate
model)
- Method:
- Response:
- Status Code:
200 OK
if successful,404 Not Found
if town with the specified ID does not exist. - Body: Updated town details in the format specified by
TownRead
model.
- Status Code:
- Endpoint:
DELETE /{town_id}
- Description: Deletes an existing town by ID.
- Request:
- Method:
DELETE
- Path:
/{town_id}
(Replace{town_id}
with the actual ID)
- Method:
- Response:
- Status Code:
200 OK
if successful,404 Not Found
if town with the specified ID does not exist. - Body:
or
{ "status": "success", "msg": "Successfully deleted town with ID {town_id}" }
{ "status": "error", "msg": "Failed to delete town with ID {town_id}" }
- Status Code:
Certainly! Here's a description for the rate-limited endpoint in your FastAPI project:
- Endpoint:
GET /rate_limit
- Description: Returns a message indicating that this is a rate-limited endpoint. The endpoint is rate-limited to allow a maximum of 2 requests every 5 seconds.
- Request:
- Method:
GET
- Path:
/rate_limit
- Method:
- Response:
- Status Code:
200 OK
if the rate limit is not exceeded,429 Too Many Requests
if the rate limit is exceeded. - Body:
- If the rate limit is not exceeded:
{ "Hello": "This is a rate-limited endpoint!" }
- If the rate limit is exceeded:
{ "detail": "Too many requests, try again later." }
- If the rate limit is not exceeded:
- Status Code:
- Rate Limit:
- Maximum Requests: 2 requests per 5 seconds.
This endpoint is configured to limit the number of requests to 2 every 5 seconds, and it will respond with a success message if the rate limit is not exceeded. If the rate limit is exceeded, it will respond with a "Too Many Requests" error message.
For deployment I used Koyeb. Koyeb is a developer-friendly serverless platform to deploy apps globally. No-ops, servers, or infrastructure management. You can learn more about it here: https://www.koyeb.com/tutorials/deploy-apps-using-docker-compose-on-koyeb
Follow the steps below to deploy and run the docker-compose application on your Koyeb account.
You need a Koyeb account to successfully deploy and run this application. If you don't already have an account, you can sign-up for free here.
The fastest way to deploy the docker-compose application is to click the Deploy to Koyeb button below.
Clicking on this button brings you to the Koyeb App creation page with everything pre-set to launch this application.
If you want to customize and enhance this application, you need to fork this repository.
If you used the Deploy to Koyeb button, you can simply link your service to your forked repository to be able to push changes. Alternatively, you can manually create the application as described below.
On the Koyeb Control Panel, click the Create App button to go to the App creation page.
- Select
GitHub
as the deployment method to use - In the repositories list, select the repository you just forked
- Specify the branch to deploy, in this case
main
- Set the
Dockerfile location
toDockerfile.koyeb
- Set the
privileged
flag - Then, give your App a name, i.e
docker-compose-on-koyeb
, and click Create App.
You land on the deployment page where you can follow the build of your docker-compose application. Once the build is completed, your application is being deployed and you will be able to access it via <YOUR_APP_NAME>-<YOUR_ORG_NAME>.koyeb.app
.
Prometheus is used to collect and store metrics from your FastAPI application. It gathers data from various services and components, providing insights into the performance and behavior of your application.
The Prometheus configuration is defined in the prometheus_data/prometheus.yml
file. Make sure to customize this file based on your application's specific metrics and requirements.
Prometheus can be accessed at http://localhost:9090 in your local development environment.
Grafana is utilized for visualizing and analyzing the metrics collected by Prometheus. It offers powerful tools for creating dashboards and monitoring the health of your application.
No specific Grafana configuration is needed for basic functionality. However, you can customize Grafana settings and dashboards based on your monitoring needs.
To learn more about this you can read this article: https://dev.to/ken_mwaura1/getting-started-monitoring-a-fastapi-app-with-grafana-and-prometheus-a-step-by-step-guide-3fbn
Grafana is accessible at http://localhost:4000 in your local development environment.
- Username: admin
- Password: admin
Your FastAPI application is instrumented to expose relevant metrics, which are then visualized in Grafana dashboards. These metrics provide valuable insights into request/response times, error rates, and various aspects of your application's performance.
Pagination using FastAPI-Pagination is implemented in the get all towns and get all people routes.
- To run tests with in-memory SQLite database:
cd backend
pip install -r api/requirements.txt
python init_db.py
pytest tests
- Check coverage with:
pytest --cov=yourmodule
- Add pagination using FastAPI-Pagination
- Write test cases for the api.
- Add GitHub Actions template for running tests
- Write Makefile for easier project management
- Remove unnecessary files
- Abstract routes to a
public_routes.py
file - Improve documentation for public routes API
- Add Alembic support for database migrations
- Implement either Propelauth or FastAPI-Auth0 for authentication
- Implement rate limiting
- Allow CORS
- Write Docker and Docker Compose files for production deployment.
- Replace current linter with "ruff".
- Use bookmark to add code samples and change logo. generate logo with bing
- Create a new branch and remove async functionality for ease of use.
- Create a frontend with nextjs/reactjs showing how to use the api
- Background tasks with celery
- Logging with Prometheus and Grafana
Certainly! Here are five FastAPI repositories with their names and links:
-
Fast-Api-Grafana-Starter
-
prometheus-fastapi-instrumentator
-
fastapi-keycloak
- Repository Link Certainly! Here are five more FastAPI repositories with their names and links:
-
propelauth-fastapi
-
Shortify
-
fastapi-beanie-jwt
-
fastapi-best-practices
-
fastapi-sqlmodel-alembic
This project is licensed under the MIT LICENSE - see the LICENSE file for details.