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
dataanderrorsside 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.
Feedback is a gift. Something to improve, a feature to add, or a bug? Tell me.
Runs entirely in your browser — network requests go only to the services named on this page.