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:
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.
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:
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
:
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:
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 namex-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.