GraphQL Input Types

Input types let you group multiple arguments together into a single named object. Instead of passing five separate arguments to a mutation, you define an input type with those five fields and pass one structured object. This keeps mutations clean and makes complex argument lists manageable.

The Problem Input Types Solve

Without input types, a mutation to create a user might have a long, messy argument list that is hard to read and validate.

  Without input type — messy argument list:
  ──────────────────────────────────────────
  type Mutation {
    createUser(
      firstName: String!
      lastName:  String!
      email:     String!
      password:  String!
      phone:     String
      role:      UserRole!
    ): User!
  }

  With input type — clean and grouped:
  ─────────────────────────────────────
  input CreateUserInput {
    firstName: String!
    lastName:  String!
    email:     String!
    password:  String!
    phone:     String
    role:      UserRole!
  }

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

Defining an Input Type

Input types use the keyword input instead of type. They look similar to object types but serve a different purpose — input types flow from client to server, while object types flow from server to client.

  input BookingInput {
    hotelId:    ID!
    checkIn:    String!
    checkOut:   String!
    guests:     Int!
    roomType:   RoomType!
  }

  type Mutation {
    bookRoom(booking: BookingInput!): Reservation!
  }

Using an Input Type in a Mutation

  Query (mutation):
  ──────────────────
  mutation CreateBooking {
    bookRoom(booking: {
      hotelId:  "h42",
      checkIn:  "2025-06-01",
      checkOut: "2025-06-05",
      guests:   2,
      roomType: DELUXE
    }) {
      id
      confirmationCode
      totalPrice
    }
  }

Input Types Can Be Nested

Input types can contain other input types, allowing you to build structured, hierarchical input objects that match complex data models.

  input AddressInput {
    street:  String!
    city:    String!
    country: String!
  }

  input RegisterInput {
    name:     String!
    email:    String!
    password: String!
    address:  AddressInput!   ← nested input type
  }

  type Mutation {
    register(data: RegisterInput!): User!
  }

  Mutation:
  ──────────
  mutation {
    register(data: {
      name:     "Priya",
      email:    "priya@example.com",
      password: "securepass",
      address: {
        street:  "12 Oak Street",
        city:    "Mumbai",
        country: "India"
      }
    }) {
      id
      name
    }
  }

Input Types vs Object Types — Key Difference

  input Type                   type (Object Type)
  ──────────────               ──────────────────
  Used as argument             Used as return value
  Client → Server              Server → Client
  Cannot have resolvers        Can have resolvers
  Cannot have arguments        Fields can have arguments
  on its fields                on their fields

Default Values in Input Types

Fields in an input type can have default values. If the client does not provide the field, the server uses the default.

  input PaginationInput {
    page:    Int = 1
    perPage: Int = 20
  }

  type Query {
    products(pagination: PaginationInput): [Product]
  }

  Query without pagination — defaults apply:
  ───────────────────────────────────────────
  { products { name } }   → page 1, 20 per page

  Query with pagination — overrides defaults:
  ────────────────────────────────────────────
  { products(pagination: { page: 3, perPage: 5 }) { name } }

Leave a Comment