Common GraphQL Schema Patterns for JSON-Based APIs

When wrapping an existing JSON API with a GraphQL layer — or designing a new one from scratch — certain patterns emerge repeatedly. Here are the most useful ones.

Pattern 1: Connection / Pagination

Relay-style connections are the standard way to paginate list fields. Instead of returning a raw array, wrap it with pagination metadata:

GraphQL SDL
type UserConnection {
  edges: [UserEdge!]!
  pageInfo: PageInfo!
  totalCount: Int!
}

type UserEdge {
  node: User!
  cursor: String!
}

type PageInfo {
  hasNextPage: Boolean!
  hasPreviousPage: Boolean!
  startCursor: String
  endCursor: String
}

type Query {
  users(first: Int!, after: String): UserConnection!
}

This pattern gives clients cursor-based pagination with metadata about whether more results exist — essential for infinite scroll and "load more" UIs.

Pattern 2: Result Wrapper for Error Handling

GraphQL's error mechanism works for system-level errors, but business-level errors (e.g., "user not found") are better modeled in the type system:

GraphQL SDL
union UserResult = User | NotFoundError | RateLimitError

type NotFoundError {
  message: String!
  resourceId: String!
}

type RateLimitError {
  message: String!
  retryAfter: Int!
}

type Query {
  user(id: ID!): UserResult!
}

Clients handle each case explicitly with fragments, making error handling type-safe and visible in the schema.

Pattern 3: Input/Output Type Separation

Never reuse an object type as both input and output. Create separate input types for mutations:

GraphQL SDL
input CreateUserInput {
  name: String!
  email: String
}

input UpdateUserInput {
  name: String
  email: String
}

Pattern 4: Custom Scalars for Domain Types

Elevate important domain concepts to custom scalars:

GraphQL SDL
scalar DateTime
scalar URL
scalar EmailAddress

type User {
  id: ID!
  email: EmailAddress!
  website: URL
  registeredAt: DateTime!
}

Custom scalars add semantic meaning and enable server-side validation at the type level rather than in resolver logic.

Applying These Patterns

Our JSON to GraphQL Converter generates the initial SDL types from your JSON samples. You can then apply these patterns on top — wrapping lists in connections, separating input/output types, and adding custom scalars — to build a production-grade schema.