graphqlsdlschemaapitypesconverters

JSON to GraphQL Schema: Writing SDL Types from a JSON Sample

·8 min read·Converters

Describing JSON as a GraphQL type

GraphQL's schema is written in SDL (Schema Definition Language), and a frequent task is turning an existing JSON response into the SDL type that describes it. The mapping is straightforward, with one twist GraphQL adds that JSON doesn't have: an explicit, first-class nullability model. Get that right and the rest is mechanical. This complements JSON to TypeScript — same shape, different target.

The scalar mapping

Given this JSON:

json
{
  "id": "u_1",
  "name": "Ada",
  "age": 36,
  "rating": 4.5,
  "isActive": true
}

it maps to a GraphQL type using the five built-in scalars:

graphql
type User {
  id: ID!
  name: String!
  age: Int!
  rating: Float!
  isActive: Boolean!
}
JSON valueGraphQL scalar
identifier stringID
"text"String
36 (whole)Int
4.5 (decimal)Float
true / falseBoolean

GraphQL has only these five built-in scalars — there's no native Date, so ISO date strings map to String or a custom scalar like DateTime you define yourself.

The twist: nullability and the `!`

This is the concept JSON lacks. In GraphQL, a field is nullable by default, and a trailing ! means non-null (guaranteed present). So the mapping from JSON isn't just about type — it's about whether each field can be missing:

graphql
type User {
  id: ID!            # ! = always present, never null
  name: String!      # required
  nickname: String   # no ! = nullable, may be null
}

The judgement call: mark a field ! only if it's truly always present in every response. A single JSON sample can't prove that — if nickname is present in your example but sometimes absent, it must stay nullable (no !). Over-using ! is a common mistake, because a non-null field that later returns null becomes a hard query error.

Lists

A JSON array maps to a GraphQL list with square brackets — and lists have *two* nullability positions, the item and the list itself:

json
{ "tags": ["a", "b"], "scores": [90, 85] }
graphql
type User {
  tags: [String!]!     # non-null list of non-null strings
  scores: [Int!]       # nullable list of non-null ints
}

[String!]! reads as "the list is always present, and every item is non-null" — the most common and usually-correct choice for a required collection.

Nested objects become their own types

A nested JSON object becomes a separate GraphQL type referenced by field:

json
{ "id": "u_1", "address": { "city": "London", "zip": "EC1" } }
graphql
type User {
  id: ID!
  address: Address
}

type Address {
  city: String!
  zip: String!
}

GraphQL is a *graph*, so this is also where you'd add fields that don't exist in the JSON at all — a posts: [Post!]! connection the client can traverse, which is the whole point of GraphQL over a flat REST JSON response.

Input types for mutations

The type you generate describes output. To *accept* the same shape in a mutation, GraphQL requires a separate `input` type — output types and input types can't be interchanged:

graphql
input CreateUserInput {
  name: String!
  nickname: String
  tags: [String!]!
}

type Mutation {
  createUser(input: CreateUserInput!): User!
}

What JSON can't express

As with any schema generated from a sample, JSON gives you shape but not graph semantics — you add:

  • Relationships between types (the edges that make it a graph).
  • Correct nullability, which requires knowing your data, not one payload.
  • Enums where a string is really a fixed set (role: "admin" | "member" → a GraphQL enum).
  • Custom scalars for dates, emails, URLs.
  • Queries and mutations — the operations, which no data sample contains.

Treat generated SDL as a first draft of the types, then design the actual graph. For related targets see JSON to TypeScript and GraphQL vs REST with JSON; to lock the shape for validation, generate a JSON Schema.

Frequently asked questions

Map each JSON field to a GraphQL scalar — identifier strings to ID, text to String, whole numbers to Int, decimals to Float, booleans to Boolean — wrap arrays in [ ], and turn nested objects into their own type. Then decide nullability by adding ! to fields that are always present.

! means non-null — the field is guaranteed to be present and never null. Fields without ! are nullable by default. Only mark a field ! if it's always present in every response, since a non-null field that returns null causes a query error.

A JSON array becomes a GraphQL list in square brackets, e.g. [String!]!. Lists have two nullability positions — the items and the list itself — so [String!]! means a non-null list of non-null strings, the usual choice for a required collection.

GraphQL doesn't allow output object types to be used as arguments. To accept a shape in a mutation you define a matching input type, e.g. input CreateUserInput, and reference it in the mutation's arguments.

The graph itself — relationships between types, enums behind string fields, custom scalars for dates, correct nullability, and the queries and mutations. A JSON payload only shows one object's shape, so you design the operations and edges yourself.

Try JSONKit — JSON Developer Tools

Format, validate, convert and diff JSON. Instant, accurate and 100% private.