Edge Resolvers
Resolvers extend the unified GraphQL API provided by Grafbase and run in V8 isolates. Resolvers can be used to compute custom business logic for internal and external data sources.
You can build locally using the Grafbase CLI and control access to resolvers using authorization rules. Resolvers automatically deploy to the edge when you push to GitHub.
Resolvers can be added to extend database models with field resolvers, or the schema with query and mutation resolvers.
Resolvers are made up of two major components:
- The field definition with
@resolver
directive - The resolver function implementation inside
grafbase/resolvers
Inside grafbase/schema.graphql
add the query hello
:
extend type Query {
hello(name: String): String! @resolver(name: "hello")
}
Add the following to the file grafbase/resolvers/hello.js
:
export default function Resolver(_, { name }) {
return `Hello ${name || 'world'}!`
}
Now run the Grafbase CLI to start the local development server:
npx grafbase dev
Finally execute the following GraphQL query:
query {
hello(name: "Grafbase")
}
Type definitions are written using the standard GraphQL SDL and are used to tell GraphQL the server's capabilities in the format of a schema.
You must use the @resolver
directive and pass it the path of the resolver file.
type User @model {
name: String
email: Email
gravatar: URL @resolver(name: "user/gravatar")
}
All resolvers must be placed inside the folder grafbase/resolvers
. You can also use folders to separate your resolver types for queries, mutations, fields, or models.
.
└── grafbase
└── package.json
└── resolvers
└── user
└── gravatar.js
└── schema.graphql
Resolver functions have the following arguments in this order:
root
: The root of the current query/mutation/fieldargs
: The arguments provided to the query/mutation/fieldcontext
: The context from the Grafbase server/project
export default function Resolver(root, args, context) {
// return ...
}
The first parameter root
(sometimes called parent
) is the result of the previous call.
type User @model {
name: String
email: Email
gravatar: URL @resolver(name: "user/gravatar")
}
The second parameter passed to the resolver is known as args
.
These arguments are defined by you and work with query, mutation and field resolvers.
extend type Query {
sum(a: Int! = 0, b: Int! = 0): Int! @resolver(name: "sum")
}
extend type Mutation {
checkout(input: CheckoutSessionInput!): CheckoutSession!
@resolver(name: "checkout")
}
input CheckoutSessionInput {
lineItems: [CheckoutLineItem]
}
input CheckoutLineItem {
price: String!
quantity: Int = 1
}
type CheckoutSession {
url: Int!
}
The third argument referred to as context
contains important information from Grafbase about the request.
You can obtain the forwarded request headers from inside the context
argument:
export default function Resolver(_, __, { request }) {
const { headers } = request
// ...
}
This is useful if you need to use the current Authorization
header from the user management platform with whatever next action you take inside the resolver.
The Query type is at the root of your GraphQL API which contains any auto-generated queries by the Grafbase Database, and also any custom resolvers you have added.
extend type Query {
hello: String! @resolver(name: "hello-world")
}
Mutations are used when you want to mutate data. The GraphQL API contains mutations auto-generated by the Grafbase Database for all models and any custom resolvers you have added.
extend type Mutation {
say(word: String!): String! @resolver(name: "say")
}
Resolvers can be attached to fields and fields can access the data stored inside the model using the first argument (root
):
type Location {
latitude: Float!
longitude: Float!
}
type Place @model {
name: String
location: Location
weather: Float @resolver(name: "place/weather")
}
Resolvers can use environment variables set using the CLI or in your project settings.
extend type Query {
hello: String @resolver(name: "hello")
}
You can install dependencies for resolvers inside of the grafbase
folder.
Make sure you have a package.json
.
cd grafbase
npm init -y
npm install stripe
type Address {
line1: String
city: String
country: String
}
type Customer {
id: ID!
balance: Int
email: Email
address: Address
}
extend type Query {
customers: [Customer] @resolver(name: "customers")
}
Resolvers can be written in TypeScript (.ts
) and JavaScript (.js
), with support for WebAssembly and more on the way.
You can enable caching for resolvers using the @cache
directive:
type User @model {
name: String
email: Email
gravatar: URL
@resolver(name: "user/gravatar")
@cache(maxAge: 60, staleWhileRevalidate: 60)
}