Skip to content

feat: add graphile-realtime-subscriptions plugin (Phase 3a)#1103

Merged
pyramation merged 2 commits intomainfrom
feat/graphile-realtime-subscriptions
May 10, 2026
Merged

feat: add graphile-realtime-subscriptions plugin (Phase 3a)#1103
pyramation merged 2 commits intomainfrom
feat/graphile-realtime-subscriptions

Conversation

@pyramation
Copy link
Copy Markdown
Contributor

@pyramation pyramation commented May 10, 2026

Summary

Adds a new PostGraphile v5 plugin package (graphile-realtime-subscriptions) that discovers tables tagged with @realtime via pgRegistry and generates per-table GraphQL subscription fields.

For each @realtime-tagged table (e.g. projects), the plugin generates:

  • onProjectsChanged(id: UUID): ProjectsSubscriptionPayload subscription field
  • ProjectsSubscriptionPayload type with event: String! (INSERT/UPDATE/DELETE) and projects: Projects (re-queried row)

The plugin uses pgSubscriber.listen() on per-table NOTIFY channels (realtime:{schema}.{table}) and enforces RLS by re-querying through resource.get() on the authenticated connection.

This depends on the per-table NOTIFY trigger added in constructive-db#1090.

Package contents

  • src/plugin.ts — table discovery via @realtime tag, type definition generation, Grafast plan generation
  • src/preset.ts — convenience preset wrapper
  • src/types.ts — options interface (empty for Phase 3a, reserved for 3b+)
  • __tests__/plugin.test.ts — 18 unit tests covering discovery, type generation, plan wiring, NOTIFY channel naming
  • __tests__/preset.test.ts — 2 unit tests for preset structure

Review & Testing Checklist for Human

  • Verify (grafastContext() as any).get('pgSubscriber') is the correct Grafast v5 API — this uses a type cast to escape the context type system. Confirm this is how other plugins (or Graphile's own code) access pgSubscriber at plan time, or whether there's a typed accessor. If there's a proper API, the as any cast should be replaced.
  • Verify the subscribePlan / plan / payload resolver pattern matches Graphile v5 subscription conventions — the current pattern uses listen()object() in subscribePlan, passes through in plan, then re-queries in the payload type resolver. Confirm this is idiomatic for extendSchema-based subscriptions.
  • Evaluate the single-record subscription design — when subscribing with id, ALL table events still wake the subscription; filtering happens only at the re-query layer (resource.get({ id })). This is intentional for Phase 3a simplicity but means unnecessary wake-ups. Confirm this is acceptable.
  • No integration tests exist — all 20 tests mock grafast/graphile-utils entirely. The actual runtime behavior (pgSubscriber connection, NOTIFY delivery, RLS enforcement) is untested. Consider whether an integration test against a real PostGraphile schema is needed before merge.
  • as GraphileConfig.Preset cast in preset.ts — the preset uses a type assertion, suggesting the return shape may not fully match the Preset type. Verify this won't cause issues when composed with other presets.

Suggested test plan: Add the plugin to a PostGraphile preset in a local dev environment, run introspection to confirm onXxxChanged fields appear on @realtime-tagged tables, then test a subscription with a real INSERT/UPDATE/DELETE to verify the NOTIFY → re-query → payload flow works end-to-end.

Notes

  • The pnpm-lock.yaml diff is large but is entirely formatting changes (pnpm version whitespace normalization), not dependency changes.
  • Phase 3b (sparse set mode) and 3c (filtered subset mode) are deferred — the options interface is intentionally empty.
  • The plugin has no eslint config of its own; the repo's root eslint setup has a pre-existing v8/v9 config mismatch that prevents linting from running on any package currently.
  • pnpm build passes cleanly across the full workspace with this plugin included.

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

PostGraphile v5 plugin that discovers tables with @realtime smart tag
and generates per-table GraphQL subscription fields (onXxxChanged).

- Discovers subscriber tables via pgRegistry @realtime tag
- Generates onXxxChanged subscription fields with optional id argument
- Creates XxxSubscriptionPayload types with event + row fields
- Uses pgSubscriber.listen() on per-table NOTIFY channels
- Enforces RLS via resource.get() on authenticated connection
- Includes preset wrapper for convenient plugin inclusion
- 20 unit tests covering discovery, types, plans, and preset
@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 6d5334b into main May 10, 2026
54 checks passed
@pyramation pyramation deleted the feat/graphile-realtime-subscriptions branch May 10, 2026 19:43
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