Code generation
Grafbase can generate TypeScript types for your custom resolvers. Codegen takes into account all types, including those from connectors, for example when you use the Postgres or OpenAPI connectors and you extend types coming from your database or your API.
To enable the feature, you have to opt in to the codegen
experimental feature:
export default config({
graph: g,
experimental: {
codegen: true,
},
})
Once the experimental feature is enabled, whenever you run grafbase dev
, the
types will be generated in a directory called generated
next to your Grafbase
configuration.
❯ tree .
.
├── generated
│ └── index.ts
├── grafbase.config.ts
├── node_modules
│ └── @grafbase
│ └── sdk -> ../.pnpm/@grafbase+sdk@0.15.0/node_modules/@grafbase/sdk
├── package.json
├── package-lock.json
├── pnpm-lock.yaml
├── tsconfig.json
└── README.md
You can decide to commit these types or put the generated
directory in your
.gitignore
. Both options work fine once you deploy.
We advise you to add the following snippet to your tsconfig.json
inside
"compilerOptions", so you can import the generated types from @grafbase/ generated
:
"compilerOptions": {
"paths": {
"@grafbase/generated": ["./generated"]
}
Then you are ready use types in your resolvers. In this example, we'll extend a type from the Stripe API, using the OpenAPI connector.
import { config, connector, graph } from '@grafbase/sdk'
const g = graph.Standalone()
const stripe = connector.OpenAPI('Stripe', {
schema:
'https://raw.githubusercontent.com/stripe/openapi/master/openapi/spec3.json',
headers: headers => {
headers.set('Authorization', { forward: 'Authorization' })
},
transforms: { queryNaming: 'OPERATION_ID' },
})
g.extend('StripeAccountBusinessProfile', t => {
t.addField(
'isRecurringCustomer',
g.boolean().resolver('customer/is_recurring'),
)
})
g.datasource(stripe)
export default config({
graph: g,
cache: {
rules: [
{
types: ['Query'],
maxAge: 60,
},
],
},
auth: {
rules: rules => {
rules.public()
},
},
experimental: { codegen: true },
})
When you write the resolver at resolvers/customer/is_recurring.ts
, you only
need one type annotation to make your resolver fully typed, including checking
of argument and return types, as well as autocompletion.
import { Resolver } from '@grafbase/generated'
const resolver: Resolver['StripeAccountBusinessProfile.isRecurringCustomer'] = (
parent,
args,
context,
) => {
// TODO: implement the customerIsRecurring resolver
return true
}
export default resolver
When you start typing const resolver: Resolver['
, you get autocompletion on
the names of the custom resolvers. Once that is inserted, all the arguments
(parent, args, context) and the return type are strongly typed.
In addition, the grafbase dev
command uses the type annotation to double check
that you are using the right generated type in the right resolver file, and that
the resolver file corresponds to the right field in your configuration.
Code generation is still an experimental feature because we want to gather feedback on the developer experience and the generated code before we stabilize it. We'd love to hear your feedback and ideas, so join us on Discord.