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.