Quickstart to Grafbase

Learn how to build three simple subgraphs: accounts, products, and reviews. See how GraphQL Federation works, how to publish subgraphs to a federated graph, and how to join data between subgraphs.

Create a new account on the Grafbase dashboard, and install the Grafbase CLI and Grafbase Gateway before you start.

Start by creating the accounts subgraph with Node.js and GraphQL Yoga. First, create a new directory and initialize a new Node.js project.

mkdir accounts && cd accounts npm init -y npm install graphql-yoga @apollo/subgraph graphql

Next, create a new file index.js and add the following code:

import { createSchema, createYoga } from 'graphql-yoga' import { createServer } from 'http' const users = [ { id: '1', email: 'john@example.com', username: 'john_doe', }, { id: '2', email: 'bob@example.com', username: 'bob_dole', }, ] const schema = createSchema({ typeDefs: /* GraphQL */ ` type Query { users: [User!]! } type User { id: ID! email: String! username: String! } `, resolvers: { Query: { users: () => users, }, }, }) const yoga = createYoga({ schema }) const server = createServer(yoga) server.listen(4000, () => { console.log('🚀 Server ready at http://localhost:4000/graphql') })

Launch the server by running node index.js. Access the GraphQL playground at http://localhost:4000/graphql.

The accounts subgraph is simple and not yet ready for Federation. A single query users returns all users in the subgraph:

query { users { id email username } }

Which returns:

{ "data": { "me": { "id": "1", "email": "john@example.com", "username": "john_doe" } } }

While Grafbase Gateway typically federates multiple subgraphs, you can create a configuration for a single subgraph and publish it to the schema registry. Use the grafbase dev command to start quickly. Add this configuration to subgraphs.toml file:

[subgraphs.accounts] introspection_url = "http://localhost:4000/graphql"

Start the grafbase dev server:

grafbase dev -o subgraphs.toml

The server starts at http://127.0.0.1:5000. Access the Pathfinder and query the federated graph.

Define an identifier for a type to be part of federation. The User type has the id field as the identifier. Add the @key directive to the User type:

type Query { me: User } type User @key(fields: "id") { id: ID! email: String! username: String! }

Modify the Yoga server to accommodate federation. Use the @apollo/subgraph package to create a federated schema, parse the schema with graphql library and add the edge __resolveReference to the user type. The @key directive identifies User as an entity with the id field. Use the same User type in multiple subgraphs, with each subgraph resolving the User type by its id.

import { buildSubgraphSchema } from '@apollo/subgraph' import { parse } from 'graphql' import { createYoga } from 'graphql-yoga' import { createServer } from 'http' const users = [ { id: '1', email: 'john@example.com', username: 'john_doe', }, { id: '2', email: 'bob@example.com', username: 'bob_dole', }, ] const typeDefs = parse(/* GraphQL */ ` type Query { users: [User!]! } type User @key(fields: "id") { id: ID! email: String! username: String! } `) const resolvers = { Query: { users: () => users, }, User: { __resolveReference: user => users.find(u => u.id === user.id), }, } const schema = buildSubgraphSchema({ typeDefs, resolvers, }) const yoga = createYoga({ schema }) const server = createServer(yoga) server.listen(4000, () => { console.log('🚀 Server ready at http://localhost:4000/graphql') })

Restart the yoga server after making changes. The Grafbase dev server automatically updates the subgraph.

Create a new products directory and initialize a new Node.js project:

mkdir products && cd products npm init -y npm install graphql-yoga @apollo/subgraph graphql

Create a new file index.js and add this code:

import { buildSubgraphSchema } from '@apollo/subgraph' import { parse } from 'graphql' import { createYoga } from 'graphql-yoga' import { createServer } from 'http' const typeDefs = parse(` type Query { topProducts(first: Int = 5): [Product] } type Product @key(fields: "id") { id: String! upc: String! name: String! price: Int! } `) const products = [ { id: '1', upc: 'upc-1', name: 'Product 1', price: 999, }, { id: '2', upc: 'upc-2', name: 'Product 2', price: 1299, }, ] const resolvers = { Query: { topProducts: (_, { first = 5 }) => products.slice(0, first), }, Product: { __resolveReference: reference => { return products.find(p => p.id === reference.id) }, }, } const yoga = createYoga({ schema: buildSubgraphSchema([ { typeDefs, resolvers, }, ]), }) const server = createServer(yoga) server.listen(4001, () => { console.log('🚀 Server ready at http://localhost:4001/graphql') })

The Product type has two identifiers, id and upc, both required to resolve the type. A new query topProducts returns the top products. Edit the subgraphs.toml file to include the new subgraph:

[subgraphs.accounts] introspection_url = "http://localhost:4000/graphql" [subgraphs.products] introspection_url = "http://localhost:4001/graphql"

The Grafbase dev server now has two subgraphs, accounts and products. Access both by calling the topProducts query from the products subgraph and the me query from the accounts subgraph.

Create the final reviews subgraph. Create a new directory reviews and initialize a new Node.js project:

mkdir reviews && cd reviews npm init -y npm install graphql-yoga @apollo/subgraph graphql

Create a new file index.js and add this code:

import { buildSubgraphSchema } from '@apollo/subgraph' import { parse } from 'graphql' import { createYoga } from 'graphql-yoga' import { createServer } from 'http' // Define the schema const typeDefs = parse(` type Product @key(fields: "id") { id: String! @external reviews: [Review] } type User @key(fields: "id") { id: ID! @external email: String! @external username: String! @external reviews: [Review] } type Review { body: String! author: User! product: Product! } `) // Mock data const reviews = [ { body: 'Great product!', authorId: '1', productId: '1', }, { body: 'Would recommend!', authorId: '2', productId: '2', }, ] // Define resolvers const resolvers = { Product: { __resolveReference: reference => { return { id: reference.id } }, reviews: product => { return reviews.filter(review => review.productId === product.id) }, }, User: { __resolveReference: reference => { return { id: reference.id } }, reviews: user => { return reviews.filter(review => review.authorId === user.id) }, }, Review: { author: review => { return { id: review.authorId } }, product: review => { return { id: review.productId } }, }, } // Create the yoga server const yoga = createYoga({ schema: buildSubgraphSchema([ { typeDefs, resolvers, }, ]), }) // Create and start the server const server = createServer(yoga) server.listen(4002, () => { console.log('🚀 Server ready at http://localhost:4002/graphql') })

This subgraph extends the Product and User types from the products and accounts subgraphs instead of defining queries. Fields with the @external directive resolve by the parent subgraph; this subgraph only provides reviews for products and users.

The Grafbase Gateway gets product reviews by sending the product id to find all reviews. The same applies to users.

The review object returns the author and product IDs. The gateway resolves the author and product based on these IDs.

Add the reviews subgraph to the subgraphs.toml file:

[subgraphs.accounts] introspection_url = "http://localhost:4000/graphql" [subgraphs.products] introspection_url = "http://localhost:4001/graphql" [subgraphs.reviews] introspection_url = "http://localhost:4002/graphql"

With three subgraphs in the federated graph, query and join data between them:

query Users { users { id email username reviews { body product { id name price upc } } } }

This query returns all users from accounts, their reviews from reviews and product information from products:

{ "data": { "topProducts": [ { "id": "1", "name": "Product 1", "price": 999, "reviews": [ { "author": { "id": "1", "email": "john@example.com", "username": "john_doe" }, "body": "Great product!" } ] }, { "id": "2", "name": "Product 2", "price": 1299, "reviews": [ { "author": { "id": "2", "email": "bob@example.com", "username": "bob_dole" }, "body": "Would recommend!" } ] } ] } }

Click "Response Query Plan view" in Pathfinder to see the query plan:

Query Plan

The query plan shows how the federated graph resolves queries. It first gets topProducts from products, then gets _entities from reviews with product IDs from topProducts, and finally gets _entities from accounts with user IDs from reviews. The Grafbase Gateway minimizes requests and resolves data efficiently. If possible, it runs some queries in parallel to speed up response time.

After creating and testing the subgraphs, publish the graph to Grafbase Platform. Create a new graph in the Grafbase Dashboard.

Create New Graph

Name the graph quickstart and click "Create Graph". Use the graph reference to publish the graph.

From the terminal, publish the subgraphs. First introspect the running accounts subgraph, then pipe output to publish. Provide the subgraph name, URL, commit message and organization-name/graph slug:

grafbase introspect http://localhost:4000/graphql \ | grafbase publish \ --name accounts \ --url http://localhost:4000/graphql \ --message "init accounts" \ my-org/quickstart

Repeat for products and reviews:

grafbase introspect http://localhost:4001/graphql \ | grafbase publish \ --name products \ --url http://localhost:4001/graphql \ --message "init products" \ my-org/quickstart grafbase introspect http://localhost:4002/graphql \ | grafbase publish \ --name reviews \ --url http://localhost:4002/graphql \ --message "init reviews" \ my-org/quickstart

The schema and subgraphs appear in the Grafbase Dashboard after publishing.

The Grafbase Gateway deploys as a single binary in your infrastructure. The gateway optimizes for production while sharing code with dev. It automatically detects and updates on federated graph changes.

After installation, create an access token in organization settings:

New Access Token

Export it and start the gateway:

export GRAFBASE_ACCESS_TOKEN="ey..." grafbase-gateway --graph-ref quickstart

Use Pathfinder in the Grafbase Dashboard to query the federated graph. Set the endpoint URL to http://127.0.0.1:5000/graphql before the first query.

Deploy a production gateway to a server with proper domain and SSL. For testing, configure the local gateway to trace all requests. Edit grafbase.toml:

[telemetry.tracing] sampling = 1

Restart the gateway:

grafbase-gateway --graph-ref quickstart --config grafbase.toml

Return to Pathfinder, run the previous query:

query Users { users { id email username reviews { body product { id name price upc } } } }

Click "Response Trace view" to see sequential queries to accounts, reviews, and products:

Trace

It is a good practice to run schema checks before deploying changes to the federated graph.

Enable operation checks for the production branch in schema checks settings for best results:

Operation checks

Try changing a queried field. Make the username optional in accounts:

type Query { users: [User!]! } type User @key(fields: "id") { id: ID! email: String! username: String }

Restart the accounts subgraph and run schema checks against the federated graph:

grafbase introspect http://localhost:4000/graphql \ | grafbase check --name accounts my-org/quickstart

The check returns an error because clients queried username in the past seven days:

❌ [Error] The field `User.username` became optional, but clients do not expect null.

You can fix the error by making sure no clients query username for the configured period, or by making the field non-optional.

Define these headers in clients using the Grafbase Gateway federated graph:

  • x-grafbase-client-name for client name
  • x-grafbase-client-version for client version

These headers let Grafbase analytics show which clients access which fields, helping track field usage as schemas grow.

This guide showed how to create and federate three subgraphs, use Grafbase Gateway to query them, and publish to Grafbase Enterprise Platform.

Grafbase Gateway leads the market in GraphQL gateway speed and aims for full GraphQL Federation v2 compatibility. We continuously improve and add features. Contact us with questions or feedback.