Skip to content

fix(graphile-postgis): camelCase @spatialRelation field + PascalCase filter type#1006

Merged
pyramation merged 1 commit intomainfrom
fix/spatial-relation-casing
Apr 18, 2026
Merged

fix(graphile-postgis): camelCase @spatialRelation field + PascalCase filter type#1006
pyramation merged 1 commit intomainfrom
fix/spatial-relation-casing

Conversation

@pyramation
Copy link
Copy Markdown
Contributor

Summary

A @spatialRelation tag like

COMMENT ON COLUMN places.geom IS
  E'@spatialRelation inside_neighborhood neighborhoods.geom st_within';

used to generate a filter whose casing passed the raw tag identifier through verbatim:

export interface PlaceFilter {
  // …
  /** Filter by rows from `Neighborhood` related to this row via `st_within`. */
  inside_neighborhood?: PlaceSpatialInside_neighborhoodFilter;
}

The fix routes the relation name through the standard Graphile inflectors so spatial-relation identifiers follow the same conventions as every other generated GraphQL type/field:

  insideNeighborhood?: PlaceSpatialInsideNeighborhoodFilter;

Concretely:

  • <ref_snippet file="/home/ubuntu/repos/constructive/graphile/graphile-postgis/src/plugins/spatial-relations.ts" lines="430-439" /> — filter type name now uses inflection.upperCamelCase(rel.relationName) instead of a raw charAt(0).toUpperCase() splice.
  • <ref_snippet file="/home/ubuntu/repos/constructive/graphile/graphile-postgis/src/plugins/spatial-relations.ts" lines="644-650" /> — the field name on the owner filter type now uses inflection.camelCase(rel.relationName).
  • <ref_snippet file="/home/ubuntu/repos/constructive/graphile/graphile-postgis/src/plugins/spatial-relations.ts" lines="325-330" /> — the parametric arg field name (e.g. travel_distancetravelDistance) is normalized at collection time so both the GraphQL field and the value[paramFieldName] read inside apply() stay consistent.

Both inflectors are idempotent on already-camelCased input, so existing tags such as nearbyClinic / intersectingCounty keep producing the exact same identifiers they do today (the TelemedicineClinicFilter.nearbyClinic / distance assertions in graphql/orm-test/__tests__/postgis-spatial-relations.test.ts all still hold).

Drive-by: connection-filter augmentation barrel

While adding the test, jest surfaced a pre-existing TS2339 in the same plugin:

src/plugins/spatial-relations.ts:703:29 - error TS2339: Property 'filterType' does not exist on type 'Inflection'.

graphile-connection-filter/src/augmentations.ts declares the filterType / filterManyType inflection augmentations, but the package barrel never imported it — so any satellite plugin that only did import 'graphile-connection-filter'; (as this one does) couldn't see the augmentations. Every plugin inside the package imports ../augmentations directly, but downstream consumers can't reach into that path.

Fixed by loading the augmentations from the public barrel: <ref_snippet file="/home/ubuntu/repos/constructive/graphile/graphile-connection-filter/src/index.ts" lines="52-59" />

Review & Testing Checklist for Human

  • Regenerate the public SDK against a DB with at least one @spatialRelation tag and confirm the emitted *Filter interface now uses camelCase field names and PascalCase filter-type names (including the PlaceSpatial*Filter that triggered this).
  • Confirm any hand-written callers of where: { inside_neighborhood: … } (should be none, but worth a quick grep across downstream apps) — the field name changes from snake_case to camelCase. This is a breaking change on the generated schema if anyone was somehow writing snake_case relation tags in production.
  • Spot-check that a tag with a snake_case parametric arg name (e.g. st_dwithin travel_distance) produces a GraphQL field named travelDistance and that the subfilter still propagates the numeric value correctly at runtime.

Notes

  • All 250 graphile-postgis unit tests still pass; a new camelCases snake_case parametric arg names test pins the parametric-arg fix.
  • No changes to the tag grammar, operator registry, or SQL emit path.

Link to Devin session: https://app.devin.ai/sessions/51d10f7657484009ba4b4c303b9704e4
Requested by: @pyramation

…filter type

The generated GraphQL for a @spatialRelation tag like
  '@spatialRelation inside_neighborhood neighborhoods.geom st_within'

used to produce a field and type whose casing passed through the raw
tag identifier verbatim:

  inside_neighborhood?: PlaceSpatialInside_neighborhoodFilter;

Normalize the relation name through the standard Graphile inflectors so
the GraphQL identifiers follow the same conventions as every other
generated field/type:

  insideNeighborhood?: PlaceSpatialInsideNeighborhoodFilter;

Also camelCase the parametric arg name (e.g. 'travel_distance' →
'travelDistance') on parametric ops (st_dwithin). Added a unit test
that pins this behavior.

While here, re-export graphile-connection-filter's declaration-merge
augmentations from its package entry so that satellite plugins (like
graphile-postgis) that type-only import the package actually pick up
the Inflection.filterType/filterManyType augmentations the file
already declared — the satellite plugin was previously relying on an
augmentation chain that was never loaded via the public barrel, which
produced TS2339 errors during typecheck.
@devin-ai-integration
Copy link
Copy Markdown
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

@pyramation pyramation merged commit 4975a23 into main Apr 18, 2026
51 checks passed
@pyramation pyramation deleted the fix/spatial-relation-casing branch April 18, 2026 10:50
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