API errors are django-ninja's default shape, not the standardised one: make them RFC 9457 problem documents #296
Labels
No labels
accessibility
authentication
breaking change
bug
documentation
enhancement
interface
internationalisation
observability
security
tier
1
tier
2
tier
3
tier/4
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Postulo/postulo#296
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Every refusal the API makes comes back as django-ninja's default:
{"detail": "..."}withContent-Type: application/json. It is consistent, and the OpenAPI description describesit, so a generated client copes. What it is not is the standardised shape — and a client
written against Postulo therefore needs Postulo-specific error handling, which is the one
thing an OpenAPI description is supposed to spare it.
RFC 9457 (Problem Details for HTTP APIs, which obsoletes RFC 7807) is that shape:
application/problem+json, withtype,title,status,detailandinstance, plusextension members.
What changes
detailis already an RFC 9457 member and already a sentence, so for most refusals this isadditive: a client reading
detailkeeps working.typeabout:blankwhere the status code already says everythingtitlestatusdetailinstanceAnd the types worth naming, because a client branches on them:
typevalidation-failederrors— the per-field listrate-limitedretry_after, matching the headerinsufficient-scopescope— the one the token lacksidempotency-key-in-useidempotency-key-reusedThe one breaking change
A validation failure's
detailis a sentence, not a list. Today it is the raw Pydanticerror list, which RFC 9457 does not allow —
detailis a string there. The list moves toan
errorsextension member, unchanged in shape.A client that reads
response.json()["detail"]and prints it is unaffected. A client thatiterates it must read
errorsinstead. This wants a### ⚠️ Breakingchangelog entry withthat instruction.
Content-Typealso becomesapplication/problem+jsonon refusals. A client checking forexactly
application/jsonwill need+jsonsuffix matching, which is what RFC 6839 saysto do anyway.
Not in scope
instancenaming a unique occurrence. Nothing here mints a request id, and an id nobodylogs is worse than the address, which at least says where. If request ids arrive later,
this is where they surface.
title.detailstays translated, because a person debugging reads it.titleis a label for the type — the RFC says it SHOULD be the same for everyoccurrence — and a client that switches on it must not have it move with
Accept-Language. That is whattypeis for, andtitlefollows it.Done when
HttpError,ValidationError,Http404, the throttlehandler — come back as problem documents, from handlers on the
NinjaAPIrather thanfrom changes at each of the twenty-odd raise sites.
Problemschema, so a generated client has a typefor it.
errorsfor a refused capture, notdetail, or the person stops being told which field is wrong #1