Connectors

The Grafbase Engine allows you to unify your data sources into a single GraphQL endpoint.

You can merge existing APIs to extend remote types, add additional fields with resolvers, apply auth rules and edge caching.

Get started connecting an external service using one of the connector types inside of your Grafbase configuration file.

Here we will connect Stripe using the OpenAPI connector:

import { config, connector, g } from '@grafbase/sdk'

const stripe = connector.OpenAPI('Stripe', {
  schema:
    'https://raw.githubusercontent.com/stripe/openapi/master/openapi/spec3.json',
  headers: headers => {
    headers.set('Authorization', `Bearer ${g.env('STRIPE_API_KEY')}`)
  },
})

g.datasource(stripe)

export default config({
  schema: g,
})

Make sure to add the environment variable STRIPE_API_KEY to the .env file.

Now run the Grafbase CLI to start the local development server:

npx grafbase dev

Finally you can execute a GraphQL query or mutation using one of the generated types.

query {
  stripe {
    invoices {
      nodes {
        id
        amountDue
        amountPaid
        created
        customerName
        customerEmail
      }
    }
  }
}

Combine multiple data sources by adding additional datasources.

import { config, connector, g } from '@grafbase/sdk'

const stripe = connector.OpenAPI('Stripe', {
  schema:
    'https://raw.githubusercontent.com/stripe/openapi/master/openapi/spec3.json',
})

const openai = connector.OpenAPI('OpenAI', {
  schema:
    'https://raw.githubusercontent.com/openai/openai-openapi/master/openapi.yaml',
})

g.datasource(stripe)
g.datasource(openai)

Grafbase supports static and introspection headers depending on the connector used.

Below is an example of how you can set different headers to be sent with requests.

import { config, connector, g } from '@grafbase/sdk'

const contentful = connector.GraphQL('Contentful', {
  url: g.env('CONTENTFUL_API_URL'),
  headers: headers => {
    headers.set('Authorization', '...')
    headers.set('Method', 'POST')
    headers.introspection('x-api-key', '...')
  }
})

const openai = connector.OpenAPI('OpenAI', {
  schema: g.env('OPENAI_API_KEY')
  headers: headers => {
    headers.set('Authorization', '...')
    headers.set('Method', 'POST')
    headers.introspection('x-api-key', '...')
  }
})

g.datasource(contentful)
g.datasource(openai)

You can configure header forwarding from user requests to the downstream connected service.

import { config, connector, g } from '@grafbase/sdk'

const contentful = connector.GraphQL('Contentful', {
  url: g.env('CONTENTFUL_API_URL'),
  headers: headers => {
    headers.set('Authorization', { forward: "Authorization" })
  }
})

const openai = connector.OpenAPI('OpenAI', {
  schema: g.env('OPENAI_API_KEY')
  headers: headers => {
    headers.set('Authorization', { forward: "Authorization" })
  }
})

g.datasource(contentful)
g.datasource(openai)

You can also forward the user's request header and map it to another header:

import { config, connector, g } from '@grafbase/sdk'

const contentful = connector.GraphQL('Contentful', {
  url: g.env('CONTENTFUL_API_URL'),
  headers: headers => {
    headers.set('Authorization', { forward: 'original-header-name' })
  },
})

g.datasource(contentful)

You can use the transform methods to modify the generated APIs from connectors.

You can transform the connected APIs to exclude queries, mutations and fields.

import { config, connector, g } from '@grafbase/sdk'

const stripe = connector.OpenAPI('Stripe', {
  schema:
    'https://raw.githubusercontent.com/stripe/openapi/master/openapi/spec3.json',
  transforms: schema => {
    // Excluding email from StripeCustomer
    schema.exclude('StripeCustomer.email')

    // Excluding the customer field from all top-level query types
    schema.exclude('Query.*.customer')

    // Excluding a set of fields from a type
    schema.exclude('StripeCustomer.{email,name}')

    // Passing multiple arguments
    schema.exclude('StripeCustomer.email', 'Query.*.customers')

    // Exclude all mutations
    schema.exclude('Mutation.*')
  },
})

g.datasource(stripe)

export default config({
  schema: g,
})

If you want to create a read-only API you can use the Mutation.* wildcard with the exclude transformer.

You can extend connected API types and access root fields with resolvers:

import { g } from '@grafbase/sdk'

g.extend('StripeCustomer', {
  myField: {
    args: { myArg: g.string().optional() },
    returns: g.string().optional(),
    resolver: 'file',
  },
})

Connectors can also use environment variables locally with the CLI, and in production.

import { config, connector, g } from '@grafbase/sdk'

const stripe = connector.OpenAPI('Stripe', {
  schema: g.env('STRIPE_SCHEMA_URL'),
  headers: headers => {
    headers.set('Authorization', `Bearer ${g.env('STRIPE_API_KEY')}`)
  },
})

g.datasource(stripe)

When connecting multiple APIs is better to namespace connectors to avoid type collisions.

Connectors are automatically namespaced using the name provided. The output GraphQL schema looks something like this:

type Query {
  stripe: StripeQuery!
  github: GithubQuery!
}

type Mutation {
  stripe: StripeMutation!
  github: GithubMutation!
}

The namespace is derived from the name given to the connector. You must use a unique connector name to rename the namespace:

import { config, connector, g } from '@grafbase/sdk'

const stripe = connector.OpenAPI('Payments', {
  schema: g.env('STRIPE_SCHEMA_URL'),
})

g.datasource(stripe)

You can also disable namespaces. This is useful if you use a single connector and want to use Grafbase as a caching gateway:

import { config, connector, g } from '@grafbase/sdk'

const stripe = connector.OpenAPI('Stripe', {
  schema: g.env('STRIPE_SCHEMA_URL'),
})

g.datasource(stripe, { namespace: false })

You can enable caching to connected APIs using the caching config:

import { config, connector, g } from '@grafbase/sdk'

const contentful = connector.GraphQL('Contentful', {
  schema: g.env('CONTENTFUL_API_URL'),
  headers: headers => {
    headers.set('Authorization', `Bearer ${g.env('CONTENTFUL_API_KEY')}`)
  },
})

g.datasource(contentful)

export default config({
  schema: g,
  cache: {
    rules: [
      {
        types: 'ContentfulQuery',
        maxAge: 60,
        staleWhileRevalidate: 60,
      },
    ],
  },
})
Was this page helpful?