- Add deprecation middleware for warning headers on sunset endpoints - Add jails_v2 router for API v2 migration path - Update CI workflow with new test coverage - Update API versioning documentation - Remove completed tasks from Tasks.md
166 lines
5.7 KiB
Markdown
166 lines
5.7 KiB
Markdown
# API Versioning Strategy
|
|
|
|
**Status:** Active — Current version: **v1**
|
|
|
|
All BanGUI API endpoints are versioned using URI path versioning (e.g., `/api/v1/`).
|
|
This document explains when and how to version endpoints, how deprecation works, and what guarantees consumers can rely on.
|
|
|
|
---
|
|
|
|
## 1. Version Lifecycle
|
|
|
|
| Stage | Meaning |
|
|
|-------|---------|
|
|
| **Current** | Active, receiving new features and bug fixes. |
|
|
| **Deprecated** | Still functional but marked for removal. Clients receive `Deprecation: true` and `Sunset: <date>` response headers. |
|
|
| **Removed** | Endpoint no longer exists. Clients must migrate to a newer version. |
|
|
|
|
---
|
|
|
|
## 2. URL Structure
|
|
|
|
```
|
|
/api/v{major}/<resource>/<path>
|
|
```
|
|
|
|
- **v1** — current version (released 2026-05-02)
|
|
- **v2** — reserved; skeleton router deployed at `/api/v2/jails` but **not yet active** for production traffic
|
|
- **PATCH** versions (v1.1, v1.2) are **not** used; only **major** version bumps indicate breaking changes
|
|
- The OpenAPI schema is always available at `/api/openapi.json` regardless of version
|
|
|
|
---
|
|
|
|
## 3. What Triggers a Version Bump
|
|
|
|
A new major version is required when a **breaking change** must be introduced, including:
|
|
|
|
- Removing or renaming a field in a response model
|
|
- Changing the type of a request or response field
|
|
- Removing an endpoint entirely
|
|
- Changing authentication/authorization semantics
|
|
- Modifying the semantics of an existing operation
|
|
|
|
**Non-breaking changes** (backward-compatible):
|
|
|
|
- Adding new optional request fields
|
|
- Adding new response fields
|
|
- Adding new endpoints
|
|
- Fixing bugs that caused incorrect behavior
|
|
|
|
These do **not** require a version bump.
|
|
|
|
---
|
|
|
|
## 4. Deprecation Policy
|
|
|
|
When an endpoint is deprecated:
|
|
|
|
1. The endpoint **remains functional** for a minimum of **6 months** from the `Sunset` date
|
|
2. Response headers are added to every 2xx response:
|
|
```
|
|
Deprecation: true
|
|
Sunset: <RFC-5322 date>
|
|
Link: <https://bangui.example.com/api/v2/...>; rel="successor-version"
|
|
```
|
|
3. The endpoint is registered in the deprecation middleware (``app/middleware/deprecation.py``)
|
|
4. The OpenAPI schema marks the endpoint with `deprecated: true`
|
|
5. Documentation is updated to show the endpoint as deprecated
|
|
|
|
### Implementing Deprecation Headers
|
|
|
|
The ``DeprecationHeaderMiddleware`` (``app/middleware/deprecation.py``) automatically injects
|
|
the correct headers for any registered deprecated endpoint. To schedule an endpoint for removal:
|
|
|
|
```python
|
|
from datetime import datetime, timezone, timedelta
|
|
from app.middleware.deprecation import register_deprecated_endpoint
|
|
|
|
# Example: deprecate /api/v1/jails on 2026-11-03 (6 months from v2 release)
|
|
register_deprecated_endpoint(
|
|
path_prefix="/api/v1/jails",
|
|
sunset_date=datetime(2026, 11, 3, tzinfo=timezone.utc),
|
|
successor_url="/api/v2/jails",
|
|
)
|
|
```
|
|
|
|
The middleware runs on every response; if the request path matches a registered deprecated prefix,
|
|
the appropriate headers are appended before the response is returned.
|
|
|
|
---
|
|
|
|
## 5. Backend Development: Adding Versioned Endpoints
|
|
|
|
### New endpoints
|
|
|
|
All new endpoints are added to the **current** version (`/api/v1/`). Prefix your router:
|
|
|
|
```python
|
|
router = APIRouter(prefix="/api/v1/my-resource", tags=["My Resource"])
|
|
```
|
|
|
|
### Breaking changes requiring v2
|
|
|
|
1. Create a new router file (e.g., `routers/my_resource_v2.py`) with the v2 prefix:
|
|
```python
|
|
router = APIRouter(prefix="/api/v2/my-resource", tags=["My Resource (v2)"])
|
|
```
|
|
2. Copy or adapt the v1 handler logic as needed. Extract shared business logic into
|
|
a **service layer function** so both routers call the same underlying code.
|
|
3. Register the new router in `app/main.py`:
|
|
```python
|
|
app.include_router(my_resource_v2.router)
|
|
```
|
|
4. Register the v1 endpoint for deprecation headers (see §4 above)
|
|
5. Update this document to reflect the new version lifecycle
|
|
|
|
### Keeping routers DRY
|
|
|
|
Routers should only contain HTTP concerns (parameters, responses, status codes). Business logic
|
|
belongs in the service layer. Both v1 and v2 handlers can call the same service function.
|
|
|
|
---
|
|
|
|
## 6. Frontend Development
|
|
|
|
The frontend always uses the current version's base URL:
|
|
|
|
```typescript
|
|
const BASE_URL: string = import.meta.env.VITE_API_URL ?? "/api/v1";
|
|
```
|
|
|
|
All endpoint paths in `frontend/src/api/endpoints.ts` are defined as relative paths (e.g., `/bans`, `/jails`) and are appended to `BASE_URL` at runtime.
|
|
|
|
When v2 is released, update ``VITE_API_URL`` in the environment configuration to point to `/api/v2`.
|
|
|
|
---
|
|
|
|
## 7. OpenAPI / Documentation
|
|
|
|
- Swagger UI: `/api/docs`
|
|
- ReDoc: `/api/redoc`
|
|
- OpenAPI schema: `/api/openapi.json`
|
|
- Docs are **not** versioned; they always reflect the **current** (latest) API version
|
|
|
|
---
|
|
|
|
## 8. CI Breaking-Change Checks
|
|
|
|
A GitHub Actions job runs on every pull request to detect breaking OpenAPI changes:
|
|
|
|
- ``openapi-breaking-changes`` job (PR only): generates the current OpenAPI spec and
|
|
compares it against the baseline committed on the last push to `main`. If any breaking
|
|
changes are found, the job fails and the PR cannot be merged.
|
|
- ``openapi-baseline-commit`` job (main push only): generates and commits the current
|
|
OpenAPI spec as the new baseline for future PR comparisons.
|
|
|
|
To trigger the baseline update, push to main after merging a version bump or any change
|
|
that legitimately alters the OpenAPI surface.
|
|
|
|
---
|
|
|
|
## 9. Version History
|
|
|
|
| Version | Status | Released | Sunset Date | Notes |
|
|
|---------|--------|---------|-------------|-------|
|
|
| v1 | **Current** | 2026-05-02 | — | Initial versioning; all endpoints moved from `/api/` to `/api/v1/` |
|
|
| v2 | **Reserved — skeleton active, endpoints not yet available** | — | — | Router skeleton at `app/routers/jails_v2.py`; real endpoints will be added before activation | |