fix(postgrest): serialize generated typed dict payloads (#1443)#1483
Open
SM1LE-X wants to merge 1 commit intosupabase:mainfrom
Open
fix(postgrest): serialize generated typed dict payloads (#1443)#1483SM1LE-X wants to merge 1 commit intosupabase:mainfrom
SM1LE-X wants to merge 1 commit intosupabase:mainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR updates the PostgREST write path so generated Python TypedDict payloads can be used with insert-like operations when they contain PostgreSQL-derived Python values such as UUID and temporal types. It fits into the request-builder layer by widening write payload typing and replacing httpx(json=...) serialization with an explicit JSON encoding step before requests are sent.
Changes:
- Added request-body serialization for
UUID,datetime,date, andtimevalues before dispatching non-GET/HEAD requests. - Introduced new write-payload typing for
insert,upsert, andupdatein both sync and async request builders. - Added sync/async request-builder tests for generated
TypedDictpayload serialization and related request-body behavior.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
src/postgrest/tests/_sync/test_request_builder.py |
Adds sync tests for generated TypedDict inserts, request body encoding, content type preservation, and NaN rejection. |
src/postgrest/tests/_async/test_request_builder.py |
Adds async tests covering generated TypedDict insert serialization. |
src/postgrest/src/postgrest/types.py |
Introduces the new internal request/write payload type aliases. |
src/postgrest/src/postgrest/base_request_builder.py |
Implements custom request JSON serialization and routes write requests through encoded request content. |
src/postgrest/src/postgrest/_sync/request_builder.py |
Updates sync insert/upsert/update signatures to use the new write-payload type. |
src/postgrest/src/postgrest/_async/request_builder.py |
Updates async insert/upsert/update signatures to use the new write-payload type. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| ) | ||
| JSONAdapter: TypeAdapter = TypeAdapter(JSON) | ||
| RequestJSON = Any | ||
| WriteJSON = Union[Mapping[str, Any], Sequence[Mapping[str, Any]]] |
Comment on lines
+172
to
+204
| async def test_insert_serializes_generated_typeddict_values(self): | ||
| inserted_id = uuid4() | ||
| payload: GeneratedMovieInsert = { | ||
| "id": inserted_id, | ||
| "created_at": datetime(2026, 4, 6, 12, 30, tzinfo=timezone.utc), | ||
| "release_date": date(2026, 4, 6), | ||
| "starts_at": time(12, 30), | ||
| "name": "Inception", | ||
| } | ||
|
|
||
| async def handler(request: Request) -> Response: | ||
| assert request.headers["content-type"] == "application/json" | ||
| assert request.headers["content-length"] == str(len(request.content)) | ||
| assert request.content == ( | ||
| b'{"id":"' | ||
| + str(inserted_id).encode() | ||
| + b'","created_at":"2026-04-06T12:30:00+00:00",' | ||
| + b'"release_date":"2026-04-06","starts_at":"12:30:00",' | ||
| + b'"name":"Inception"}' | ||
| ) | ||
| return Response(201, json=[{"id": str(inserted_id)}], request=request) | ||
|
|
||
| async with AsyncClient(transport=MockTransport(handler)) as client: | ||
| builder = AsyncRequestBuilder( | ||
| client, | ||
| URL("https://example.supabase.co/rest/v1/movies"), | ||
| Headers(), | ||
| None, | ||
| ) | ||
|
|
||
| response = await builder.insert(payload).execute() | ||
|
|
||
| assert response.data == [{"id": str(inserted_id)}] |
1ea951d to
3fadcf9
Compare
Contributor
Author
|
Addressed the write-surface coverage gap and tightened the payload alias:
Verification after the update:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What kind of change does this PR introduce?
Issue
Fixes #1443
What is the current behavior?
Generated Python REST types can produce
TypedDictwrite payloads with PostgreSQL-derived Python values such asUUID,datetime,date, andtime.The current
insert,upsert, andupdatesignatures only accept the recursive responseJSONtype, so generatedTypedDictpayloads are rejected by type checkers. Widening the annotation alone does not fix the runtime path because request execution still relies on stdlib JSON serialization through HTTPX, which raisesTypeErrorforUUIDanddatetimevalues.What is the new behavior?
This PR keeps response
JSONparsing strict and adds a separate write payload type for row-shaped request bodies.Write methods now accept generated-style payloads, and PostgREST serializes supported generated values before sending the HTTP request body:
UUIDis serialized withstr(value).datetime,date, andtimeare serialized withvalue.isoformat().Content-Type: application/jsonwhen absent, andallow_nan=False.Changes made:
WriteJSONforMapping[str, Any] | Sequence[Mapping[str, Any]]write payloads.insert,upsert, andupdateto useWriteJSON.TypedDictvalues serialize correctly at runtime.TypedDictpayloads containingUUID,datetime,date, andtime.Content-Typeand rejecting non-finite floats.Verification
uv run --package postgrest mypy src/postgrest testsuv run --package postgrest pytest --cov=./ --cov-report=xml -vv- 292 passeduv run ruff check <touched files>uv run ruff format --check <touched files>TypedDictpayloads - 0 errorsChecklist: