JSON to GraphQL Schema: Best Practices for API Design

Converting JSON to a GraphQL schema is more than a mechanical transformation. How you name types, handle nullability, and structure relationships determines whether your API is a joy to use or a source of confusion.

1. Use PascalCase for Type Names

GraphQL conventions dictate that type names use PascalCase. When converting JSON keys like user_profile or billingAddress, convert them to UserProfile and BillingAddress. Our JSON to GraphQL converter handles this automatically.

2. Be Intentional with Nullability

In JSON, every field can technically be absent or null. In GraphQL, you have a choice:

  • Non-null (!) — Use for fields that must always have a value (IDs, names, required attributes).
  • Nullable — Use for optional fields, especially in input types where the client may not provide a value.

A good rule: make fields non-null by default in your output types, and nullable in your input types. This gives consumers predictable responses while keeping mutations flexible.

3. Handle Nested Objects as Separate Types

When your JSON has nested objects, create a named GraphQL type for each level instead of inlining fields:

GraphQL SDL
type Address {
  street: String
  city: String
}

type Order {
  id: ID!
  shippingAddress: Address
  billingAddress: Address
}

4. Map JSON Arrays Correctly

JSON arrays map to GraphQL list types ([Type]). If the array contains objects, wrap them in a named type. Consider whether the list itself should be non-null ([Type!]!) or nullable ([Type]).

5. Choose Sensible Scalar Types

JSON has fewer types than GraphQL. Here's the standard mapping:

  • stringString
  • number (integer) → Int
  • number (float) → Float
  • booleanBoolean
  • null → nullable field
  • object → named type
  • array[Type]

6. Add a Root Query Type

Every GraphQL schema needs a root Query type. When converting a single JSON object, wrap it in a query field:

GraphQL SDL
type Query {
  data: RootType
}

Many tools (including ours) let you customize this wrapper name — use something descriptive like getUser or fetchConfig.