Skip to content

feat: add relational/nested insert support and DataBulk node type for graphile-bulk-mutations#1142

Merged
pyramation merged 4 commits into
mainfrom
feat/bulk-mutations-relational
May 12, 2026
Merged

feat: add relational/nested insert support and DataBulk node type for graphile-bulk-mutations#1142
pyramation merged 4 commits into
mainfrom
feat/bulk-mutations-relational

Conversation

@pyramation
Copy link
Copy Markdown
Contributor

Summary

Extends the graphile-bulk-mutations plugin (from PR #1140) with two major features:

1. Relational/Nested Inserts (BulkRelationalPlugin)

Supports nested create fields on bulk insert mutations, so you can create parent + child rows in a single GraphQL mutation:

mutation {
  bulkCreateOrders(input: {
    values: [{
      customerName: "Alice"
      orderItems: [
        { productName: "Widget", quantity: 2, unitPrice: "9.99" }
        { productName: "Gadget", quantity: 1, unitPrice: "19.99" }
      ]
    }]
  }) {
    affectedCount
    returning { id customerName }
  }
}

How it works:

  • Discovers one-to-many FK relations via pgRegistry.pgRelations (isReferencee: true)
  • Registers nested input types with FK columns excluded (auto-populated from parent PKs)
  • Layered execution within a single transaction: parent INSERT → child INSERTs with FK auto-fill
  • Validates that onConflict cannot coexist with nested inserts
  • Opt-in via preset: bulkRelational: true

2. Returning Field Fix

Fixed type resolution for the returning field across all bulk mutation payloads:

  • Uses pgSelectFromRecords + access from grafast/@dataplan/pg to properly wrap raw SQL results into PgSelectStep instances
  • Previously returned raw JS objects that PostGraphile's plan system couldn't resolve fields from

3. DataBulk Node Type (node-type-registry)

New DataBulk node type for blueprint provisioning:

  • Per-operation flags: insert, upsert, update, delete
  • Auto-generates @behavior +bulkInsert +bulkUpsert etc. smart tags during provisioning
  • Located at packages/node-type-registry/src/data/data-bulk.ts

Files Changed

  • New: src/plugins/BulkRelationalPlugin.ts — relation discovery + nested input type registration
  • New: src/utils/relations.tsdiscoverNestedRelations() utility
  • New: packages/node-type-registry/src/data/data-bulk.ts — DataBulk node type
  • Modified: BulkInsertPlugin.ts — layered nested insert execution
  • Modified: BulkTypesPlugin.tspgSelectFromRecords plan for returning field
  • Modified: augmentations.ts, preset.ts, index.ts — new plugin/type exports

Review & Testing Checklist for Human

  • Verify nested insert SQL correctness — Check that FK columns are properly auto-populated from parent PKs and child rows are correctly linked. Run the bulkCreateOrders mutation with nested orderItems against a real database.
  • Test ON CONFLICT + nested insert validation — Confirm that passing both onConflict and nested children throws an appropriate error.
  • Test returning field resolution — Verify that returning { id customerName } works correctly on all bulk mutation types (insert, upsert, update, delete), especially with column-level SELECT grants.
  • Verify DataBulk node type — Check that DataBulk appears in the node type registry exports and has the correct parameter schema.
  • Test empty nested arrays — Confirm that orderItems: [] is a no-op (no child inserts executed).

Notes

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

Adds a PostGraphile v5 plugin for bulk insert, upsert, update, and delete
mutations with ON CONFLICT handling. Features:

- bulkCreateX: INSERT ... VALUES with optional ON CONFLICT DO NOTHING
- bulkUpsertX: INSERT ... ON CONFLICT DO UPDATE SET (per-column control)
- bulkUpdateX: UPDATE ... SET ... WHERE (connection-filter conditions)
- bulkDeleteX: DELETE ... WHERE (connection-filter conditions)

Design decisions:
- Opt-in per table via smart tags (@behavior +bulkInsert etc.)
- Three-layer gating: database enable_bulk + api enable_bulk + smart tags
- Configurable naming: bulk (default), pluralized, many
- Auto-batch at PostgreSQL 32K parameter limit
- Per-table constraint enums and column enums for ON CONFLICT
- Safety guards: bulkMaxRows (1000), bulkRequireWhere (true)

Closes constructive-io/constructive-planning#844
- Fix plan functions: use getRaw('input') for proper Grafast step resolution
- Fix constraint targeting: use column-based ON CONFLICT (col1, col2) instead of
  constraint names, since PgResourceUnique doesn't store PG constraint names
- Fix WHERE clause builder: support both simple equality (Condition type) and
  operator-based (Filter type) conditions
- Fix sql-builder: use conflictColumns instead of constraintName
- Add integration tests: 4 schema generation + 3 insert + 1 upsert + 1 update + 1 delete
- Add test seed SQL with smart tags for bulk behavior opt-in
- Align README with other graphile packages (logo, badges, examples)
- Remove debug logging
- Column-level SELECT grant safety (RETURNING <pk> + follow-up SELECT)
- Add BulkRelationalPlugin for nested insert support on bulk create/upsert
  - Discovers FK relations via pgRegistry.pgRelations (one-to-many)
  - Registers nested input types with FK columns excluded (auto-populated)
  - Layered execution: parent insert → child inserts with FK auto-fill
  - Validates ON CONFLICT cannot coexist with nested inserts
  - Opt-in via preset: bulkRelational: true

- Fix returning field type resolution across all bulk mutation payloads
  - Use pgSelectFromRecords + access from grafast/\@dataplan/pg
  - Properly wraps raw SQL results into PgSelectStep instances
  - Resolves the '.get is not a function' error

- Add DataBulk node type to node-type-registry
  - Per-operation flags: insert, upsert, update, delete
  - Auto-generates @behavior smart tags during blueprint provisioning
  - Registered in data/index.ts exports

- Add test schema: orders (parent) + order_items (child with FK)
- Add 4 relational insert tests (schema introspection, execution, empty arrays, FK exclusion)
- All 14 tests passing
@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

@socket-security
Copy link
Copy Markdown

socket-security Bot commented May 12, 2026

@pyramation pyramation merged commit e9cd73f into main May 12, 2026
37 checks passed
@pyramation pyramation deleted the feat/bulk-mutations-relational branch May 12, 2026 20:25
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