---
title: "Building Resilient GraphQL APIs Using Idempotency"
description: "Shopify’s Payment Service uses API-level idempotency to make GraphQL payment mutations resilient to timeouts, connection failures, and ambiguous outcomes that could otherwise leave merchants unpaid or double-charge buyers. The design places a UUID-based idempotency key directly in each mutation input, identifies requests by client plus key, and locks concurrent duplicates so retries receive a 409 response while the original call is processing. An IncomingRequest record stores request state and completed recovery points, allowing completed responses to be replayed and incomplete attempts to recover before continuing. Handlers divide work into no-side-effect, transactional local-side-effect, and remote-side-effect steps, each optionally providing run and recover functions. The approach adds database-write overhead and requires careful compatibility testing when recovery points change, but the team reports that its structured handlers are clear and maintainable."
---

# Building Resilient GraphQL APIs Using Idempotency

[Shopify](https://yomu.fyi/company/shopify) · 2023-10-18 · Aug 27, 2019

**Type:** Problem & solution

## Summary

Shopify’s Payment Service uses API-level idempotency to make GraphQL payment mutations resilient to timeouts, connection failures, and ambiguous outcomes that could otherwise leave merchants unpaid or double-charge buyers. The design places a UUID-based idempotency key directly in each mutation input, identifies requests by client plus key, and locks concurrent duplicates so retries receive a 409 response while the original call is processing. An IncomingRequest record stores request state and completed recovery points, allowing completed responses to be replayed and incomplete attempts to recover before continuing. Handlers divide work into no-side-effect, transactional local-side-effect, and remote-side-effect steps, each optionally providing run and recover functions. The approach adds database-write overhead and requires careful compatibility testing when recovery points change, but the team reports that its structured handlers are clear and maintainable.

## Context

Payment requests can fail or time out after processing has begun, leaving clients unable to determine whether a charge succeeded. Retrying without coordination could leave merchants unpaid or double-charge buyers, while manual fixes and automatic reconciliation introduce scalability and maintenance problems.

## Approach / What changed

The service uses client-plus-idempotency-key request identity, duplicate-request locking, and IncomingRequest records to track completion and recovery progress. Mutation handlers are split into named steps according to their side effects, with transactional local writes and optional run and recover functions for resuming incomplete requests.

## Takeaways

- Idempotency keys are first-class GraphQL mutation inputs rather than HTTP headers, allowing required-key validation and errors to use the normal GraphQL mechanism.
- IncomingRequest records distinguish new calls from retries, store the last completed recovery point, and allow a completed response to be returned immediately.
- Recovery-point changes require compatibility checks and handler tests covering every step and recovery scenario; the design also adds database-write overhead.

**Tags:** [Databases](https://yomu.fyi/topic/databases), [GraphQL](https://yomu.fyi/topic/graphql), [Idempotency](https://yomu.fyi/topic/idempotency), [Reliability](https://yomu.fyi/topic/reliability)

- Source: [Shopify](https://shopify.engineering/building-resilient-graphql-apis-using-idempotency)
- Source URL: https://shopify.engineering/building-resilient-graphql-apis-using-idempotency
- Ingested by Yomu: 2026-08-31T01:13:58.782Z

[Read original post](https://shopify.engineering/building-resilient-graphql-apis-using-idempotency)
