FastAPI application example with API Key authentication β Python Backend Template
FastAPI API Key Authentication is a 100% Python backend template project that demonstrates how to implement simple API Key authentication in a REST API built with FastAPI. Following Clean Architecture and Domain-Driven Design (DDD) principles, the project provides a modern structural foundation for secure and scalable applications.
Aimed at beginner to advanced developers, the template offers:
- Security out of the box: API Key authentication (via customizable header) already integrated into protected routes.
- Organized architecture: clear separation between core (infrastructure) and domain modules, making maintenance and growth easier.
- Automatic documentation: Swagger (OpenAPI) interface available to quickly test and integrate endpoints.
- Built-in best practices: Pydantic v2 validation, structured logging (Loguru), standardized responses, and pre-configuration for Docker and tests.
Below youβll find a complete guide to installation, usage, and contribution. Enjoy!
FastAPI API Key Authentication is a sample application created to show, in practice, how to protect FastAPI API endpoints using an API Key. It serves as a starting point for building microservices or internal APIs that need a simple authentication layer without implementing full OAuth2 or JWT flows.
- Validates all requests hitting protected endpoints, ensuring they include a valid API Key header before running business logic.
- Demonstrates a layered project structure (Clean Architecture), serving as a model for creating new endpoints and modules in a decoupled manner.
- Provides standardized JSON responses, including metadata (HTTP code, path, timestamp) for both success and error cases, simplifying consumption and debugging.
- Documents the API automatically using FastAPIβs Swagger UI (interactive interface available at
/docs) for quick route testing. - Includes production-ready utilities like a health check endpoint (
/healthz) for monitoring and easy environment configuration via a.envfile.
Many web applications must expose APIs quickly and securely, either for internal microservice consumption or for offering services to external clients. Implementing a simple authentication scheme from scratch can lead to security pitfalls (e.g., checks vulnerable to timing attacks) or to an unstructured architecture as the project grows.
This template addresses these challenges by:
- Providing a minimalist, ready-to-use authentication solution (API Key), avoiding the setup of OAuth2 or other methods when not needed.
- Establishing an organized codebase, enabling project evolution with well-defined modules without mixing business rules with infrastructure details.
- Ensuring basic security by using secure string comparison (
secrets.compare_digest) and consistent error returns (401 with appropriate headers), aligned with HTTP best practices.
| User Profile | What they gain from this project |
|---|---|
| Backend Developers | A cohesive starting point to create structured, secure APIs with API Key. |
| Internal API Teams | A consistent model to standardize authentication between microservices and ensure only authorized services access data. |
| FastAPI Beginners | A practical example of organizing a complex FastAPI project (multiple layers) cleanly, with integrated auth and docs. |
| Tech Leads / Software Architects | A reference blueprint to spread best practices for structuring Python projects and deploying simple security measures. |
- Easy to Customize: API Key header name and the key value itself are defined via
.env, enabling quick adaptation to environments or security policies. - Global Auth via Dependency/Middleware: All routes (except explicitly public ones) can be protected at once using global dependencies or middlewareβmaking it easy to extend security to new endpoints.
- Unified Standard Response: A consistent response model (
StandardResponse) wraps user data and metadata (status, method, etc.), simplifying logs and monitoring. - Docker-ready: Dockerfile and docker-compose provided for containerized execution, speeding up local testing and deployments in standardized environments.
- Fast Development Loop: Hot-reload in development (Uvicorn or FastAPI CLI) and preconfigured lint/format (Ruff) for quick feedback and consistent code.
- Test-ready: Automated testing structure (Pytest) to make unit and integration testing straightforward as the project grows.
| # | Feature | What it does | Technical/Usability Differentials |
|---|---|---|---|
| 1 | API Key Authentication | Restricts API access to requests presenting the correct key in the header. | π Secure verification using secrets.compare_digest to prevent timing attacks. Customizable header (default: X-API-Key). |
| 2 | Example Endpoint | Provides an illustrative route (e.g., /api/v1/example/) that can serve as a model for implementing features. |
π οΈ Practical application of Clean Architecture (layers for domain, use case, presentation) and Pydantic for data validation. |
| 3 | Health Check | Exposes a public /healthz endpoint for quick service checks (used by orchestrators, Kubernetes, etc.). |
β€οΈ Follows the 12βFactor App style; easily integrates with load balancers or uptime monitoring systems. |
| 4 | Interactive Docs | Offers Swagger/OpenAPI via /docs (Swagger UI) and /redoc (ReDoc). |
π Test API calls directly in the browser, including providing the API Key through Swaggerβs Authorize button. |
| 5 | Standardized Responses | All success/error responses follow a unified schema (code, method, path, timestamp, details{...}). |
π Makes logging and auditing easier; clients and developers always handle a consistent response format. |
| 6 | Structured Logging | Logs each request with important info (execution time, status, origin) and X-Request-ID identification. |
π Based on Loguru for JSON output; easy integration with observability tools (ELK, Graylog, etc.) and readable stack traces (Stackprinter). |
| Technology | Version | Role in the Project | Why it was chosen |
|---|---|---|---|
| Python | 3.13 | Main language; native async support. | Vast ecosystem and simple syntax, plus performance improvements with each version. |
| FastAPI | 0.115.13 | ASGI web framework for building REST APIs. | Excellent performance (comparable to Node.js and Go) and automatic documentation generation (Swagger UI). |
| Pydantic v2 | 2.11.7 | Data modeling and validation (request/response models, configs). | Declarative and fast validations (Rust core), ensuring reliable data in the API. |
| Uvicorn & Hypercorn | 0.34.x / 0.17.3 | ASGI servers to run the app (Uvicorn in dev, Hypercorn optional for prod). | Modern features (HTTP/2, WebSockets); hot reload in dev and high performance in production. |
| Orjson | 3.11.0 | Ultra-fast JSON serialization for HTTP responses. | Up to 3x faster than Pythonβs stdlib JSON, improving API latency. |
| Loguru | 0.7.3 | Simple, structured logging. | Friendly API, per-request formatting, flexible sinks (console, file, etc.). |
| Pydantic Settings | 2.10.1 | Read configs and secrets from .env or environment variables. |
Embraces 12-Factor principles: configuration outside code with automatic type parsing. |
| Pytest | 8.4.1 (dev) | Testing framework. | Concise tests with fixtures; asyncio support makes testing FastAPI functions easy. |
| Ruff | 0.12.0 (dev) | Python linter and formatter. | Bundles dozens of checks (Flake8, Black-style formatting, etc.) in one ultra-fast tool for consistent code standards. |
π Dependency Security: All versions are pinned in
requirements.txtanduv.lockfor reproducible builds. Only essential libraries are included to minimize attack surface and performance overhead.
π³ Directory tree (simplified)
fastapi-apikey-authentication/
βββ app/
β βββ app.py # Initializes the FastAPI instance and includes routes
β βββ core/ # "Core" features (infrastructure & cross-cutting)
β β βββ security.py # API Key auth logic (Dependency)
β β βββ middleware.py # Response formatting and logging middleware
β β βββ exception_handler.py # Global HTTP exception handling
β β βββ settings.py # App configuration (Pydantic BaseSettings)
β β βββ schemas.py # Reusable generic schemas (e.g., StandardResponse)
β β βββ exceptions.py # Custom exceptions
β β βββ logging.py # Loguru logger configuration
β β βββ resources.py # Misc resources (e.g., error texts or constants)
β β βββ utils.py # General utilities
β βββ modules/ # Business modules (each folder is an isolated context)
β βββ example/ # Example module (demonstration feature)
β β βββ domain/ # Business rules, entities, mappers (if applicable)
β β βββ application/ # Use cases (orchestration between domain and presentation)
β β βββ presentation/ # Interface (FastAPI routers, request/response schemas, docs)
β β βββ routers.py # Example endpoints/routes definition
β β βββ schemas.py # Pydantic schemas for the exampleβs request/response
β β βββ dependencies.py # Example-specific dependencies (injections)
β β βββ docs.py # Descriptions & examples (used in OpenAPI) for the module
β β βββ exceptions.py # Example domain-specific exceptions
β βββ health/ # Health check module (system domain)
β βββ application/ # (Could include subsystem checks if needed)
β βββ presentation/
β βββ routers.py # Health route (`/healthz`)
β βββ schemas.py # Health response schema (e.g., status)
β βββ docs.py # Health endpoint documentation
β βββ exceptions.py # (n/a β health rarely needs custom exceptions)
βββ .env.example # Environment config example (copy to .env)
βββ Dockerfile # Build recipe for the appβs Docker image
βββ docker-compose.yaml # Defines service for local run (includes the app)
βββ pyproject.toml # Project metadata and dependencies (PEP 621)
βββ requirements.txt # Pinned (frozen) dependency list
βββ uv.lock # Dependency lockfile (generated by uv)
βββ scripts/
β βββ directory_tree.py # Utility script to generate the directory tree
βββ test/ # Tests (unit/integration)
β βββ core/ # (e.g., tests for core utilities)
β βββ modules/ # (e.g., tests for each business module)
βββ README.md # Main repository documentation
| Layer/Folder | Responsibility/Role | Details |
|---|---|---|
app/app.py |
Initializes the FastAPI app, sets global definitions, and includes routes from all modules. | The application βentry pointβ. Imports each moduleβs routers and adds middlewares (such as global authentication, if configured). |
app/core/ |
Core module with reusable components and configurations spanning the whole project. | Contains auth logic, response formatting, error capture, 12βFactor settings, and other utilities independent of business rules. |
app/modules/ |
Each subfolder represents an isolated business context (DDD). | Enables adding new domains/features without conflicting with existing ones. E.g., example (didactic) and health (systemic) modules. |
.../presentation/ |
Presentation layer: where Clean Architecture controllers live (here, FastAPI routers). | Defines endpoints, performs initial validation via Pydantic schemas, and returns responses using model classes. Also includes descriptions (docs.py) shown in Swagger UI. |
.../application/ |
Application layer: implements use cases or interactors. | Orchestrates calls between presentation and domain layers. In the example module, it could process input data, call domain services, and shape the final response. |
.../domain/ |
Domain layer: business rules, entities, and repository contracts. | Ideally infrastructure-agnostic. In the example module, it holds classes/functions representing the core business logic. In larger templates, this is where repository interfaces and external service contracts would live. |
| Config files | (root) Dockerfile, docker-compose.yaml, etc. |
Enable containerization and consistent execution across environments. |
test/ |
Automated test suite (initially illustrative). | Makes it easy to expand coverage as features grow (e.g., authentication tests and example endpoint tests). |
π§© Note: The layered structure (presentation, application, domain) doesnβt prevent an endpoint from calling logic directly, but it encourages separation of concerns. For simple features, the application layer can be minimal; for complex cases, this pattern helps keep code organized.
Project dependencies are managed via pyproject.toml (PEP 621) and a lockfile (uv.lock) for consistency. Below is an overview of key libraries and subcomponents:
fastapi-apikey-authentication (template) v1.0.0
ββ fastapi[standard] v0.115.13
β ββ pydantic v2.11.7
β ββ starlette v0.46.2
β ββ email-validator, python-multipart, httpx, jinja2... (FastAPI extras)
β ββ uvicorn[standard] v0.34.3 (web server + reload)
ββ hypercorn v0.17.3 (alternative ASGI server, e.g., for HTTP/2)
ββ loguru v0.7.3 (structured logging)
ββ orjson v3.11.0 (highβperformance JSON serialization)
ββ pydantic-settings v2.10.1 (BaseSettings-based configuration management)
ββ stackprinter v0.2.12 (readable traceback formatting)
ββ pytest v8.4.1 [dev] (testing framework)
ββ ruff v0.12.0 [dev] (all-in-one linter/formatter)
Notes:
- Packages marked
[dev]are for development only, not required in production. - FastAPI is installed with the
[standard]extra, bundling useful tooling like uvicorn (server) and utilities (email-validator, jinja2, etc.) for faster prototyping. requirements.txtis generated from the lockfile and pins exact versions (ensuring all developers/environments use the same versions).- There are no external DB or third-party auth dependenciesβthe goal is to keep it simple. If your use case requires integrations, add them as needed while keeping the modular organization.
To run and develop this project, ensure you have:
| Item | Version / Notes | Required? | Description / Use |
|---|---|---|---|
| Python | >= 3.13 (compatible with 3.13+) | β | Python interpreter to run the app. Newer versions ensure better performance and compatibility with Pydantic v2. |
| uv (deps CLI) | Latest stable (optional) | Recommended dependency/venv manager. Eases venv creation and uv.lock sync. (install) |
|
| Git | Any recent version | β | Version control to clone the repo and manage source code. |
| Docker + Compose | Docker Engine 20+ / Docker Compose 2+ | βοΈ (for deploy) |
To run in containers (optional for dev but recommended to ensure environment parity). |
| Editor/IDE | VSCode, PyCharm, etc. (suggested) | β | A good editor boosts productivity. This project includes ready-to-use lint config (Ruff) that integrates with your editor for instant feedback. |
Note: Using uv (by Astral) is encouraged to simplify environment and dependency management (similar to pipenv or poetry). You can also use traditional pip/venvβjust follow requirements.txt.
- Main Libraries: Already listed above (FastAPI, Pydantic, etc.). All installed via
piporuvfrompyproject.toml. - Development Libraries: Include
pytest(to run tests) andruff(lint/format). Not required in production but recommended during development to keep code quality high. - External Services: None consumed in this app. Authentication is performed locally by comparing the provided key with the one configured in environment variables. If needed, integrate DBs or external APIs in new modules, following the templateβs pattern.
-
Environment Variables: Rename
.env.exampleto.envand set values as needed. Main parameters include:SECURITY_API_KEY_HEADERβ Header name that carries the API Key (default:X-API-Key).SECURITY_API_KEYβ The secret API Key value to accept. (Set a strong value in production; the example file contains a placeholder for development.)- Other parameters: (e.g.,
LOG_LEVEL,APP_ENV) as defined incore/settings.py, which may change logging behavior or environment-specific settings.
-
Application Port: By default, the app runs on 8000 (see
docker-compose.yamland instructions below). You can change it with the--portflag or by adjusting the Docker Compose port mapping. -
Debug Mode: In development, auto-reload is enabled (when using uvicorn via
uv runorfastapi dev). In production, disable debug and reload for better performance.
You can run the application locally in two ways: directly in a Python environment (ideal for development) or using Docker (useful to test in an isolated environment or produce a deployable image).
-
Install
uv(Astralβs env & deps manager):-
Linux/macOS (via cURL):
curl -LsSf https://astral.sh/uv/install.sh | sh -
Windows (PowerShell):
iwr https://astral.sh/uv/install.ps1 -UseBasicParsing | iex
If you prefer, check the official uv docs for alternative installation methods.
-
-
Clone this repository and move into the project folder:
git clone https://github.com/BrunoTanabe/fastapi-apikey-authentication.git cd fastapi-apikey-authentication -
Create a Python virtual environment:
uv venv .venv
This creates a
.venvat the project root. (Optionally, usepython -m venv .venvandsource .venv/bin/activateinstead.) -
Install production dependencies:
uv sync
This reads
pyproject.tomland installs all specified dependencies, honoring versions pinned inuv.lock. Youβll get FastAPI, Uvicorn, and the rest. -
(Optional) Also install development dependencies:
uv sync --group dev
This includes
pytestandruff. Not required to run the app, but useful for testing and maintaining code quality. -
Set environment variables:
cp .env.example .env
Edit
.envwith appropriate values. SetSECURITY_API_KEYto a secret token required to access protected endpoints. KeepSECURITY_API_KEY_HEADERasX-API-Key(or change if needed). -
Run the API in development (with hot-reload):
uv run uvicorn app.app:app --reload --port 8000 --host 0.0.0.0
This starts Uvicorn with the FastAPI app (
app.app:app, i.e.,appobject inapp/app.py), enables auto-reload on file changes, and binds to port 8000 on all interfaces.Alternatively, use FastAPI CLI (installed via the
[standard]extra):uv run fastapi dev app/app.py --port 8000
Either way, your API will listen at
http://localhost:8000. -
Open the interactive docs to test: Go to http://localhost:8000/docs. Youβll see Swagger UI to try endpoints. Click Authorize and supply the API Key from your
.envto call protected routes.
π‘ Tip:
uv run ...ensures the lockfile is synced before execution, avoiding version drift. If you donβt useuv, you can runuvicorn app.app:app --reload --port 8000after installing dependencies withpip install -r requirements.txt.
If you prefer, or for production purposes, run the app in a Docker container:
-
Ensure Docker is installed and the daemon is running.
-
Build the Docker image: From the project root:
docker build -t fastapi-apikey-auth .Uses the provided
Dockerfileto package the app. The image is namedfastapi-apikey-auth. -
Run the container:
docker run --rm -p 8000:8000 --env-file .env fastapi-apikey-auth
This:
- Publishes container port 8000 to host 8000.
- Loads environment variables from your local
.env(ensuring the API Key and other configs are available inside the container). - Uses the image you built above.
--rmremoves the container after exit.
-
(Optional) Use Docker Compose for development: A
docker-compose.yamlis provided. It builds the image and exposes port 8000. Just run:docker-compose up --build
The
apiservice maps8000:8000and mounts the project directory for live reload. Feel free to tweakdocker-compose.yaml(e.g., volumes or commands). -
Open Swagger and test: With the container running, go to http://localhost:8000/docs to confirm everything works inside the container. Routes and authentication should behave the same as locally.
π³ Tip: The image is based on slim Python, containing only project deps (thanks to
requirements.txt). For a smaller image, consider multi-stage builds or Alpine/Python base. Also, configure secrets via CI/CD or orchestrators instead of hardcoding in the Dockerfile.
Generally, all API responses (success and error) follow the
StandardResponseschema. It includes fields likecode,method,path, andtimestamp, plus adetailsobject containing actual results or error messagesβproviding uniform info for logging and client handling.
API Key authentication works as follows in this project:
-
All βprotectedβ routes require a specific HTTP header, whose default name is defined in
SECURITY_API_KEY_HEADER(in.env). By default, we useX-API-Key. -
The header value must match the key set in the
SECURITY_API_KEYenvironment variable. -
If the key is missing or incorrect:
- The API returns HTTP 401 Unauthorized, with an error JSON indicating invalid credentials.
- The
WWW-Authenticateheader is included in the response, as recommended by HTTP for API credentials (though not Basic/Bearer, it indicates authentication is required).
-
Certain endpoints remain open by design: typically Swagger
/docs,/openapi.json, and health check/healthz. This allows checking status or documentation without a key.
Main endpoints provided by this project:
| Method | Route | Description | Auth | Body (JSON) | Success | Main Errors |
|---|---|---|---|---|---|---|
| POST | /api/v1/example/ |
Example endpoint performing a demo operation (e.g., personalized greeting). | β | Yes (JSON object input) | 200 | 401, 422, 500 |
| GET | /healthz |
Application health check (returns βokβ if alive). | β | N/A | 200 | 500 |
| GET | / |
Redirects to Swagger UI (/docs). |
β | N/A | 308 | N/A |
| GET | /docs |
Interactive Swagger documentation (OpenAPI UI). | β | N/A | 200 | N/A |
| GET | /redoc |
Alternative ReDoc documentation. | β | N/A | 200 | N/A |
(Auth = requires API Key; Body = JSON payload required, if applicable.)
Endpoint Details
Description: This illustrative endpoint accepts a JSON input (e.g., with a name) and returns a simple response (e.g., a personalized greeting). It demonstrates request validation via Pydantic, the need for authentication, and the standardized response format.
-
Requires authentication? Yes, send the
X-API-Keyheader (or the one defined inSECURITY_API_KEY_HEADER) with the correct key. -
Request body (JSON):
{ "name": "JoΓ£o da Silva" }name(string): Personβs name to greet. Required; minimum length 1 (illustrative validation).
-
Success response (HTTP 200):
Assuming the provided name is βJoΓ£o da Silvaβ:
{ "code": 200, "method": "POST", "path": "/api/v1/example/", "timestamp": "2025-07-27T03:15:00Z", "details": { "message": "Request processed successfully.", "data": { "greeting": "Hello JoΓ£o da Silva!" } } }Explanation:
details.data.greetingcontains the message generated from the supplied name. -
Possible errors (codes & conditions):
401 Unauthorized: Missing or incorrect API Key header.422 Unprocessable Entity: Input JSON doesnβt match the expected schema (e.g., required field missing, wrong type).500 Internal Server Error: Unexpected processing failure (e.g., unhandled exception).
-
Error example (missing/invalid API Key β HTTP 401):
{ "code": 401, "method": "POST", "path": "/api/v1/example/", "timestamp": "2025-07-27T03:15:00Z", "details": { "message": "Authentication failed.", "data": { "error": "Invalid or missing API key." } } } -
Error example (validation β HTTP 422):
{ "code": 422, "method": "POST", "path": "/api/v1/example/", "timestamp": "2025-07-27T03:15:00Z", "details": { "message": "Validation error.", "data": { "name": "Field required" } } }Note: The internal structure of validation details may vary with Pydantic/FastAPI settings. The projectβs standard response wraps error payloads in
StandardResponse. -
Quick test tip: Use
curlto test (replace<YOURKEY>with the key set in.env):curl -X POST "http://localhost:8000/api/v1/example/" \ -H "Content-Type: application/json" \ -H "X-API-Key: <YOURKEY>" \ -d '{"name": "JoΓ£o da Silva"}'
Endpoint Details
Description: Simple monitoring endpoint returning the applicationβs health status. Useful for automated checks (e.g., Kubernetes, AWS ELB, or other monitoring tools).
-
Requires authentication? No. Public by design, since orchestrators typically lack credentials.
-
Request body: N/A (nothing beyond the GET request).
-
Example response (HTTP 200):
{ "code": 200, "method": "GET", "path": "/healthz", "timestamp": "2025-07-27T03:15:00Z", "details": { "message": "Request processed successfully.", "data": { "status": "ok" } } }Here,
details.data.statusindicates the service is operational. You can add other fields (e.g., app version, build timestamp) if desired. -
Possible errors:
500 Internal Server Error: Rare; if something prevents even the status from being returned. If the app can respond at all, itβs unlikely to return 500 here.
Endpoint Details
Description: Application root (/) endpoint. Instead of returning content, it automatically redirects to Swagger UI (/docs).
-
Requires authentication? No. Anyone hitting the root will be redirected (and the docs page doesnβt require auth to view).
-
Behavior: Uses HTTP 308 Permanent Redirect, meaning:
- The original HTTP method is preserved (if someone POSTed to
/, it would redirect to a POST on/docsβthough thatβs not a practical case). - Clients and caches may store this redirect permanently.
- The original HTTP method is preserved (if someone POSTed to
-
Example response (HTTP 308):
{ "code": 308, "method": "GET", "path": "/", "timestamp": "2025-07-27T03:15:00Z", "details": { "message": "Redirecting to documentation.", "data": { "url": "/docs" } } }Besides the body, the
Location: /docsheader is sent, as required by HTTP for redirects. -
After redirect, the client will see the Swagger UI and can interact with the API from there.
Endpoint Details
Description: FastAPI automatically provides two documentation interfaces:
/docs: Swagger UI interface to browse endpoints, inspect request/response schemas, and execute calls directly in the browser (Try it out)./redoc: ReDoc interface, a static and clean documentation based solely on the OpenAPI schema (helpful for read-only sharing).
- Requires authentication? Not to view docs. However, to test protected endpoints via Swagger UI, click Authorize and provide the API Key (the UI will show a field using the configured header name).
- Practical use: Use
/docsduring development for quick experimentation. In production, consider disabling or protecting this route (via FastAPI configs or basic auth at the web server) if you donβt want the docs publicly exposed. - Example view: Swagger UI looks like this:
(Note: illustrative image from FastAPI docs; your actual UI will show your endpoints and models).
1. Whatβs the API Key header name and can I change it?
By default, X-API-Key, set via SECURITY_API_KEY_HEADER. You can change it in .env or production env. Ensure client apps know the header name you pick.
2. How do I generate or obtain the API Key?
Implementation choice. In this example, the key is manually set via the SECURITY_API_KEY environment variable. Choose a long random secret and configure it as the accepted key. For more complex scenarios, integrate a database or credential management service to validate multiple keysβbut thatβs beyond this simple templateβs scope.
3. I get 401 Unauthorized even when sending the header. Why?
Check that:
a) Youβre using the exact configured header name (e.g., X-API-Key, case-sensitive).
b) You set SECURITY_API_KEY correctly and use the same value in the request.
c) There are no stray spaces/characters in the sent key.
If all looks right, check the app logsβyour key may be wrong or not being sent in the expected header.
4. What happens if I donβt provide the API Key on protected endpoints? The request is immediately rejected with 401 (Unauthorized). The FastAPI security dependency prevents the endpoint function from executing. Configure your HTTP clients to always send the required header.
5. Can I protect /docs with an API Key too?
Yes. Itβs left open by default for convenience. To restrict it, disable Swagger UI (docs_url=None when initializing FastAPI) or add protection. One simple approach is applying the same APIKeyHeader dependency to a custom docs route (requires overriding the auto-generated route).
6. How do I add new endpoints or modules?
Create a new directory in app/modules following existing modules (domain, application, presentation). At minimum, create presentation/routers.py with your FastAPI routes. Then include the router in app/app.py. Use the example module as a reference for separation of concerns and schemas.
7. How do I run automated tests?
After installing dev deps (uv sync --group dev or similar), run:
uv run pytest -qThis searches test/. Initially, there may be only basic or no tests since this is a template. As you add features, create corresponding tests to keep things working as expected.
8. Does this template support user authentication (username/password) or just API Key? Natively, only API Key. The goal is to show a simple way to protect an API consumed by backend systems or partners (to whom you can provide a secret). If you need end-user auth, implement an additional mechanism (e.g., OAuth2 Password Flow with JWT, well-supported by FastAPI)βoutside this exampleβs scope.
9. Next steps if I want to use this in production? Recommendations:
- Generate a strong API key and distribute it carefully to API consumers.
- Enforce HTTPS to protect the key in transit (usually at the server/proxy layer; Uvicorn/Hypercorn donβt directly handle certificates).
- Adjust
.envfor production (e.g., less verbose logs, disable reload). - Optionally add key rotation if you need to invalidate/update keys without downtime (e.g., read multiple valid keys from an external source/file instead of a single
.envvalue). - Add monitoring and centralized logging: Loguruβs JSON logs can go to your aggregator; monitor the health endpoint periodically.
10. Is there CORS support in this project? No explicit CORS configuration by default. FastAPI doesnβt auto-enable CORS. If this API will be consumed by browsers (different origin), enable CORS manually:
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # Adjust to proper domains in production
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)Add this in app/app.py before starting the app. If your API is internal (server-to-server), CORS may be unnecessary.
Contributions are welcome! If you have ideas to improve this template (new features, bug fixes, better docs), feel free to open Issues or submit Pull Requests. Please follow the guidelines below to keep the project consistent:
-
Fork this repository to your GitHub account.
-
Clone your fork locally:
git clone https://github.com/youruser/fastapi-apikey-authentication.git
and add the original repo as
upstream:git remote add upstream https://github.com/BrunoTanabe/fastapi-apikey-authentication.git
-
Create a branch for your contribution:
git checkout -b feat/your-feature-name
Use a descriptive name that reflects the proposed change (e.g.,
feat/multiple-api-keysorfix/header-typo). -
Prepare your dev environment following How to run the project locally (including
uv sync --group devto getpytestandruff). -
Implement your change with clear code and comments when necessary.
-
Test locally: run
pytestto ensure all tests pass (preferably add tests for your new feature or fix). -
Format/Lint before committing:
uv run ruff format uv run ruff check --fix
This applies automatic formatting and fixes lint issues when possible.
-
Commit and push:
git add . git commit -m "feat: Your conventional commit message" git push origin feat/your-feature-name
Try to follow the Conventional Commits pattern (see below).
-
Open a Pull Request (PR) from your repo/branch to this repoβs
main. Fill in the PR description explaining what was done, why, and any details needed to review it. -
Follow the code review: there may be feedback or suggestions. Respond and adjust as requested.
-
Merge: once approved, your PR will be integrated. You may delete your branch locally and on the fork.
-
Name branches consistently:
- feat/ for new features.
- fix/ for bug fixes.
- docs/ for documentation-only changes.
- refactor/, test/, chore/, etc., for other change types.
- Examples:
feat/support-multiple-keys,fix/api-key-case-sensitive.
-
Use Conventional Commits for commit messages. Examples:
feat: support multiple API Keys per userfix: correct header validation when missingdocs: improve explanation for /healthz endpoint- Include a scope in parentheses if desired (e.g.,
feat(example): add new field to endpoint X payload).
Maintaining this pattern helps generate a CHANGELOG and semantic versions more easily later.
This project follows recommended Python best practices:
- Linting/Formatting: Use Ruff to keep code standardized. Itβs configured to apply Flake8-style checks, isort, and Black-like formatting. Run
ruff checkregularly and before commits to avoid style issues. - Typing: Add static types where possible. While Python doesnβt enforce types at runtime, they aid maintenance and IDE integration.
- Docstrings: Feel free to add docstrings to explain complex functions/methods. Especially for public methods, describing expected behavior is helpful.
- Organization: Keep functions/methods short and cohesive. If a piece gets too long/complex, consider refactoring into helpers.
- Donβt commit secrets: never commit your
.envwith real sensitive keys..gitignorealready ignores it. If needed, use.env.exampleas a template. - Secrets in public PRs: If you fork publicly and want CI to run on your PR, ensure secrets are configured after integration and never exposed. (For this template, there arenβt many secrets beyond the API Key, which you control locally.)
- Scope: Keep each PR focused on a single purpose. Avoid βmega PRsβ; split into smaller, review-friendly PRs.
- Description: Provide context and the solution. If thereβs a related issue, reference it (e.g., βCloses #10β).
- CI/CD: If CI is configured (e.g., GitHub Actions), wait for green checks. Fix any issues before requesting review (lint errors or failing tests).
- Discussion: If unsure about your approach, open an issue first or a draft PR for early feedback.
Make sure youβve checked all boxes:
- Tests written/updated to cover the change (when applicable).
- All tests passing (
pytest). - Lint/format applied (code follows project standards).
- Documentation updated (README.md, docstrings, examples).
- PR description explains why and what changed.
- Commits are organized with meaningful messages.
This project is distributed under the MIT license. Youβre free to use, modify, and distribute this code as long as you keep the original copyright notice. See LICENSE.
Bruno Tanabe β Creator & Maintainer β GitHub | LinkedIn I created this template to help other developers start FastAPI projects in an organized and secure way. If you have suggestions or find issues, feel free to contribute!
