Skip to content

feat(postgrest)!: only accept the table's Insert and Update types on the typed builder - #1849

Merged
spydon merged 2 commits into
mainfrom
lukasklingsbo/sdk-1879-typed-insert-update
Sep 17, 2026
Merged

spydon merged 2 commits into
mainfrom
lukasklingsbo/sdk-1879-typed-insert-update

Conversation

@spydon

@spydon spydon commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

Summary

client.table(X.table) was typed on the way out but not on the way in: insert and upsert took Object and update took Map<String, dynamic>, so a payload for the wrong table or with a misspelled column compiled. This closes that gap on the typed surface. The untyped from() path keeps accepting raw maps.

Changes

  • PostgrestTable<Row, Insert, Update> carries the write types next to the row type. Insert and Update are unbounded on purpose: a bound would make Dart silently infer it when the type arguments are left off, while without one strict-inference reports the missing arguments. Hand-written tables spell them out, for example PostgrestTable<Book, BookInsert, BookUpdate>('books', Book.new). Read-only relations use Never, which makes the write methods uncallable.
  • PostgrestTypedQueryBuilder.insert and upsert take one Insert, the new insertAll and upsertAll take a List<Insert>, and update takes Update. Dart has no union types, so single and bulk writes are separate methods, mirroring add and addAll.
  • insertAll and upsertAll throw on an empty list, like the other argument checks on the typed builder. This also closes the one call that still compiled on a Never table, insertAll([]).
  • The filter, transform and stream builders only ever needed the row converter, so they hold a RowConverter<Row> instead of the table and keep a single Row type parameter.
  • The untyped PostgrestQueryBuilder.update accepts Object like insert and upsert already did, so the typed layer can forward any Update type. Existing callers are unaffected.
  • supabase_typegen emits the three type arguments on the table constant, with Never for relations without an insert or update surface. The generated row, insert and update extension types implement Object instead of Map<String, dynamic>: the map interface let a checked BooksInsert be mutated or indexed with arbitrary keys after construction, and let a column named like a Map member break the generated code. Row types gain toJson() as the explicit way to get at the decoded map.
  • Typegen only suffixes generated member names that collide with Object members now. Reserving every Map member name was there because the row types implemented Map, so a column called length or a relation to a table called map no longer gets a $.
  • PostgrestTypedQueryBuilder.insertAll and upsertAll are registered in sdk-compliance.yaml.

Testing

  • packages/postgrest/test/typed_query_test.dart uses hand-written BookInsert and BookUpdate types, a Never table for authors, and covers insertAll, upsertAll and their empty-list checks.
  • packages/supabase_test/test/typed_api_test.dart, packages/supabase/test/mock_test.dart and stream_filter_test.dart declare their write types.
  • Typegen goldens regenerated; the behavior test now checks copy semantics of setXToNull through the request body since the update type is opaque.
  • dart analyze, dcm analyze and the sdk compliance symbol, drift and schema checks pass locally.

Closes SDK-1879.

Summary by CodeRabbit

  • New Features

    • Added strongly typed insert, update, and upsert operations using dedicated value types.
    • Added bulk insertAll and upsertAll operations, including validation for empty input.
    • Generated row types now provide toJson() for serialization.
    • Read-only relations prevent unsupported write operations through their types.
  • Documentation

    • Updated typed API guidance and examples for read, insert, update, and upsert workflows.

…the typed builder

PostgrestTable carries Insert and Update type parameters next to Row, and
the typed query builder's insert, insertAll, upsert, upsertAll and update
methods accept only those types. Read-only relations use Never for the
write types they do not support. supabase_typegen emits the type
arguments, drops the Map interface from the generated extension types and
adds toJson to row types. The untyped update accepts Object like insert.
@spydon
spydon requested a review from a team as a code owner September 17, 2026 12:40
@coderabbitai

coderabbitai Bot commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 35b8357b-d10d-4f47-b1dd-1e28d646227b

📥 Commits

Reviewing files that changed from the base of the PR and between a3a7559 and d6693e9.

📒 Files selected for processing (24)
  • packages/postgrest/lib/src/postgrest.dart
  • packages/postgrest/lib/src/postgrest_query_builder.dart
  • packages/postgrest/lib/src/postgrest_table.dart
  • packages/postgrest/lib/src/postgrest_typed_builder.dart
  • packages/postgrest/lib/src/postgrest_typed_filter_builder.dart
  • packages/postgrest/lib/src/postgrest_typed_query_builder.dart
  • packages/postgrest/lib/src/postgrest_typed_transform_builder.dart
  • packages/postgrest/test/typed_query_test.dart
  • packages/supabase/lib/src/supabase_client.dart
  • packages/supabase/lib/src/supabase_query_schema.dart
  • packages/supabase/lib/src/supabase_typed_query_builder.dart
  • packages/supabase/lib/src/supabase_typed_stream_builder.dart
  • packages/supabase/test/mock_test.dart
  • packages/supabase/test/stream_filter_test.dart
  • packages/supabase_test/test/typed_api_test.dart
  • packages/supabase_typegen/README.md
  • packages/supabase_typegen/lib/src/dart_generator.dart
  • packages/supabase_typegen/lib/src/identifiers.dart
  • packages/supabase_typegen/test/dart_generator_test.dart
  • packages/supabase_typegen/test/generated_schema_behavior_test.dart
  • packages/supabase_typegen/test/goldens/hostile_schema.dart
  • packages/supabase_typegen/test/goldens/supabase_schema.dart
  • packages/supabase_typegen/test/identifiers_test.dart
  • sdk-compliance.yaml

Included review availability: Your plan provides up to 4 included reviews per hour; 2 remain after this review.


📝 Walkthrough

Walkthrough

The PR adds row, insert, and update type parameters to typed PostgREST and Supabase tables and builders. It adds batch insert and upsert methods, passes row converters through builder chains, and updates type generation, generated schemas, tests, and SDK capability metadata.

Changes

Typed mutation API

Layer / File(s) Summary
PostgREST typed contracts
packages/postgrest/lib/src/*
Tables and query builders now carry Row, Insert, and Update types. Insert, upsert, and update methods use typed payloads. insertAll and upsertAll support non-empty batches.
Row converter propagation
packages/postgrest/lib/src/*, packages/supabase/lib/src/*
Typed filter, transform, and stream builders now receive row converter functions instead of table objects.
Typed API validation
packages/postgrest/test/*, packages/supabase/test/*, packages/supabase_test/test/*, sdk-compliance.yaml
Tests define typed payloads and tables, cover batch mutations and empty-list errors, and register the new capability symbols.
Generated typed schema output
packages/supabase_typegen/*
Generated rows and payloads implement Object; rows expose toJson(). Generated tables specify insert and update types, using Never for unsupported operations. Identifier handling and generated-schema expectations were updated.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant SupabaseClient
  participant PostgrestTypedQueryBuilder
  participant PostgREST
  Caller->>SupabaseClient: provide typed PostgrestTable
  SupabaseClient->>PostgrestTypedQueryBuilder: create typed builder
  Caller->>PostgrestTypedQueryBuilder: call insert, upsert, or update
  PostgrestTypedQueryBuilder->>PostgREST: send typed value as request body
  PostgREST-->>PostgrestTypedQueryBuilder: return response rows
  PostgrestTypedQueryBuilder-->>Caller: convert rows with rowFromJson
Loading

Suggested reviewers: grdsdev

Merge Risk: ⚪ Minimal · up to d6693

This change tightens the typed database API so insert, upsert, and update calls only accept the value types generated for each table, and adds batch insert and upsert helpers. Generated code and tests were updated consistently, and no outstanding correctness or availability concerns remain, so it is ready to merge.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 0…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: typed builders now accept the table-specific Insert and Update types. The breaking-change marker is appropriate for the public API changes.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

…r names in typegen

insertAll and upsertAll throw on an empty list, matching the other
argument checks on the typed builder. Generated extension types no longer
implement Map, so only the Object members need a suffix in generated
member names.
@spydon
spydon merged commit 333cc5c into main Sep 17, 2026
53 checks passed
@spydon
spydon deleted the lukasklingsbo/sdk-1879-typed-insert-update branch September 17, 2026 15:10
spydon added a commit that referenced this pull request Sep 17, 2026
…ter (#1850)

## Summary

Main does not compile since #1848 and #1849 merged within minutes of
each other. #1849 replaced the typed transform builder's
`PostgrestTable` field with a `RowConverter<Row>` (`_rowFromJson`), and
#1848 added `stripNulls`, `dryRun`, `maxAffected` and `csv` on top of
the old field. Neither PR's CI saw the other, so every check on main and
on the open PRs fails at compile time in
`postgrest_typed_transform_builder.dart`:

```
Error: The getter '_table' isn't defined for the type 'PostgrestTypedTransformBuilder<Row, T>'.
```

## Changes

- The four methods from #1848 pass `_rowFromJson` to the private
constructor, like the rest of the class does after #1849.
- The two tests from #1848 that passed raw maps use `BookInsert` and
`BookUpdate`, which #1849 made the only accepted payload types on the
typed builder.
- The `dryRun` dartdoc example uses `BookInsert` as well.

No public API change. `dart analyze` on `packages` and `examples` is
clean, `typed_query_test.dart` passes (49), DCM is clean.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **Bug Fixes**
- Improved typed mutation operations so options such as dry runs,
affected-row limits, CSV responses, and null stripping consistently
preserve the expected result types.
- Updated typed insert and update usage in mutation scenarios to ensure
submitted data is handled with the correct typed representations.

- **Documentation**
- Updated the dry-run example to demonstrate usage with a typed insert
value.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants