python
45 lines · 8 steps
API key authentication as a FastAPI dependency
A reusable dependency extracts, verifies, and validates an API key so route handlers receive an already-authenticated client.
Explained by
highlit
1import secrets
2
3from fastapi import Depends, FastAPI, HTTPException, Security, status
4from fastapi.security import APIKeyHeader
5
6from app.config import settings
7from app.db import Database, get_db
8from app.models import ApiClient
9
10api_key_header = APIKeyHeader(name="X-API-Key", auto_error=False)
11
12
13async def get_api_client(
14 api_key: str | None = Security(api_key_header),
15 db: Database = Depends(get_db),
16) -> ApiClient:
17 if not api_key:
18 raise HTTPException(
19 status_code=status.HTTP_401_UNAUTHORIZED,
20 detail="Missing API key",
21 headers={"WWW-Authenticate": "Header"},
22 )
23
24 client = await db.api_clients.find_by_key_prefix(api_key[:8])
25 if client is None or not secrets.compare_digest(client.api_key, api_key):
26 raise HTTPException(
27 status_code=status.HTTP_403_FORBIDDEN,
28 detail="Invalid API key",
29 )
30
31 if client.revoked_at is not None:
32 raise HTTPException(
33 status_code=status.HTTP_403_FORBIDDEN,
34 detail="API key has been revoked",
35 )
36
37 return client
38
39
40app = FastAPI()
41
42
43@app.get("/v1/usage")
44async def read_usage(client: ApiClient = Depends(get_api_client)):
45 return {"client_id": client.id, "plan": client.plan, "quota": client.quota_remaining}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Modeling auth as a dependency keeps route handlers focused on business logic while centralizing security checks.
- 2Use secrets.compare_digest for secret comparison to avoid leaking information through timing side channels.
- 3Distinguish 401 (no credentials) from 403 (bad or revoked credentials) so clients get accurate feedback.
Related explainers
typescript
import { registerLocaleData } from '@angular/common'; import localeFr from '@angular/common/locales/fr'; import localeFrExtra from '@angular/common/locales/extra/fr'; import localeDe from '@angular/common/locales/de';
Locale-aware bootstrapping in Angular
i18n
localization
dependency-injection
Intermediate
8 steps
python
from fastapi import FastAPI, WebSocket, WebSocketDisconnect app = FastAPI()
Building a WebSocket chat with FastAPI
websockets
broadcast
connection-management
Intermediate
9 steps
typescript
import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import * as Joi from 'joi';
Validating env config at boot in NestJS
configuration
schema-validation
environment-variables
Intermediate
8 steps
java
@Component @Converter public class EncryptedStringConverter implements AttributeConverter<String, String> {
Transparent column encryption in Spring & JPA
encryption
aes-gcm
jpa-converter
Advanced
10 steps
python
import time import uuid from django.utils.deprecation import MiddlewareMixin
Attaching per-request context in Django
middleware
request lifecycle
multi-tenancy
Intermediate
7 steps
typescript
import { Inject, Injectable, Logger } from '@nestjs/common'; import { CACHE_MANAGER } from '@nestjs/cache-manager'; import { Cache } from 'cache-manager'; import { InjectRepository } from '@nestjs/typeorm';
A cache-aside country lookup in NestJS
cache-aside
dependency-injection
batch-lookup
Intermediate
8 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/api-key-authentication-as-a-fastapi-dependency-explained-python-ecd4/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.