Skip to content

feat(go): PostgREST/Supabase query dialect in generated services (parity with TS #36/#56-#60) - #118

Open
cultron wants to merge 1 commit into
mainfrom
feat/postgrest-dialect-go
Open

feat(go): PostgREST/Supabase query dialect in generated services (parity with TS #36/#56-#60)#118
cultron wants to merge 1 commit into
mainfrom
feat/postgrest-dialect-go

Conversation

@cultron

@cultron cultron commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Summary

Ports the @apso/crud PostgREST/Supabase dialect to the Go (Gin+GORM) generator, so generated services accept the same PostgREST URL conventions as our TypeScript services (@apso/crud + @apso/postgrest-request). This is the Go port of the TS dialect; the incumbent nestjsx dialect is unchanged (byte-identical output — ParseQueryParams is retained and still delegates to the nestjsx parser).

Extends (does not replace) the generated utils/query.go and threads a dialect step through the generated handler + service.

Dialect detection (mirrors crud/src/dialect.ts)

  • X-Crud-Dialect header wins: postgrest | nestjsx, any other value ⇒ 400.
  • Else param-shape: select/order or a bare col=op.value ⇒ postgrest; fields/filter/join/sort/s/or/per_page ⇒ nestjsx; both families ⇒ 400 (ambiguous); limit/offset/page/cache are dialect-neutral; default nestjsx.
  • Note (parity with TS): or is a nestjsx-signal key, so a PostgREST or=() group must be forced via the header (or accompany another postgrest signal without a nestjsx one). select + or together is an intentional 400.

Operators / behaviors implemented (parity with postgrest-parser.ts)

  • select: select=a,b, alias:col output rename ⇒ col AS alias (CLI first pivot: new command surface, architecture refactor, tests #57), ::type cast stripped, embeds rel(cols) ⇒ GORM preloads.
  • filters col=op.value and not.<op>: eq, neq, gt, gte, lt, lte, like, ilike, in, is (incl. is.null/is.true/is.false, not.in, negations). like/ilike translate the PostgREST * wildcard to SQL % and apply the pattern verbatim.
  • logical groups: or=(...) / and=(...), including nested or(...)/and(...), ANDed with the flat column filters (Plat 1269 #56).
  • order: order=col.asc|desc[.nullsfirst|nullslast]ORDER BY col DIR [NULLS FIRST|LAST].
  • pagination / response shape: PostgREST limit/offsetbare JSON array (no envelope), offset windows the array (feat: MCP server + CLI modernization #58); nestjsx keeps its {data,count,total,page,pageCount} envelope.
  • errors: unknown filter/sort/select column on the postgrest path ⇒ 400 (*QueryError), not 500 (Feature: auto-detect codegen/schema issues from real usage and (opt-in) auto-file GitHub issues #60); nestjsx still silently drops unknown columns. Bad header / ambiguous mix / bad operator ⇒ 400.

Deferred

nestjsx unchanged

The nestjsx parsing, filter/or/sort/join handling, and envelope response are untouched. ParseQueryParams still exists and delegates to parseNestjsxParams.

Tests

  • Generator tests assert the emitted Go covers: dialect detection + header precedence, the full operator map, deferred-op rejection, or=/and= groups, alias rename, order nulls, bare-array + offset, unknown-column 400, and the unchanged nestjsx entry point.
  • A go build test generates a full sample service and compiles it with the real Go toolchain.
  • npm run build + the go-generator jest suite are green (46 tests; full repo suite 334 green). Verified locally with additional GORM+sqlite runtime tests (eq, or=() via header, order+limit+offset bare windowing, like wildcard, unknown-column 400) — all passing.

Do not merge — left green for review.

🤖 Generated with Claude Code

…ity with TS #36/#56-#60)

Port the @apso/crud PostgREST dialect to the Go (Gin+GORM) generator so
generated services accept the same PostgREST URL conventions as the TypeScript
services. The incumbent nestjsx dialect is unchanged (byte-identical output;
ParseQueryParams is retained and still delegates to the nestjsx parser).

Generated query util (utils/query.go):
- DetectDialect mirrors crud/src/dialect.ts: X-Crud-Dialect header wins
  (postgrest|nestjsx else 400); else param-shape (select/order or bare
  col=op.value => postgrest; fields/filter/join/sort/s/or/per_page => nestjsx;
  both => 400 ambiguous; limit/offset/page/cache neutral; default nestjsx).
- PostgREST parser mirrors postgrest-request/src/postgrest-parser.ts:
  select=a,b (+ alias:col rename #57, + embeds rel(cols) as GORM preloads),
  col=op.value for eq/neq/gt/gte/lt/lte/like/ilike/in/is and not.<op>,
  or=(...)/and=(...) logical groups incl. nesting (#56),
  order=col.asc|desc[.nullsfirst|nullslast], limit/offset. Operator mapping and
  *->% like/ilike wildcard match the TS core.
- cs/cd/ov/fts (and other advanced ops) are recognized and rejected clearly as
  deferred (#54), not silently mishandled.

Response shaping and errors:
- PostgREST path returns a bare JSON array (no envelope) via FindAllBare (#58);
  nestjsx keeps its {data,count,total,page,pageCount} envelope.
- Unknown filter/sort/select column on the postgrest path is a *QueryError the
  handler maps to 400, not 500 (#60). nestjsx keeps silently dropping unknown
  columns.
- Handler threads the X-Crud-Dialect header + query into utils.ParseQuery and
  branches on opts.Bare.

Deferred / scoped for Go: embedded ON-clause filters (#59). The Go embed path
uses GORM Preload, which has no embed-level predicate, so an rel.col= filter is
treated as a root filter rather than shaping only embedded rows. select embeds
still project relation columns. This is the only intentional gap vs the TS
TypeORM join path.

Tests (test/lib/generators/go.test.ts): assert the emitted Go for detection,
operator map, deferred-op rejection, or=/and= groups, alias rename, order nulls,
bare-array + offset, unknown-column 400, and unchanged nestjsx entry point. A
go-build test generates a full sample service and compiles it with the real Go
toolchain. npm build + the go-generator jest suite are green (46 tests).

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
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.

1 participant