Free & open source — no account required

v1.2.5-PRICING-19
Web & Frontend • Engineering Documentation

JSON to Graphql Schema Converter

This technical guide provides an in-depth analysis of the json to graphql schema engine, best practices for implementation, and data security standards.

JSON to GraphQL Schema: Generating SDL Types from JSON Data

GraphQL's type system is stricter than TypeScript's — every field must be explicitly typed, non-null fields must be declared with !, and the distinction between nullable and required is enforced by the schema, not convention. Converting a JSON response to GraphQL SDL gives you the object type definitions you need to start a GraphQL API, migrate from REST, or prototype a schema before writing resolvers.

GraphQL SDL vs TypeScript: Key Differences

Before generating a schema, understand where GraphQL and TypeScript diverge:

// TypeScript interface — optional means "may be absent"
interface User {
  id:      string;           // required
  name:    string;           // required
  email?:  string;           // optional (undefined)
  avatar:  string | null;    // required, but nullable
}

# GraphQL SDL — ! means non-null (required)
type User {
  id:     String!      # non-null = required
  name:   String!      # non-null = required
  email:  String       # nullable = may be null or absent in resolver
  avatar: String       # nullable
}

# Key insight: in GraphQL, "nullable" and "optional" are the same concept.
# A field without ! can return null. There's no undefined.

JSON to SDL: Basic Object Type

// Input JSON (API response)
{
  "id": "usr_01HXYZ",
  "name": "Ada Lovelace",
  "email": "ada@example.com",
  "created_at": "2026-01-15T10:30:00Z",
  "subscription": {
    "plan": "pro",
    "expires_at": "2027-01-15T10:30:00Z",
    "seats": 5
  },
  "tags": ["engineering", "mathematics"]
}

# Generated GraphQL SDL
type Subscription {
  plan:       String!
  expires_at: String!
  seats:      Int!
}

type User {
  id:           String!
  name:         String!
  email:        String!
  created_at:   String!
  subscription: Subscription!
  tags:         [String!]!
}

type Query {
  user(id: String!): User
  users: [User!]!
}

Adding Input Types for Mutations

GraphQL uses separate input types for mutation arguments — you can't reuse object types directly:

# Object type — what resolvers return
type Product {
  id:          ID!
  sku:         String!
  name:        String!
  price_cents: Int!
  in_stock:    Boolean!
  created_at:  String!
}

# Input type for create — id and created_at are server-generated
input CreateProductInput {
  sku:         String!
  name:        String!
  price_cents: Int!
  in_stock:    Boolean
}

# Input type for update — everything optional except id
input UpdateProductInput {
  sku:         String
  name:        String
  price_cents: Int
  in_stock:    Boolean
}

type Mutation {
  createProduct(input: CreateProductInput!): Product!
  updateProduct(id: ID!, input: UpdateProductInput!): Product
  deleteProduct(id: ID!): Boolean!
}

Resolver Types in TypeScript

Once you have the SDL, generate TypeScript resolver types with graphql-codegen or write them manually:

// Manually typed resolvers (without codegen)
import { GraphQLResolveInfo } from 'graphql';

type Context = {
  db: Database;
  user: AuthUser | null;
};

// Match the shape of your SDL types
type ProductGQL = {
  id:          string;
  sku:         string;
  name:        string;
  price_cents: number;
  in_stock:    boolean;
  created_at:  string;
};

type Resolvers = {
  Query: {
    product: (_: unknown, args: { id: string }, ctx: Context) => Promise<ProductGQL | null>;
    products: (_: unknown, args: { page?: number; limit?: number }, ctx: Context) => Promise<ProductGQL[]>;
  };
  Mutation: {
    createProduct: (
      _: unknown,
      args: { input: { sku: string; name: string; price_cents: number; in_stock?: boolean } },
      ctx: Context
    ) => Promise<ProductGQL>;
  };
};

// Implementation
const resolvers: Resolvers = {
  Query: {
    product: async (_, { id }, { db }) => db.products.findById(id),
    products: async (_, { page = 1, limit = 20 }, { db }) => db.products.list({ page, limit }),
  },
  Mutation: {
    createProduct: async (_, { input }, { db, user }) => {
      if (!user) throw new Error('Unauthorized');
      return db.products.create(input);
    },
  },
};

Enums in GraphQL

# GraphQL enum — values are unquoted identifiers, conventionally SCREAMING_SNAKE_CASE
enum OrderStatus {
  PENDING
  PAID
  SHIPPED
  CANCELLED
}

type Order {
  id:     ID!
  status: OrderStatus!
  total:  Int!
}

# In TypeScript resolvers, GraphQL enums become string literals
type OrderStatusGQL = 'PENDING' | 'PAID' | 'SHIPPED' | 'CANCELLED';

// Map your database enum to GraphQL enum values
function toGQLStatus(dbStatus: 'pending' | 'paid' | 'shipped' | 'cancelled'): OrderStatusGQL {
  return dbStatus.toUpperCase() as OrderStatusGQL;
}

Nullable Fields: Being Explicit About What Can Fail

GraphQL's null handling is a design decision, not just a type annotation. A common pattern is to make top-level query fields nullable (so fetching a missing resource returns null, not an error), but object fields non-null:

type Query {
  # Returns null if user doesn't exist — not an error
  user(id: ID!): User

  # Returns null if product is out of catalog — expected scenario
  product(id: ID!): Product

  # Always returns a list (empty array, never null)
  products: [Product!]!
}

type User {
  # These fields always exist on a valid User object
  id:    ID!
  name:  String!
  email: String!

  # Profile is optional — user may not have filled it in
  profile: UserProfile
}

Common Pitfalls

  • Using String for IDs: GraphQL has a built-in ID scalar specifically for identifiers. It serializes identically to String but signals intent to the schema consumer and codegen tools. Always use ID! for primary keys.
  • Non-null lists vs non-null items: [String], [String!], [String]!, and [String!]! are four different types. [String!]! means the list is always present and never contains nulls — usually what you want for collections of domain objects.
  • Returning objects from mutations instead of booleans: Return the mutated object (createProduct: Product!) rather than a boolean or void. This lets clients update their cache with the server's canonical version and avoids a follow-up query.
  • Forgetting to handle N+1 queries in resolvers: If your Order type has a customer: User! field, a naive resolver fetches one user per order — N+1. Use DataLoader to batch user fetches into a single query.

FAQ

Q: Should I use code-first or schema-first GraphQL?
A: Schema-first (write SDL, then implement resolvers) keeps the API contract visible and language-agnostic. Code-first (generate SDL from code using Nexus or Pothos) is better for complex TypeScript-driven schemas where SDL duplication becomes painful. For teams with multiple consumers or a public API, schema-first is generally clearer.

Q: How do I handle pagination in the GraphQL schema?
A: Use the Relay Cursor Connection spec: type ProductConnection { edges: [ProductEdge!]! pageInfo: PageInfo! }. This is the standard that Apollo Client and Relay expect, and it handles forward/backward pagination cleanly.

Q: Can I use the same JSON-derived types for both my REST and GraphQL APIs?
A: Define the core domain types once (as TypeScript interfaces) and adapt them separately for REST responses and GraphQL resolvers. Don't let the GraphQL type system leak into your domain model — the nullable/non-null semantics are specific to GraphQL.

Q: How do I represent dates in GraphQL?
A: The built-in scalars don't include Date. Either use String with ISO 8601 format (simple, interoperable) or define a custom DateTime scalar with a spec that serializes to ISO 8601. Libraries like graphql-scalars provide common custom scalars with validation.

Developer FAQ

Is the processing local-only?

Absolutely. TypeMorph operates entirely within your browser's sandbox. We use Web Workers for high-performance computation without ever transmitting your JSON, SQL, or API data to a remote server.

Can I use this for enterprise projects?

Yes. The tool is designed for professional software engineers who require GDPR compliance and data privacy. It is trusted by developers at top-tier startups and financial institutions.