Chris Collins

Tools · dev

GraphQL playground

A GraphQL server in your browser — write a schema in SDL and resolvers in JavaScript over sample data (a blog, a library, a shop), query it with hints from the schema, and watch every resolver call and database query on a timeline, which is how the N+1 problem shows itself and DataLoader fixes it. Also sends queries to real endpoints.

Added

How to use it

This runs graphql-js, the reference implementation of GraphQL, in this page. You edit the schema (SDL) and the resolvers (JavaScript), and queries run against a small in-memory database that logs every query it answers. Each resolver is wrapped so the page can show what was called, when, how often, and which database queries it caused.

The point I most wanted to make visible is the N+1 problem: GraphQL resolves one field at a time, so posts { author { name } } calls Post.author once per post, and a naive resolver fetches each author separately — one query for the posts and N for their authors. The Trace tab counts calls and queries per field and flags it; the labs then fix it with a DataLoader, first the full one and then one you can read (and edit) in twenty lines.

How to use it

Playground. Pick a sample schema — a blog (users, posts, comments), a library (books, magazines, loans) or a shop (products, orders, a cart) — or an example query, and press Run (Ctrl/⌘+Enter runs the operation the cursor is in).

  • Hints: under the editor, the fields (with their types and required arguments), arguments or fragment types that fit where the cursor is. Click one, or press Tab to take the first.

  • Schema panel: every type, grouped by kind. Click a type to see its fields, arguments, interfaces and possible types; click a field to insert it. Deprecated fields are struck through.

  • Checks as you type: syntax and validation errors with line and column (click to jump), and a static estimate of the query’s depth and complexity.

  • Result is the response JSON, with data and errors side by side when a field fails.

  • Trace: resolver calls and database queries per field, an N+1 warning when a field issues one query per call, a timeline (resolvers in blue, queries in orange) and the call tree.

  • DB: every query in order, with its rows, time and the resolver that issued it (or loader batch when a DataLoader sent it).

  • Cost: depth, field count and complexity — one point a field, with list fields multiplying their subtree by first/last/limit, or by an assumed size — against limits you set under ⚙ Settings. A query over the limits is refused before it runs, as a server enforcing them would. The database latency (a few milliseconds a query by default) is there too.

  • Reset data undoes your mutations. curl copies the request as a curl command.

Schema & resolvers. Edit the SDL and the resolvers and press Apply. Resolvers get (parent, args, ctx, info); ctx.db has find, findMany, where, all, groupBy, count, search, insert, update and remove, each one logged query; ctx.loader('User') and ctx.loader('Comment', 'postId') are per-request DataLoaders by id and by a foreign key. DataLoader, connection(array, args) (Relay connections) and GraphQLError are in scope, and an optional context(ctx) builds the context for each request. For the blog there are three sets of resolvers to switch between: naive, with DataLoader, and with a hand-written TinyLoader. Your edits stay in this browser; Restore the original brings the sample back.

Labs walk through GraphQL one idea at a time, each step loading a query (and the right resolvers) and running it: a first query; operations and variables; aliases and fragments; @include, @skip and @deprecated; interfaces and unions; mutations (and why they run in order); the N+1 problem; fixing it with DataLoader; writing your own DataLoader; Relay cursor pagination; introspection; depth and complexity limits; errors, partial results and how a null in a non-null field bubbles up.

History keeps the queries you ran, in this browser only.

Remote endpoint. Switch the playground to Remote endpoint, enter a URL and press Load schema — an introspection query — and the hints, checks and schema panel use that server’s schema. Headers (an Authorization token, an API key) go under Headers, one Name: value a line. The examples include public endpoints that allow cross-origin requests (countries, Rick and Morty, SWAPI, AniList). Servers that don’t send CORS headers can’t be reached from a web page; use the HTTP client's notes on CORS, or curl.

Works in any current browser. Related: HTTP request client (REST, and GraphQL as plain POSTs), JSON workbench, Postgres playground (where the joins a DataLoader saves you would really run), Protobuf and gRPC playground.

Linking to an example

Share link copies a link with the sample schema, query and variables (and your edited schema and resolvers, if any). By hand: ?example= with an example’s id opens it and runs it, e.g. ?example=posts-authors, ?example=library:items or ?example=shop:orders; ?lab= opens a lab at its first step (&step=2 for another), e.g. ?lab=n-plus-one, ?lab=dataloader, ?lab=tiny-loader; ?q= and ?v= set the query and variables, ?ds= the sample (blog, library, shop).

Privacy

graphql-js 16.14.2 (MIT, about 45 KB compressed) is loaded from cdn.jsdelivr.net when the page opens. The in-page server, its data, your schema, resolvers and queries stay in this browser; nothing is sent anywhere. Only in Remote endpoint mode does the page send the query, variables and headers you entered — straight from your browser to the URL you typed, not through this site. Headers are kept only while the page is open unless you tick Remember headers, and they never go into the history or share links.

Runs entirely in your browser — network requests go only to the services named on this page.