JSON to GraphQL SDL
Paste sample JSON, get GraphQL SDL types — nullability read from your samples, entirely on this device.
Non-null marks are read from the samples you paste: a field every sample carries, never null, gets ! — everything else stays nullable.
GraphQL has no map type, so an object whose keys are data comes back as a type listing exactly the keys you pasted. Shapes with no SDL spelling at all — mixed arrays, always-null fields — are noted with their path, never guessed.
Names the outermost type. A name GraphQL cannot spell is sanitized to its Name grammar and the change is noted.
Off by default. On, only a String field named id, _id or <Type>Id becomes ID — no other name is guessed.
Mixed arrays, empty objects, empty arrays and always-null fields fall back to this type, each one noted with its path.
The SDL type definitions appear here as you type.
Press ? for shortcuts
Do it offline:$ npx graphql-schema-linter <your-schema>.graphql
How to use it
- Paste one response, or drop a .json file on the left pane — the SDL rebuilds as you type.
- Separate a second and third payload with a blank line: a field missing from any one loses its exclamation mark.
- Name the root type, then choose where nullability comes from, whether an id becomes ID, and what stands in for a shape SDL cannot spell.
- Copy the types, save them as a .graphql file, or share a permalink that reopens the same payloads and switches.
Paste sample JSON and get GraphQL SDL: nested objects become named types, arrays carry the full exclamation-mark discipline. Treat what comes back as evidence, not a contract — every non-null mark comes from the samples you pasted and nothing else, a key none of them carried cannot be known, and GraphQL has no map type, so an arbitrary-key object arrives as a type holding the keys you showed it. Nothing is uploaded.
Examples
{"id":1,"name":"Ada","tags":["a"],"profile":{"city":"Paris"}}
{"id":2,"name":"Ben","tags":[],"profile":{"city":null}}type Profile {
city: String
}
type Root {
id: Int!
name: String!
tags: [String!]!
profile: Profile!
}
Two payloads, four verdicts. id and name are in both and never null, so both are marked required; city is null in the second, so Profile.city stays open. An empty tags array is not a null element, and only the second would drop the inner mark.
{"id":"o-1","total":9.5,"lines":[{"sku":"A1","qty":2}]}
{"id":"o-2","total":3,"lines":[{"sku":"B7","qty":1,"note":"gift"}]}type Line {
sku: String!
qty: Int!
note: String
}
type Order {
id: ID!
total: Float!
lines: [Line!]!
}
type Query {
root: Order!
}
Objects inside an array merge into one named type, so note — carried by the second line only — survives as an optional field rather than splitting Line in two. total is whole once and a fraction once, and the wider wins. The ID promotion and the entry point are switches, off unless you ask.
{"labels":{"en-US":"Save","fr-FR":"Enregistrer"}}type Labels {
en_US: String!
fr_FR: String!
}
type Root {
labels: Labels!
}
The edge case worth seeing early: an object whose keys are data, not a schema. It comes back as a type listing the two locales you pasted, hyphens rewritten because the Name grammar has no room for them. Replace it before anyone builds on it.
{"mixed":["a",1],"nickname":null}scalar JSON
type Root {
mixed: [JSON!]!
nickname: JSON
}
Two shapes SDL has no words for, one escape hatch. An array holding a string and a number would need a union of scalars, and a field never seen as anything but null has no type. The scalar is declared once, only when referenced, and your server supplies it.
Every example on this page is executed against the same engine that powers the tool, on every build.
Gotchas worth knowing
[String!]! and [String] promise different things
One array field has four spellings, and each says something distinct: whether the list can be null, and whether an element can. Both marks are read separately — the list from the key, the element from the values.
Names are rewritten, and __ is not yours
A GraphQL name is a letter or underscore followed by letters, digits and underscores, so kebab-case and dotted keys are rewritten and a leading digit takes an underscore. Two leading underscores are reserved for introspection and are dropped. Each rename names the key it came from.
Int stops at 2147483647
The Int scalar is a signed 32-bit integer, narrower than the numbers JSON lets you write. A literal past that range raises a warning naming the path, and the field stays Int: your samples said whole number, and widening them quietly would be a decision taken behind your back.
A union may only hold object types
That one rule decides most of what cannot be expressed. An array of two scalars, or a field that is a string here and a number there, has no legal spelling — the escape-hatch scalar stands in, with a note pointing at the position.
Common errors
Sample 2: Unexpected '}' at line 1, column 8 — probable cause: trailing comma after the last property.- Every payload has to be strict JSON before any are merged. The prefix names the failing one and appears only when more than one is open; Jump to error puts the cursor on the character.
Input is larger than the 10 MB limit for this tool.- Past the combined ceiling the run is refused whole, never quietly trimmed — a half-read payload produces marks that lie. Three representative payloads decide as much as three thousand rows.
/views carried an integer outside GraphQL's 32-bit Int range — use Float or a custom scalar for it.- A warning, not a refusal. The path is a pointer into the payload, with [] standing for any array position. Snowflake ids and epoch microseconds land here most often, and are happier as Strings.
/nickname was null in every sample — GraphQL has no null type, so it is left as JSON- Nothing was learned about that key beyond its name. Paste a payload where it holds a real value and the type appears.
/mixed/[] held more than one type — a GraphQL union may contain object types only, so it is left as JSON- Notes like this sit under the output without blocking it. Switch the fallback to String if the values are printable, or restructure the payload so the position holds one type.
FAQ
How do I get a map type for an object with arbitrary keys?
You write it yourself, because the language has none. The usual shape is a list of pair objects with a key field and a value field, or a custom scalar your server serialises. Either way, the generated type listing today's keys is a placeholder, not a schema to publish.
Why is a field marked required when I know it can be missing?
Because it was never missing in what you pasted. Add a payload where the key is absent, or switch the source of nullability so nothing is marked and you decide by hand.
Can I hand this straight to a GraphQL server?
As a starting point. It is not compiler-verified in this environment — no parser runs here — and it carries no resolvers, arguments, interfaces, input types or directives. Run it through your own toolchain first.
Keyboard shortcuts
- ⌘/Ctrl + Enter
- Run the types again, which is how a large paste starts.
- ⌘/Ctrl + S
- Save the types as a .graphql file, not the page.
- ⌘/Ctrl + C
- With nothing selected, copies the whole schema.
- ⌘/Ctrl + Shift + C
- Take the schema 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.