Building Your First GraphQL Schema – Designing the Data Layer
Welcome back! Now that we’ve got your GraphQL environment set up and running, it’s time to dive deeper into the true heart of GraphQL—the schema. This is where things start to get really interesting because the schema is what shapes your API. Think of it like a blueprint. Just like a blueprint tells builders what a house should look like, the schema tells your API what types of data clients can request and how it should be structured.
In this tutorial, we’re going to explore GraphQL types, walk you through building your very first GraphQL schema, and introduce you to resolvers—the magic behind fetching your data. By the end of this, you’ll know exactly how to design and query your own data layers. Plus, we’ll keep it simple and relatable throughout the process.
So, roll up your sleeves, and let’s jump right into building our data layer!
Understanding GraphQL Types
In GraphQL, everything revolves around types. Types define the shape of the data that your API will expose. If you’ve ever worked with TypeScript or JSON, you’ll feel right at home here. Let’s start with the basic types you’ll use most often:
- String – Just like in any programming language, this represents a sequence of characters (text). For example:
"John Doe"
. - Int – This stands for an integer (a whole number). You’ll use this for things like ages, quantities, or other numerical data. For example:
25
. - Float – This is a number with a decimal point. It’s used for things like prices or measurements. For example:
19.99
. - Boolean – This represents a true or false value. It’s handy for toggling features or representing whether something is active. For example:
true
orfalse
.
Those are the most basic types. Now, let’s talk about something even cooler—custom types. GraphQL lets you define your own types to represent more complex data structures. This is where it gets powerful because you can design types that perfectly fit your API’s needs.
For instance, you might define a User type that represents a user in your system. That User type could have fields like a name (which would be a string), an age (which would be an integer), and a isActive field (which would be a Boolean to show if the user is currently active).
Let’s move from theory to practice by creating our first custom type!
Creating Your First Schema
Now that you know what types are, let’s start designing our first GraphQL schema. We’re going to create a simple API that allows clients to request a list of users. Our User type will have the following fields:
- id: A unique identifier for each user (an Int).
- name: The user’s name (a String).
- age: The user’s age (an Int).
- isActive: Whether the user is active or not (a Boolean).
To define this in GraphQL, we’ll add the type definition to our schema. Let’s go ahead and build the schema step by step.
- Open the
index.js
file where we wrote our first GraphQL query. - Update the
typeDefs
to include the User type:
const { ApolloServer, gql } = require('apollo-server');
// Define the GraphQL schema
const typeDefs = gql`
type User {
id: Int
name: String
age: Int
isActive: Boolean
}
type Query {
users: [User]
}
`;
// Define the resolvers
const resolvers = {
Query: {
users: () => [
{ id: 1, name: 'John Doe', age: 25, isActive: true },
{ id: 2, name: 'Jane Smith', age: 30, isActive: false },
],
},
};
// Create a new Apollo Server instance
const server = new ApolloServer({ typeDefs, resolvers });
// Start the server
server.listen().then(({ url }) => {
console.log(`Server ready at ${url}`);
});
What’s going on here?
- We’ve defined a custom type called User. This tells GraphQL what a User object looks like.
- We’ve added a new query type, users, which will return a list of User objects. The square brackets
[User]
mean that this query returns an array of User objects. - In the resolvers (we’ll get more into these in a second), we’ve added a static list of users that the query will return.
Resolvers Explained
So, we’ve created the User type and the users query. But where is the data coming from? How does GraphQL know what data to return when someone asks for a list of users?
This is where resolvers come in. Think of resolvers as the "workers" of your API. When someone makes a query, GraphQL uses resolvers to fetch the requested data. It’s like when you place an order at a restaurant. The menu (schema) tells you what you can order, and the kitchen (resolvers) is where the actual food (data) gets prepared and delivered to you.
Here’s what our resolvers do:
const resolvers = {
Query: {
users: () => [
{ id: 1, name: 'John Doe', age: 25, isActive: true },
{ id: 2, name: 'Jane Smith', age: 30, isActive: false },
],
},
};
We’ve defined a users resolver inside the Query object. This is a function that returns a static list of two users. In a real-world application, this data would likely come from a database or another API, but for now, we’re keeping it simple with hardcoded values.
Querying Data with GraphQL
Now that we have our schema and resolvers in place, let’s run the server and try querying our new users data.
- Start the server by running:
node index.js
-
Open your browser and go to
http://localhost:4000/
to access the GraphQL playground. -
In the playground, enter the following query:
{
users {
id
name
age
isActive
}
}
- Hit the “play” button, and you should see this response:
{
"data": {
"users": [
{
"id": 1,
"name": "John Doe",
"age": 25,
"isActive": true
},
{
"id": 2,
"name": "Jane Smith",
"age": 30,
"isActive": false
}
]
}
}
Congrats! You’ve just queried your first list of users with GraphQL. Notice how the query is simple and straightforward? You can ask for exactly the fields you need (in this case, id, name, age, and isActive), and GraphQL will give you just that—nothing more, nothing less.
The Role of Resolvers in Data Fetching
Now that you’ve seen how resolvers work, let’s talk a bit more about their role in data fetching.
Resolvers are responsible for getting the data for each field in your schema. In our example, the users resolver returns a static list of users. But in a real-world API, resolvers would typically fetch data from a database, an external API, or some other source.
Here’s a quick rundown of how data flows through a GraphQL API:
- Client makes a query – The client sends a request asking for specific data (in our case, a list of users).
- GraphQL validates the query – GraphQL checks the query against the schema to make sure the requested fields are valid.
- Resolvers fetch the data – For each field in the query, GraphQL calls the corresponding resolver. The resolvers do the heavy lifting of fetching or generating the data.
- GraphQL returns the response – Once all the resolvers have finished, GraphQL sends the data back to the client.
In our example, the users query calls the users resolver, which returns a static list of users. As we progress through these tutorials, we’ll start fetching real data from databases and external APIs.
Recap and Next Steps
In this tutorial, we’ve covered some big concepts:
- You learned about GraphQL types and how they define the structure of your data.
- You built your first custom GraphQL schema, defining a User type and a users query.
- You saw how resolvers fetch data when a query is made and how they connect your schema to the actual data layer.
- You successfully queried a list of users from your API using GraphQL.
That’s a lot to take in, but don’t worry if it doesn’t all click right away. The more you practice, the more it’ll start to make sense. And speaking of practice, try adding another type and query to your API! Maybe add a **
Post** type and a query to fetch a list of posts.
In the next tutorial, we’ll start fetching real data from a database. You’ll learn how to connect your GraphQL API to a database and start serving dynamic data. This is where your API will really start to shine, so stay tuned!
Until next time, keep experimenting, and have fun building your first GraphQL schema!