In the previous lesson we called the schema a contract between the frontend and the backend. Now let's learn to write one. Picture the form you fill in when you register at a clinic: it lists every field (name, date of birth, phone), every field has an obvious answer format, and the required ones are marked with a red asterisk. A GraphQL schema is the same kind of form, only for the data of your API: it describes in advance which data types exist, what fields they consist of and which fields are required. The server will never return or accept anything that is not in the schema. In this lesson we will describe the schema of a blog — with users, posts and comments — and use it throughout the whole module.

SDL: the language a schema is written in

A schema is written in SDL (Schema Definition Language). It is not JavaScript and not JSON, but a small declarative language: you do not say how to get the data, you only describe what it will look like. The main building block is a type definition:

graphql
type Post {
  id: ID!
  title: String!
  body: String!
  published: Boolean!
}

Let's take the line title: String! apart:

  • title — the name of the field;
  • String — the type of the value (text);
  • ! — the value is never null; we will talk about this sign in detail a bit later.

There are naming conventions that almost everyone follows: types are written in PascalCase (Post, CreatePostInput), fields and arguments in camelCase (createdAt), enum values in SCREAMING_SNAKE_CASE (NEWEST). Comments start with # and are ignored by the server. The order of type definitions does not matter: you can use a type before it is defined.

Scalar types: the "atoms" of data

At the end of any query there must be concrete values: a number, a text, a yes/no. These "leaves" are called scalars. GraphQL has five built-in ones:

TypeWhat it holdsExample
Inta whole number (32-bit)42
Floata number with a fraction19.9 (a price in AZN)
StringUTF-8 text"Hello"
Booleana logical valuetrue
IDa unique identifier"42"

Three details that beginners often trip over:

  • ID is always a string in the response. Even if it is the number 42 in the database, the client receives "42". An identifier is for finding and comparing records, not for calculating: adding two IDs makes no sense.
  • Int is 32-bit. The maximum is 2,147,483,647 (about 2.1 billion). For bigger numbers (for example, identifiers from social networks) use String.
  • There is no date in the list. There is no built-in type for date and time. A date is usually sent as an ISO 8601 string, for example "2026-03-15T10:30:00Z" — that is what we will do for the createdAt field. Large projects declare their own scalar (scalar DateTime), for example with the graphql-scalars library.

Object types and the links between them

The main kind of type is the object type: a named set of fields. Let's describe a blog. It has three entities: a user (User), a post (Post) and a comment (Comment):

graphql
type User {
  id: ID!
  name: String!
  email: String!
  posts: [Post!]!
}

type Post {
  id: ID!
  title: String!
  body: String!
  published: Boolean!
  author: User!
  comments: [Comment!]!
  createdAt: String!
}

type Comment {
  id: ID!
  text: String!
  author: User!
  post: Post!
}