Express is just a web server framework, and GraphQL is an API query language. You can add a GraphQL endpoint to an Express server by integrating a GraphQL server library like express-graphql or Apollo Server.
Let’s see how to do it with express-graphql.
First, install the necessary packages:
npm install express express-graphql graphql
Now, set up your Express server and add the GraphQL endpoint:
// server.js
const express = require('express');
const graphqlHTTP = require('express-graphql');
const { buildSchema } = require('graphql');
// Construct a schema, using GraphQL schema language
const schema = buildSchema(`
type Query {
hello: String
user(id: ID!): User
}
type User {
id: ID!
name: String
email: String
}
`);
// Mock data for users
const users = {
1: { id: '1', name: 'Alice', email: 'alice@example.com' },
2: { id: '2', name: 'Bob', email: 'bob@example.com' },
};
// The root provides a resolver function for each API endpoint
const root = {
hello: () => 'Hello world!',
user: ({ id }) => users[id],
};
// Create an Express server
const app = express();
// Mount the GraphQL endpoint
app.use('/graphql', graphqlHTTP({
schema: schema,
rootValue: root,
graphiql: true, // Enable GraphiQL, a browser-based IDE
}));
// Start the server
const port = 4000;
app.listen(port, () => {
console.log(`Running a GraphQL API server at http://localhost:${port}/graphql`);
});
When you run this server and navigate to http://localhost:4000/graphql in your browser, you’ll see the GraphiQL interface.
Here’s how you can query the hello field:
query {
hello
}
And the response will be:
{
"data": {
"hello": "Hello world!"
}
}
To query for a user:
query GetUser($userId: ID!) {
user(id: $userId) {
id
name
email
}
}
With variables:
{
"userId": "1"
}
The response for user with ID 1 will be:
{
"data": {
"user": {
"id": "1",
"name": "Alice",
"email": "alice@example.com"
}
}
}
The schema defines the shape of your data and the operations (queries, mutations, subscriptions) that clients can perform. buildSchema is a utility from the graphql package that parses the GraphQL schema language into a format that the GraphQL execution engine can understand.
The rootValue is an object where each key corresponds to a field in your schema’s Query type (or Mutation and Subscription types). The value associated with each key is a resolver function. This function is responsible for fetching the data for that specific field. In our example, hello returns a static string, and user looks up a user by ID in a mock users object.
express-graphql acts as a bridge, taking incoming GraphQL requests, executing them against your schema and resolvers, and returning the results in a standard JSON format. The graphiql: true option is incredibly useful for development, providing an interactive environment to test your API directly in the browser.
One of the most powerful aspects of GraphQL is its ability to allow clients to request exactly the data they need, no more and no less. This contrasts sharply with REST APIs, where you often get fixed data structures and might have to make multiple requests to gather related information, leading to over-fetching or under-fetching. With GraphQL, a single query can fetch a complex object graph.
The way resolvers are structured is crucial. For nested fields within a type (like name and email within User), GraphQL automatically resolves them if the parent resolver returns an object with properties matching the field names. If you need custom logic for nested fields, you can define resolver functions for them as well.
The next step in building a robust GraphQL API is often implementing mutations for data modification, followed by setting up subscriptions for real-time data updates.