How to Pass JSON Object in GraphQL

One of the most common questions when working with GraphQL is: how do I pass a JSON object as an argument? Unlike REST, where you can send arbitrary JSON bodies, GraphQL requires you to define explicit input types for complex arguments.

Option 1: Use Input Types (Recommended)

The idiomatic GraphQL approach is to define an input type that mirrors your JSON structure. Input types are like object types but used specifically for arguments.

GraphQL SDL
input UserProfileInput {
  displayName: String!
  bio: String
  avatarUrl: String
  preferences: JSON
}

type Mutation {
  updateProfile(input: UserProfileInput!): User
}

Then call it from your client:

GraphQL
mutation {
  updateProfile(input: {
    displayName: "Ada Lovelace"
    bio: "First programmer"
    preferences: { theme: "dark", lang: "en" }
  }) {
    id
    displayName
  }
}

Option 2: Custom JSON Scalar

When your JSON structure is dynamic or unknown at schema design time, use a custom JSON scalar. This accepts any valid JSON value. Libraries like graphql-type-json (Node.js) or graphql-java-extended-scalars provide ready-made implementations.

GraphQL SDL
scalar JSON

type Mutation {
  submitAnalytics(data: JSON!): Boolean
}

The server-side implementation (JavaScript example):

JavaScript
import { GraphQLJSON } from 'graphql-type-json';

const resolvers = {
  JSON: GraphQLJSON,
  Mutation: {
    submitAnalytics: (_, { data }) => {
      console.log('Received:', data.event, data.properties);
      return true;
    },
  },
};

Option 3: Stringify and Parse

A simple but less elegant approach: pass the JSON as a String argument and parse it on the server. This works but sacrifices type safety and validation.

GraphQL
mutation {
  saveConfig(jsonString: "{\"theme\": \"dark\", \"lang\": \"en\"}")
}

Which Approach Should You Use?

  • Input types — Best for structured, known data. Provides full type safety, validation, and IDE autocomplete.
  • JSON scalar — Best for dynamic or semi-structured data where the shape isn't known in advance.
  • Stringified JSON — Quick workaround but loses all GraphQL's type-safety benefits.

Nested Input Objects

Input types can be nested just like object types:

GraphQL SDL
input AddressInput {
  street: String!
  city: String!
  zipCode: String
  country: String!
}

input CreateUserInput {
  name: String!
  email: String!
  address: AddressInput!
  tags: [String!]
}

This maps naturally to JSON and lets you pass deeply nested structures while maintaining type safety at every level.