Python

Verified against ChatGPT · 2026-08-12

Write a FastAPI endpoint whose 422 and 401 responses actually tell the caller what went wrong

Builds one FastAPI endpoint with Pydantic request/response models, an auth dependency, and distinct, informative error responses — the version that survives a frontend integrating against it, not just a Swagger demo.

ChatGPT4 fillable variables

The prompt

Ready to copy — highlighted parts are example details you can swap.

Write one FastAPI endpoint. This will be integrated against by a real frontend or another service, so the error responses matter as much as the happy path — a 422 or 401 with a vague generic body just moves the debugging work onto whoever's calling this.

ENDPOINT
POST /api/v1/projects/{project_id}/invite — invites a user by email to join a project.

AUTH REQUIREMENT
Requires a valid JWT bearer token; only users with the 'owner' or 'admin' role on that specific project may invite others.

REQUEST/RESPONSE SHAPE
Request: {email: str, role: 'member' | 'admin'}. Response: the created invitation record with id, status, and expiry.

BUSINESS RULES TO ENFORCE
Can't invite an email already invited or already a member; a 'member' role cannot invite anyone with 'admin' role; project must not have hit its 50-seat limit.

RULES
Define explicit Pydantic models for both the request body and the response — never return a bare dict, since that's how a response silently drifts out of sync with what the frontend expects as the code evolves and nobody notices until a field goes missing in production. Implement the auth requirement as a FastAPI dependency, not inline logic in the endpoint function, so it can be reused and unit-tested independently of this one route. For every business rule listed, decide and state the specific HTTP status code and response body it should produce when violated — a validation failure Pydantic can catch automatically should return its normal 422, but a business-rule violation (a duplicate, an out-of-range state transition, a permission the user's role doesn't have) needs its own distinct status code and a response body that names which rule was violated, not just "Bad Request". Use FastAPI's dependency injection for anything this endpoint needs (a DB session, the current user) rather than instantiating it inside the function body, so the endpoint stays testable with `TestClient` and overridden dependencies rather than requiring a real database connection to test.

WHAT NOT TO DO
Do not catch a broad `Exception` inside the endpoint just to return a clean error — let unexpected exceptions propagate to FastAPI's exception handling (or a registered exception handler) so they show up in logs/monitoring as the internal errors they are, rather than getting silently reshaped into a generic 400 that hides a real bug. Do not put business logic directly in the route function if it's more than a few lines — call into a separate function so the route stays a thin adapter between HTTP and the actual logic.

OUTPUT FORMAT
1. The Pydantic request and response models.
2. The auth dependency.
3. The route function.
4. A table: condition -> status code -> response body shape.
5. A short `TestClient`-based test for the main business-rule failure case.

Customize

Optional — swap in your own details for the highlighted parts above.

Why this works

Requiring an explicit Pydantic response model rather than a bare dict return matters because FastAPI only validates and documents what you tell it to — a route that returns `dict(...)` still works and still renders in the interactive docs with an inferred shape, so the discipline gap is completely invisible in local testing and only surfaces as a silent contract break once a field gets renamed or dropped and the frontend that was relying on the old shape breaks without FastAPI ever raising an error, because there was no explicit contract to violate. Pulling the auth check into a dependency rather than inline route logic is what makes `TestClient` testing of the business rules actually tractable: FastAPI's `app.dependency_overrides` mechanism lets a test swap in a fake authenticated user without touching a real JWT or database, which is only possible if the auth logic is a dependency in the first place — inline auth logic forces every test of every business rule to also stand up real authentication, which is exactly the kind of friction that gets tests skipped under deadline pressure. Requiring a distinct status code and named-rule response body per business rule, rather than letting Pydantic's automatic 422 cover everything, addresses a real confusion GPT-5.1 defaults toward: it tends to treat "validation" as one bucket, so a request that's shaped correctly but violates a business invariant (inviting someone already invited) gets folded into the same 422 as a malformed field, and the calling frontend has no reliable way to distinguish "you sent bad JSON" from "this specific business rule was violated" without parsing error message text, which is fragile the moment the message wording changes.

What you get back

class InviteRequest(BaseModel): email: EmailStr; role: Literal['member','admin']. 409 Conflict -> {'error': 'already_invited', 'detail': 'user@example.com already has a pending invitation'}. 403 Forbidden -> {'error': 'insufficient_role', 'detail': 'members cannot invite admins'}. def test_invite_duplicate_returns_409(client, override_owner_user): ... assert response.status_code == 409 and response.json()['error'] == 'already_invited'

Verified against

ChatGPT GPT-5.1 · 2026-08-12

Changelog

  • 2026-08-12 Initial publish, verified against ChatGPT GPT-5.1.

Need this built into your business?

If a prompt isn't enough — custom software, built and maintained for you — that's Scult's day job.

EXPLORE CUSTOM SOFTWARE
All Python prompts

Check your AI visibility

One URL in, a 0–100 score and the exact fixes out.

RUN THE CHECK

Browse all the tools

15 tools across six categories
13 of them never send your data anywhere

Free · No signup · No trial clock

SEE THE DIRECTORY