Skip to content

GraphQL SDL (Schema Definition Language) — Complete Syntax Guide

DodaTech Updated 2026-06-28 6 min read

In this tutorial, you will learn about Graphql SDL (Schema Definition Language). We cover key concepts, practical examples, and best practices to help you master this topic.

GraphQL Schema Definition Language (SDL) is the syntax used to describe the types, fields, and relationships in a GraphQL API, serving as the contract between client and server.

What You'll Learn

You will learn SDL syntax for defining object types, scalars, enums, interfaces, unions, and input types, along with field modifiers, directives, and schema organization patterns.

Why SDL Matters

The schema is the single source of truth for every GraphQL API. A well-written SDL schema communicates intent, enables tooling (autocomplete, validation, Code Generation), and prevents ambiguity between frontend and backend teams. DodaTech's Durga Antivirus Pro uses SDL to define types for Device, Threat, User, and Scan, and the schema is checked into version control as the contract that both the React dashboard team and the Go backend team agree on.

flowchart TB
    A["SDL Schema"] --> B["Object Types\n(type Device { ... })"]
    A --> C["Scalars\n(scalar DateTime)"]
    A --> D["Enums\n(enum Severity { LOW })"]
    A --> E["Interfaces\n(interface Node { ... })"]
    A --> F["Unions\n(union Result = A | B)"]
    A --> G["Input Types\n(input Input { ... })"]
    A --> H["Directives\n(@deprecated, @skip)"]
    style A fill:#dbeafe,stroke:#2563eb
    style B fill:#fef3c7,stroke:#d97706
    style C fill:#fef3c7,stroke:#d97706
    style D fill:#fef3c7,stroke:#d97706
    style E fill:#fef3c7,stroke:#d97706
    style F fill:#fef3c7,stroke:#d97706
    style G fill:#fef3c7,stroke:#d97706
    style H fill:#fef3c7,stroke:#d97706
â„šī¸ Info

Prerequisites: Basic GraphQL concepts. Familiarity with JSON and type systems is helpful.

SDL Syntax Basics

SDL is a human-readable syntax for defining GraphQL types:

# This is a comment in SDL
type Device {
  id: ID!
  name: String!
  os: String!
  version: String
  lastScan: DateTime
  user: User!
  threats: [Threat!]!
}

Each field has a name, a type, and optional modifiers. The ! means non-nullable. [ ] denotes a list.

Object Type Definition

Object types represent entities in your API:

type User {
  id: ID!
  email: String!
  displayName: String!
  role: UserRole!
  devices: [Device!]!
  createdAt: DateTime!
}

type Device {
  id: ID!
  name: String!
  os: String!
  version: String
  user: User!
  threats: [Threat!]!
  scans: [Scan!]!
}

Naming convention: types are PascalCase, fields are camelCase. Object types can reference each other, forming the relationship graph.

The Query and Mutation Types

These are special root types that define API entry points:

type Query {
  devices: [Device!]!
  device(id: ID!): Device
  threats(severity: Severity): [Threat!]!
  user(id: ID!): User
}

type Mutation {
  createDevice(input: CreateDeviceInput!): Device!
  updateDevice(id: ID!, input: UpdateDeviceInput!): Device!
  deleteDevice(id: ID!): DeleteResponse!
}
# Example query using the schema
query {
  device(id: "dev-001") {
    name
    os
    user {
      displayName
      email
    }
  }
}

# Expected response
{
  "data": {
    "device": {
      "name": "Office-PC",
      "os": "Windows 11",
      "user": {
        "displayName": "Alice Smith",
        "email": "alice@dodatech.com"
      }
    }
  }
}

Field Modifiers

Modifiers control nullability and cardinality:

type Example {
  basic: String              # Nullable string
  required: String!          # Non-null string
  list: [String]             # Nullable list of nullable strings
  nonNullItems: [String!]    # Nullable list of non-null strings
  nonNullList: [String]!     # Non-null list of nullable strings
  strict: [String!]!         # Non-null list of non-null strings
}

Best practice: use [String!]! for most list fields — the list always exists (may be empty) and every item is guaranteed valid.

Directives

Directives add metadata and behavior to schema elements:

type Device {
  id: ID!
  name: String!
  os: String!
  version: String @deprecated(reason: "Use 'osVersion' instead")
  osVersion: String
  user: User!
}

# Query directives
query GetDevices($includeScans: Boolean!) {
  devices {
    id
    name
    scans @include(if: $includeScans) {
      id
      status
    }
  }
}

Built-in directives: @deprecated, @skip, @include, @specifiedBy. Custom directives enable features like authentication (@auth) and formatting.

Common Mistakes

1. Using Reserved Names for Types

Query, Mutation, Subscription, String, Int, Float, Boolean, and ID are reserved. You cannot create a type named Query (that IS the root query type).

2. Forgetting Non-Null Modifiers on Key Fields

Every type should have a non-null id: ID! field. Nullable IDs make cache normalization impossible in Apollo Client.

3. Inconsistent Naming Conventions

Mixing snake_case and camelCase confuses clients. GraphQL convention is camelCase for fields and PascalCase for types.

4. Not Using Description Strings

Without descriptions (" " string literals in SDL), GraphiQL and generated docs show no guidance. Document every type and field:

"Represents a device running Durga Antivirus Pro"
type Device {
  "Unique device identifier"
  id: ID!
  "Human-readable device name"
  name: String!
}

5. Circular Reference Without Nullable Fields

If User has devices: [Device!]! and Device has user: User!, you must ensure at least one side can be null to avoid an impossible-to-resolve chain.

Practice Questions

  1. What does the ! symbol mean in SDL?
  2. What is the difference between type and input in SDL?
  3. What are the five built-in scalar types in GraphQL?
  4. How do you add descriptions to types and fields?
  5. What is the purpose of the @deprecated directive?

Answers:

  1. Non-nullable — the field will always return a value and cannot be null. If the resolver returns null, GraphQL propagates the null upward.
  2. type defines output objects with resolvers. input defines argument structures without resolvers (used for mutation arguments).
  3. String, Int, Float, Boolean, ID. Custom scalars (like DateTime) extend this set.
  4. Use string literals before the type or field definition: "Description here" on the line above. These appear in GraphiQL and generated documentation.
  5. @deprecated(reason: "...") marks a field as deprecated in the schema. Tools display the reason and suggest alternatives. Clients should migrate away from deprecated fields.

Challenge: Write an SDL schema for DodaTech's antivirus alert system. Include types for Alert (id, message, severity, device, createdAt), User (id, email, alerts, role), Device (id, name, os, user, alerts), an AlertSeverity enum, input types for creating and updating alerts, and root Query/Mutation types with pagination support.

FAQ

Can I import types from other SDL files?

GraphQL SDL does not have a built-in import mechanism. Use code-level merging (apollo-server's mergeTypeDefs or GraphQL Tools' loadFilesSync) to combine multiple .graphql files at build time.

What is the difference between SDL and IDL?

SDL (Schema Definition Language) is the GraphQL community name. IDL (Interface Definition Language) is the formal term from the GraphQL specification. They refer to the same syntax.

How do I organize SDL files in a large project?

Split by domain: user.graphql, device.graphql, threat.graphql. Use GraphQL Tools' mergeTypeDefs to combine them. For federated services, each subgraph publishes its own SDL.

Can I write SDL in YAML or JSON instead?

No — SDL is a custom syntax. However, you can generate SDL from JavaScript template literals (gql tag) or from JSON introspection results using utilities like graphql-cli.

What happens if two types have the same field name?

GraphQL allows this — each type's fields are independent. However, if you merge schemas with conflicting types, mergeTypeDefs will warn about duplicate type definitions.

Mini Project

Create an SDL file for a complete Durga Antivirus Pro API. Include types for User, Device, Threat, Scan, Alert, and Subscription. Add input types for all mutations, enums for severity and device status, and descriptions on every type and field.

What's Next

Topic Description
Custom Scalars Extending the scalar type system
Enums in Depth Defining enum values and use cases
Types & Schema Design Schema design best practices
âŦ… Types & Schema Design
➡ Custom Scalars

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro