GraphQL Schema Visualizer
See your GraphQL schema as an interactive type graph — fields, relations and reachability from Query — from pasted SDL or introspection JSON.
Renders a schema you paste — this page never contacts a GraphQL endpoint.
Getting the JSON from your own endpoint
Run the standard introspection query against your endpoint yourself, then paste the response here. This page has no URL field and sends nothing.
curl -s -X POST -H 'Content-Type: application/json' -d '{"query":"{ __schema { queryType { name } } }"}' https://api.example.com/graphqlquery IntrospectionQuery {
__schema {
queryType { name }
mutationType { name }
subscriptionType { name }
types { ...FullType }
}
}
fragment FullType on __Type {
kind
name
description
fields(includeDeprecated: true) {
name
description
args { ...InputValue }
type { ...TypeRef }
isDeprecated
deprecationReason
}
inputFields { ...InputValue }
interfaces { ...TypeRef }
enumValues(includeDeprecated: true) {
name
description
isDeprecated
deprecationReason
}
possibleTypes { ...TypeRef }
}
fragment InputValue on __InputValue {
name
description
type { ...TypeRef }
defaultValue
}
fragment TypeRef on __Type {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType { kind name }
}
}
}
}
}
}
}
The type graph appears here: one box per type, one arrow per field, laid out left to right from the Query root.
Paste a schema on the left, or load the example to explore one.
Arrow keys move, Enter selects, F focuses the graph on the selected type.
- Types appear here once a schema renders.
Select a type in the graph or the list to see its fields here.
Do it offline:$ curl -s -X POST -H 'Content-Type: application/json' -d '{"query":"{ __schema { queryType { name } } }"}' https://api.example.com/graphql
How to use it
- Paste SDL or introspection JSON on the left — small inputs render as you type, and Auto-detect reads a leading brace as JSON.
- Need the JSON? Copy the printed introspection query, run it against your own endpoint, and paste the response — the bare and the data-wrapped forms both work.
- Click any box, or walk the type list with the arrow keys, to open a type in the rail; Focus graph here re-roots the layout at that type.
- Adjust depth and the scalar toggle to size the neighborhood, then copy the Mermaid text or download the graph as an SVG file.
See a GraphQL schema as a type graph: one box per type, one arrow per field, laid out left to right from the Query root. Paste SDL or the JSON an introspection query returns — this page never contacts a GraphQL endpoint. Click a type for its fields, refocus the graph anywhere, and find the types nothing reaches.
Examples
type Query {
user(id: ID!): User
}
type User {
id: ID!
name: String!
posts: [Post!]!
}
type Post {
id: ID!
author: User!
}{
"types": 5,
"fields": 6,
"edges": 7,
"query": "Query",
"orphans": []
}The extraction digest for a three-type blog schema. Five types, not three: ID and String are drawn once something refers to them, and a built-in scalar nothing references is never invented. The seventh edge is the id argument on user — arguments count.
{
"data": {
"__schema": {
"queryType": { "name": "Query" },
"mutationType": null,
"subscriptionType": null,
"types": [
{ "kind": "OBJECT", "name": "Query", "description": null, "fields": [{ "name": "user", "description": null, "args": [{ "name": "id", "description": null, "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "ID", "ofType": null } }, "defaultValue": null }], "type": { "kind": "OBJECT", "name": "User", "ofType": null }, "isDeprecated": false, "deprecationReason": null }], "inputFields": null, "interfaces": [], "enumValues": null, "possibleTypes": null },
{ "kind": "OBJECT", "name": "User", "description": null, "fields": [{ "name": "id", "description": null, "args": [], "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "ID", "ofType": null } }, "isDeprecated": false, "deprecationReason": null }, { "name": "name", "description": null, "args": [], "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } }, "isDeprecated": false, "deprecationReason": null }, { "name": "posts", "description": null, "args": [], "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "LIST", "name": null, "ofType": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "OBJECT", "name": "Post", "ofType": null } } } }, "isDeprecated": false, "deprecationReason": null }], "inputFields": null, "interfaces": [], "enumValues": null, "possibleTypes": null },
{ "kind": "OBJECT", "name": "Post", "description": null, "fields": [{ "name": "id", "description": null, "args": [], "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", "name": "ID", "ofType": null } }, "isDeprecated": false, "deprecationReason": null }, { "name": "author", "description": null, "args": [], "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "OBJECT", "name": "User", "ofType": null } }, "isDeprecated": false, "deprecationReason": null }], "inputFields": null, "interfaces": [], "enumValues": null, "possibleTypes": null },
{ "kind": "SCALAR", "name": "ID", "description": null, "fields": null, "inputFields": null, "interfaces": null, "enumValues": null, "possibleTypes": null },
{ "kind": "SCALAR", "name": "String", "description": null, "fields": null, "inputFields": null, "interfaces": null, "enumValues": null, "possibleTypes": null }
]
}
}
}classDiagram
class Query {
user: User
}
class User {
id: ID!
name: String!
posts: [Post!]!
}
class Post {
id: ID!
author: User!
}
Query --> User : user
User --> Post : posts
Post --> User : author
The same blog schema as example 1, arriving as an introspection result this time, printed as the Mermaid export. Both notations extract the identical graph — the test suite holds this page to that equality, byte for byte on this very text.
type Query {
ping: String
}
type Legacy {
note: String
}["Legacy"]The unreachable set, the graph's sharpest teaching artifact. Legacy is declared and perfectly valid — but no field path from Query reaches it, so it ships dead weight. In the tool it wears an unreachable badge and sits apart until you focus it.
Every example on this page is executed against the same engine that powers the tool, on every build.
Gotchas worth knowing
Both introspection envelopes are accepted
Servers answer with {"data": {"__schema": …}}; tooling often stores just {"__schema": …}. Paste either — the normalizer looks in both places, and the shape refusal names the two forms rather than guessing at half-pasted JSON.
Deprecated fields stay visible, struck through
A field carrying @deprecated — or isDeprecated in introspection — is drawn with a strikethrough in its box and a badge plus the reason in the rail. Hiding it would misstate the schema: deprecated fields still resolve.
Implements and union edges are their own strokes
A dashed edge means implements — including an interface implementing another interface — and a dash-dot edge ties a union to each member. Field arrows stay solid, argument links dotted, so relation kinds survive any color scheme.
Past 750 types the graph degrades honestly
Parsing stops at 2000 types and 20000 fields. Above 750 types a full graph would be wallpaper, so the tool draws only the focus neighborhood, says so in a banner, and the type list keeps every type reachable by keyboard.
Common errors
Expected a field definition or "}", found end of input.- The SDL parser's own verdict, passed through untouched with line and column and a caret frame — here an unclosed type block. Jump to error puts the cursor on the spot.
Introspection input is not valid JSON — a truncated response or a trailing comma is the usual cause.- Introspection dumps are big, and a copy that missed the final brace is the classic failure. Recapture the response, or run it through a JSON validator first.
No "__schema" object found — paste the whole introspection result, either {"__schema": …} or the HTTP body {"data": {"__schema": …}}.- Valid JSON, wrong shape — usually an errors-only response or a fragment of the result. The message names the two shapes this tool reads, so you can check which one your capture actually is.
Input is larger than the 5 MB limit for this tool.- The refusal is whole, never a silent trim — half a schema would draw confident nonsense. Introspection JSON balloons fastest; capturing without descriptions shrinks it dramatically.
FAQ
Can this tool introspect my endpoint for me?
No, deliberately. There is no URL field and the page sends nothing — paste is the only input. The curl line below and the on-page introspection query are for you to run against your own endpoint; what your server returns, you paste here.
Why is a type marked unreachable when my schema uses it?
Unreachable means no field, argument, union or implements path from Query, Mutation or Subscription arrives at it — and implements counts both ways, so a type reached only as the implementor of a reachable interface stays reachable, like a union member. Input types used only in arguments do count as reachable. A type only a custom directive mentions does not — directives are ignored here, a recorded limit.
What is the Mermaid export for?
Documentation. It is classDiagram source text — types as classes, fields as members, relations as arrows — for readmes and design docs. It is emitted as text only: no diagram runtime ships on this page, and complex field types may need touching up for strict renderers.
Keyboard shortcuts
- ⌘/Ctrl + Enter
- Render or re-render the graph.
- ⌘/Ctrl + S
- Download the graph as an SVG file.
- ⌘/Ctrl + C
- With nothing selected, copies the Mermaid text.
- ⌘/Ctrl + Shift + C
- Copy the Mermaid text even while a selection is live.
- t
- Flip the theme, unless a text field has focus.
- ? or ⌘/Ctrl + /
- Bring up the list of shortcuts.
- Esc
- Dismiss the topmost overlay.