GraphQL is a query language for your API and a server-side runtime for executing those queries by using a type system you define for your data. It was developed by Facebook to solve a key problem with REST APIs: over-fetching (getting more data than you need) and under-fetching (not getting enough data, requiring multiple requests).
With GraphQL, the client requests exactly what it needs, and that's exactly what it gets. No more, no less. 🚀
The Three Core Concepts
- Schema (The "What"): The schema is the heart of a GraphQL API. It's a contract between the client and the server that defines what data can be queried. It's written in the Schema Definition Language (SDL). The main components are Types, Queries, and Mutations.
- type: Defines the shape of an object.
- Query: Defines the entry points for read operations (fetching data).
- Mutation: Defines the entry points for write operations (creating, updating, deleting data).
- Resolvers (The "How"): Resolvers are the implementation of the schema. They are functions that "resolve" a value for a type or field in a schema. Think of them as the functions that fetch the data from a database, a third-party API, or any other source.
- Apollo Server (The "Server"): Apollo Server is a popular, open-source GraphQL server library for Node.js. It helps you connect your schema and resolvers and expose them as a queryable GraphQL API endpoint.
Let's Build a Mini API
Let's create a simple API for a blog that has authors and posts.
1. Define the Schema
First, we define our data shapes and the available queries using SDL.
GraphQL
# schema.graphql
# Defines the shape of an Author
type Author {
id: ID!
name: String!
posts: [Post!]
}
# Defines the shape of a Post
type Post {
id: ID!
title: String!
content: String!
author: Author!
}
# Defines the available queries
type Query {
getAllPosts: [Post!]
getPost(id: ID!): Post
}
- ID!: The ! means the field is non-nullable. ID is a special scalar type.
- [Post!]: This means it's an array of Post objects.
2. Write the Resolvers
Next, we write the JavaScript functions that will fetch the data for our schema. For this example, we'll use a simple in-memory data array instead of a real database.
JavaScript
// A mock database
const authors = [{ id: '1', name: 'J.K. Rowling' }];
const posts = [{ id: '101', title: 'GraphQL is Magic', content: '...', authorId: '1' }];
// The resolver map
const resolvers = {
Query: {
getAllPosts: () => posts,
getPost: (parent, args) => posts.find(post => post.id === args.id),
},
Post: {
// This resolver fetches the author for a given post
author: (parent) => authors.find(author => author.id === parent.authorId),
},
};
Notice how the resolver map's structure mirrors the schema. The Post.author resolver is a nested resolver that gets called whenever the author field of a Post is requested.
3. Set Up Apollo Server
Finally, we put it all together with apollo-server.
JavaScript
// server.js
import { ApolloServer, gql } from 'apollo-server';
// Mock data and resolvers from the previous step
const authors = [{ id: '1', name: 'J.K. Rowling' }];
const posts = [{ id: '101', title: 'GraphQL is Magic', content: '...', authorId: '1' }];
const resolvers = {
Query: {
getAllPosts: () => posts,
getPost: (parent, args) => posts.find(post => post.id === args.id),
},
Post: {
author: (parent) => authors.find(author => author.id === parent.authorId),
},
};
// The schema definition (typeDefs)
const typeDefs = gql`
type Author {
id: ID!
name: String!
}
type Post {
id: ID!
title: String!
author: Author!
}
type Query {
getAllPosts: [Post!]
getPost(id: ID!): Post
}
`;
// Create an instance of ApolloServer
const server = new ApolloServer({ typeDefs, resolvers });
// Start the server
server.listen().then(({ url }) => {
console.log(`🚀 Server ready at ${url}`);
});
Now, if you run this and navigate to the URL, you'll see the Apollo Studio, where you can run queries like this:
GraphQL
query GetOnePost {
getPost(id: "101") {
title
author {
name
}
}
}
The server will respond with only the title and the author's name for that post, demonstrating the power of GraphQL!