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:

  1. root: The root of the current query/mutation/field
  2. args: The arguments provided to the query/mutation/field
  3. context: 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)
}
Was this page helpful?