In this section, we will cover the fundamentals of GraphQL schemas. Understanding schemas is crucial as they define the structure of your GraphQL API, including the types of data you can query and the operations you can perform.
What is a GraphQL Schema?
A GraphQL schema is a central piece of a GraphQL server. It defines:
- Types: The shape of the data.
- Queries: The read operations.
- Mutations: The write operations.
- Subscriptions: Real-time updates (optional).
Key Components of a GraphQL Schema
- Types: Define the structure of the data.
- Queries: Define how to read data.
- Mutations: Define how to modify data.
- Resolvers: Functions that resolve the data for each field.
Defining a Simple Schema
Let's start with a basic example. Suppose we have a simple application that manages users.
Step 1: Define Types
ID!: A non-nullable unique identifier.String!: A non-nullable string.
Step 2: Define Queries
users: Returns a list of users.user(id: ID!): Returns a single user by ID.
Step 3: Define Mutations
type Mutation {
createUser(name: String!, email: String!): User!
updateUser(id: ID!, name: String, email: String): User
deleteUser(id: ID!): User
}createUser: Creates a new user.updateUser: Updates an existing user.deleteUser: Deletes a user.
Step 4: Combine into a Schema
Example Schema
Here is the complete schema for our user management example:
type User {
id: ID!
name: String!
email: String!
}
type Query {
users: [User!]!
user(id: ID!): User
}
type Mutation {
createUser(name: String!, email: String!): User!
updateUser(id: ID!, name: String, email: String): User
deleteUser(id: ID!): User
}
schema {
query: Query
mutation: Mutation
}Practical Example
Let's implement this schema in a GraphQL server using Node.js and the graphql package.
Step 1: Install Dependencies
Step 2: Create the Server
const express = require('express');
const { graphqlHTTP } = require('express-graphql');
const { buildSchema } = require('graphql');
// Define the schema
const schema = buildSchema(`
type User {
id: ID!
name: String!
email: String!
}
type Query {
users: [User!]!
user(id: ID!): User
}
type Mutation {
createUser(name: String!, email: String!): User!
updateUser(id: ID!, name: String, email: String): User
deleteUser(id: ID!): User
}
schema {
query: Query
mutation: Mutation
}
`);
// Sample data
let users = [
{ id: '1', name: 'John Doe', email: '[email protected]' },
{ id: '2', name: 'Jane Doe', email: '[email protected]' },
];
// Define the resolvers
const root = {
users: () => users,
user: ({ id }) => users.find(user => user.id === id),
createUser: ({ name, email }) => {
const newUser = { id: String(users.length + 1), name, email };
users.push(newUser);
return newUser;
},
updateUser: ({ id, name, email }) => {
const user = users.find(user => user.id === id);
if (!user) return null;
if (name) user.name = name;
if (email) user.email = email;
return user;
},
deleteUser: ({ id }) => {
const userIndex = users.findIndex(user => user.id === id);
if (userIndex === -1) return null;
const [deletedUser] = users.splice(userIndex, 1);
return deletedUser;
},
};
// Create an Express server and a GraphQL endpoint
const app = express();
app.use('/graphql', graphqlHTTP({
schema: schema,
rootValue: root,
graphiql: true,
}));
app.listen(4000, () => console.log('Server running on http://localhost:4000/graphql'));Step 3: Test the Server
You can test the server by navigating to http://localhost:4000/graphql and running queries and mutations in the GraphiQL interface.
Exercises
Exercise 1: Add a New Field to the User Type
Add a new field age of type Int to the User type and update the schema accordingly.
Solution:
type User {
id: ID!
name: String!
email: String!
age: Int
}
type Query {
users: [User!]!
user(id: ID!): User
}
type Mutation {
createUser(name: String!, email: String!, age: Int): User!
updateUser(id: ID!, name: String, email: String, age: Int): User
deleteUser(id: ID!): User
}
schema {
query: Query
mutation: Mutation
}Exercise 2: Implement a Resolver for the New Field
Update the resolvers to handle the new age field.
Solution:
const root = {
users: () => users,
user: ({ id }) => users.find(user => user.id === id),
createUser: ({ name, email, age }) => {
const newUser = { id: String(users.length + 1), name, email, age };
users.push(newUser);
return newUser;
},
updateUser: ({ id, name, email, age }) => {
const user = users.find(user => user.id === id);
if (!user) return null;
if (name) user.name = name;
if (email) user.email = email;
if (age !== undefined) user.age = age;
return user;
},
deleteUser: ({ id }) => {
const userIndex = users.findIndex(user => user.id === id);
if (userIndex === -1) return null;
const [deletedUser] = users.splice(userIndex, 1);
return deletedUser;
},
};Conclusion
In this section, we covered the basics of GraphQL schemas, including defining types, queries, and mutations. We also implemented a simple GraphQL server to demonstrate these concepts. Understanding schemas is fundamental to working with GraphQL, as they define the structure and capabilities of your API. In the next module, we will dive deeper into core concepts such as queries, mutations, and resolvers.
