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:
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 nevernull; 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:
| Type | What it holds | Example |
|---|---|---|
Int | a whole number (32-bit) | 42 |
Float | a number with a fraction | 19.9 (a price in AZN) |
String | UTF-8 text | "Hello" |
Boolean | a logical value | true |
ID | a unique identifier | "42" |
Three details that beginners often trip over:
IDis always a string in the response. Even if it is the number42in the database, the client receives"42". An identifier is for finding and comparing records, not for calculating: adding twoIDs makes no sense.Intis 32-bit. The maximum is 2,147,483,647 (about 2.1 billion). For bigger numbers (for example, identifiers from social networks) useString.- 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 thecreatedAtfield. 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):
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!
}