Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/run-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,8 @@ jobs:
env: {}
- package: graphile/graphile-ltree
env: {}
- package: graphile/graphile-bulk-mutations
env: {}
- package: graphql/orm-test
env: {}
- package: graphql/test
Expand Down
139 changes: 139 additions & 0 deletions graphile/graphile-bulk-mutations/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
# graphile-bulk-mutations

<p align="center" width="100%">
<img height="250" src="https://raw.githubusercontent.com/constructive-io/constructive/refs/heads/main/assets/outline-logo.svg" />
</p>

<p align="center" width="100%">
<a href="https://github.com/constructive-io/constructive/actions/workflows/run-tests.yaml">
<img height="20" src="https://github.com/constructive-io/constructive/actions/workflows/run-tests.yaml/badge.svg" />
</a>
<a href="https://github.com/constructive-io/constructive/blob/main/LICENSE"><img height="20" src="https://img.shields.io/badge/license-MIT-blue.svg"/></a>
<a href="https://www.npmjs.com/package/graphile-bulk-mutations"><img height="20" src="https://img.shields.io/github/package-json/v/constructive-io/constructive?filename=graphile%2Fgraphile-bulk-mutations%2Fpackage.json"/></a>
</p>

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

Adds bulk mutation support to PostGraphile:

- **Bulk insert** with optional `ON CONFLICT DO NOTHING` (ignore duplicates)
- **Bulk upsert** with `ON CONFLICT DO UPDATE SET` (selective column updates)
- **Bulk update** with condition-based WHERE clauses
- **Bulk delete** with condition-based WHERE clauses
- Column-level SELECT grant safety (`RETURNING <pk>` + follow-up SELECT)
- PG parameter batching (auto-split at 32K limit)
- Opt-in via smart tags (`@behavior +bulkInsert +bulkUpsert +bulkUpdate +bulkDelete`)

## Usage

```typescript
import { BulkMutationPreset } from 'graphile-bulk-mutations';

const preset: GraphileConfig.Preset = {
extends: [
BulkMutationPreset(),
],
};
```

## Per-Table Opt-In

Tables must opt in via smart tags — no bulk mutations are generated by default:

```sql
COMMENT ON TABLE users IS E'@behavior +bulkInsert +bulkUpsert +bulkUpdate +bulkDelete';
```

You can enable specific operations per table:

```sql
COMMENT ON TABLE orders IS E'@behavior +bulkInsert +bulkUpdate';
COMMENT ON TABLE categories IS E'@behavior +bulkInsert +bulkUpsert';
```

## Example Queries

### Bulk Insert

```graphql
mutation {
bulkCreateUsers(input: {
values: [
{ name: "Alice", email: "alice@example.com" }
{ name: "Bob", email: "bob@example.com" }
]
}) {
affectedCount
}
}
```

### Bulk Insert with ON CONFLICT DO NOTHING

```graphql
mutation {
bulkCreateUsers(input: {
values: [
{ name: "Alice", email: "alice@example.com" }
]
onConflict: {
constraint: USERS_EMAIL_KEY
action: IGNORE
}
}) {
affectedCount
}
}
```

### Bulk Upsert

```graphql
mutation {
bulkUpsertUsers(input: {
values: [
{ name: "Alice Updated", email: "alice@example.com" }
]
onConflict: {
constraint: USERS_EMAIL_KEY
}
}) {
affectedCount
}
}
```

### Bulk Update

```graphql
mutation {
bulkUpdateUsers(input: {
where: { name: "Alice" }
patch: { name: "Alice Updated" }
}) {
affectedCount
}
}
```

### Bulk Delete

```graphql
mutation {
bulkDeleteUsers(input: {
where: { name: "Bob" }
}) {
affectedCount
}
}
```

## Configuration

```typescript
BulkMutationPreset({
bulkMaxRows: 1000, // Max rows per insert/upsert (default: 1000)
bulkRequireWhere: true, // Require WHERE on update/delete (default: true)
bulkNaming: 'bulk', // Naming strategy: 'bulk' | 'pluralized' | 'many'
})
```
Loading
Loading