GraphQL in Production · Capstone

Capstone: Design a Marketplace Graph

Design the schema, pagination and mutations for a two-sided marketplace.

Scenario

A marketplace connects sellers and buyers. The web app, iOS and Android apps need: product search with facets, product pages (seller info, reviews, related products), a cart and checkout, and a seller dashboard (orders, payouts). Data lives in four services: catalog, orders, payments and users.

Requirements

  • Schema for products, sellers, reviews, cart, orders
  • Paginated lists everywhere
  • Checkout with explicit business outcomes
  • Seller-only fields hidden from buyers

Constraints

  • Product page p95 < 400 ms
  • Mobile apps can't be force-updated for 6 months
  • Four backend services owned by different teams

Deliverables

  1. Sketch the core types, queries and the checkout mutation.
  2. How will the server avoid N+1 queries and protect itself from expensive queries?
  3. Organize ownership across four teams and plan schema evolution for old mobile apps.

Hints

  • List every client screen and the fields it needs before writing types.
  • Unions must be extended carefully: old clients need a default branch.

Draft each deliverable below before revealing the model answer and rubric.

Further reading: GraphQL best practices

Deliverables

Sketch the core types, queries and the checkout mutation.

Model answer

interface Node { id: ID! }
type Product implements Node { id: ID! title: String! price: Money! seller: Seller! reviews(first: Int = 10, after: String): ReviewConnection! related(first: Int = 4): [Product!]! }
type Seller implements Node { id: ID! name: String! rating: Float payouts(first: Int = 20, after: String): PayoutConnection @auth(requires: SELLER_OWNER) }
type Query { node(id: ID!): Node  search(query: String!, filters: SearchFilters, first: Int = 24, after: String): ProductSearchResult!  viewer: User }
union CheckoutResult = CheckoutSuccess | PaymentDeclined | OutOfStock | PriceChanged
type Mutation { checkout(input: CheckoutInput!): CheckoutResult! addCartItem(input: AddCartItemInput!): AddCartItemPayload! }

Rubric

Strong answers: Node interface + global IDs, connections with capped first, Money scalar/type, checkout as a union of explicit outcomes, seller-only fields guarded. Misses: generic updateCart(JSON), unbounded lists, payouts exposed without authorization.

How will the server avoid N+1 queries and protect itself from expensive queries?

Model answer

  • Per-request DataLoaders for sellers, reviews, products by id (batched across services)
  • Max depth 8, cost analysis (list sizes from first/last), max first = 100, alias limits, timeouts
  • Trusted documents for web/iOS/Android; public partner access (if any) via REST
  • Cost-based rate limits per client

Rubric

Look for batching per request, cost budgets tied to pagination args, and trusted documents for first-party clients.

Organize ownership across four teams and plan schema evolution for old mobile apps.

Model answer

  • Federation: catalog, orders, payments, users subgraphs; Product/Seller/Order as entities with @key(id); router composes; composition + usage checks in CI
  • Evolution: additive changes only, @deprecated with dates, registry usage per client version, remove only when usage is zero; clients handle unknown enum values and union members (__typename default branch)

Rubric

Strong answers connect team boundaries to subgraphs and use real usage data before removing fields. Misses: versioned endpoints (/graphql/v2), breaking changes without usage checks.

Open in the app to mark lessons complete, take notes and track your progress.