Implementing Batch Operations

1. Creating Batch Mutations

Example: Batch create

type Mutation {
  createPosts(inputs: [CreatePostInput!]!): CreatePostsPayload!
}

type CreatePostsPayload {
  posts: [Post!]!
  errors: [BatchError!]!
}
type BatchError { index: Int!, message: String!, code: String! }

2. Using Input Lists

PatternDetail
List of input objectsinputs: [CreateXInput!]!
BoundedMax 100 per batch (configurable)
Order-preservingIndex in result matches input index

3. Returning Batch Results

FieldDetail
results: [Result!]!One per input, in order
errorsIndexed errors
summaryCounts: success, failed, total

4. Handling Partial Failures

StrategyWhen
All-or-nothing (transaction)Strong consistency required
Best-effort (independent)Each input independent (e.g., notifications)
Result union per itemPer-item Success | Error

5. Validating Batch Size

Example: Cap batch input

if (inputs.length > 100) {
  throw new GraphQLError("Batch too large (max 100)", { extensions: { code: "BAD_USER_INPUT" } });
}

6. Implementing Transactional Batches

Example: DB transaction

return db.$transaction(async (tx) => {
  const posts = [];
  for (const input of inputs) {
    posts.push(await tx.post.create({ data: input }));
  }
  return { posts, errors: [] };
});

7. Optimizing Batch Performance

TechniqueDetail
createManySingle INSERT for the batch
Bulk validate firstFail fast before any write
Parallelize independent itemsPromise.all with concurrency limit

8. Using Batch Loaders

UseDetail
DataLoader inside resolverBatch lookups while building results
Prime after batchCache the newly-created entities

9. Implementing Batch Queries

Example: Batch get

type Query {
  usersByIds(ids: [ID!]!): [User]!  # null for missing, preserves order
}

10. Returning Batch Metadata

FieldDetail
processedCountTotal processed
successCountSuccessful
failedCountFailed (with errors)
durationMsServer-side wall time