# Tedivm Oscilla Fastapi Routes

> FastAPI Routes

- Skill: `tomevault-io/tedivm-oscilla-fastapi-routes` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add tomevault-io/tedivm-oscilla-fastapi-routes`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tomevault-io/tedivm-oscilla-fastapi-routes/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: tomevault-io (https://skillmd.com/u/tomevault-io)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/tomevault-io/tedivm-oscilla-fastapi-routes

---


# FastAPI Routes

> **context7**: If the `mcp_context7` tool is available, resolve and load the full `fastapi` documentation before proceeding:
> ```
> mcp_context7_resolve-library-id: "fastapi"
> mcp_context7_get-library-docs: <resolved-id>
> ```

Guidelines and patterns for writing FastAPI routes in the Oscilla codebase.

---

## REST Principles

APIs must adhere as closely as possible to REST principles, including appropriate use of HTTP verbs:

| Verb     | Usage                            |
| -------- | -------------------------------- |
| `GET`    | Read one or many resources       |
| `POST`   | Create a new resource            |
| `PUT`    | Replace / fully update a resource |
| `PATCH`  | Partial update (use sparingly)   |
| `DELETE` | Remove a resource                |

---

## Pydantic Models

- **Always** use Pydantic models for both input and output — never use plain `dict` or `Any`.
- **Never** reuse the same model for input and output. Use separate models:
  - `PostCreate` — user input for creating a resource
  - `PostUpdate` — user input for updating (optional fields)
  - `PostRead` — response shape returned to the client
- Parameters in **input** models must use `Field()` with validation constraints and a `description`.
- Output models do not require `Field()` unless you need aliases or serialization control.

```python
from uuid import UUID
from pydantic import BaseModel, Field


class PostCreate(BaseModel):
    title: str = Field(min_length=1, max_length=200, description="Post title")
    content: str = Field(min_length=1, description="Post content")


class PostRead(BaseModel):
    id: UUID
    title: str
    content: str
    created_at: str


class PostUpdate(BaseModel):
    title: str | None = Field(default=None, max_length=200)
    content: str | None = None
```

---

## Router Pattern

Use `APIRouter` for all routes. Register the router in `oscilla/www.py` under the `/api` prefix.

```python
from uuid import UUID

from fastapi import APIRouter, status

router = APIRouter()


@router.post("/posts", response_model=PostRead, status_code=status.HTTP_201_CREATED)
async def create_post(post: PostCreate) -> PostRead:
    ...


@router.get("/posts/{post_id}", response_model=PostRead)
async def get_post(post_id: UUID) -> PostRead:
    ...


@router.put("/posts/{post_id}", response_model=PostRead)
async def update_post(post_id: UUID, post: PostUpdate) -> PostRead:
    ...


@router.delete("/posts/{post_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_post(post_id: UUID) -> None:
    ...
```

---

## Route Registration

All application routes must be registered under the `/api` prefix in `oscilla/www.py`.

**Exceptions** (no `/api` prefix by convention):
- `/health` — liveness probe
- `/ready` — readiness probe
- `/static` — static file serving

```python
# oscilla/www.py
app.include_router(posts_router, prefix="/api")
```

---

## OpenAPI Docs

The OpenAPI documentation is served at these fixed paths — do not move them:

| URL               | Interface    |
| ----------------- | ------------ |
| `/api/docs`       | Swagger UI   |
| `/api/redoc`      | ReDoc        |
| `/api/openapi.json` | Raw schema |

---

## Error Handling

Use `HTTPException` with appropriate status codes:

```python
from fastapi import HTTPException, status

@router.get("/posts/{post_id}", response_model=PostRead)
async def get_post(post_id: UUID) -> PostRead:
    post = await get_post_from_db(post_id)
    if post is None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Post not found")
    return post
```

---

## Style Checklist

Before submitting a new route, verify:

- [ ] HTTP verb matches the operation semantics
- [ ] Separate `Create`, `Update`, and `Read` models defined
- [ ] All input model fields use `Field()` with description
- [ ] Route is `async`
- [ ] Router is registered under `/api` in `oscilla/www.py`
- [ ] 201 status used for `POST` create endpoints
- [ ] 204 status used for `DELETE` endpoints (no response body)
- [ ] Path parameters use typed UUIDs where appropriate

---

## Further Reading

- [docs/dev/api.md](../../docs/dev/api.md) — Full FastAPI developer guide covering the application structure, startup events, Swagger UI (`/api/docs`), ReDoc (`/api/redoc`), OpenAPI schema, middleware, authentication integration, and dependency injection patterns.
- [docs/dev/authentication.md](../../docs/dev/authentication.md) — Authentication and authorization patterns used in this project.
- [docs/dev/dependencies.md](../../docs/dev/dependencies.md) — FastAPI dependency injection conventions.
- [FastAPI Docs](https://fastapi.tiangolo.com/)

---
> Source: [tedivm/oscilla](https://github.com/tedivm/oscilla) — distributed by [TomeVault](https://tomevault.io).
<!-- tomevault:4.0:skill_md:2026-06-15 -->

