Grafbase KV

Grafbase KV is a simple and fast serverless key-value store for resolvers that is eventually consistent.

This feature is currently experimental and free, for now.

You can enable the experimental KV by adding the following to your grafbase.config.ts config export object:

export default config({
  schema: g,
  experimental: {
    kv: true,
  },
})

Make sure to enable the experimental KV support inside your project's configuration file.

  1. Import kv from the context argument inside a JavaScript or TypeScript resolver:
export default async function MyResolver(_, __, { kv }) {
  // ...
}
  1. Get a value from KV using a custom key:
export default async function MyResolver(_, __, { kv }) {
  let { value } = await kv.get('hello')
}
  1. Set if no value exists:
export default async function MyResolver(_, __, { kv }) {
  let { value } = await kv.get('hello')

  if (!value) {
    value = await kv.set('hello', 'world')
  }

  return value
}
  1. Set an optional time-to-live value in seconds:
export default async function MyResolver(_, __, { kv }) {
  let { value } = await kv.get('hello')

  if (!value) {
    value = await kv.set('hello', 'world', { ttl: 3600 })
  }

  return value
}

You can create a new key-value pair or update an existing value for a key by calling kv.set():

export default async function MyResolver(_, __, { kv }) {
  const key = '...'
  const value = '...'

  await kv.set(key, value)
}

You can fetch the value of a key-value pair at anytime by calling kv.get():

export default async function MyResolver(_, __, { kv }) {
  const key = '...'

  const { value } = await kv.get(key)
}

You can also fetch metadata for the key-value pair:

export default async function MyResolver(_, __, { kv }) {
  const key = '...'

  const { metadata } = await kv.get(key)
}

You can fetch a list of all keys anytime by calling kv.list():

export default async function MyResolver(_, __, { kv }) {
  await kv.list()
}

The object returned by kv.list() looks something like this:

{
  "keys": [
    {
      "customKey": "customValue",
      "expiration": 1696328752,
      "metadata": { "someKey": "someValue" }
    }
  ],
  "list_complete": false,
  "cursor": "6Ck1la0VxJ0djhidm1MdX2FyD"
}

If the list_complete is false then you will receive a cursor value you can pass to list() to obtain the next set of results.

You can pass values prefix, limit and cursor to kv.list():

export default async function MyResolver(_, __, { kv }) {
  await kv.list({ prefix, limit, cursor })
}
  • prefix is an optional string used to filter all keys
  • limit is an optional max number of keys to return
  • cursor is an optional string used for paginating responses

Keys are always returned in the order sorted based on their UTF-8 byte values.

To delete a key-value pair you can call kv.delete():

export default async function MyResolver(_, __, { kv }) {
  const key = '...'

  await kv.delete(key)
}

You can store metadata (serialized JSON) on key-value pairs which is also returned by list() and get():

export default async function MyResolver(_, __, { kv }) {
  const key = '...'
  const value = '...'
  const metadata = {
    hello: 'world',
  }

  await kv.set(key, value, { metadata })
}

You can expire key-value pairs by passing a ttl value (in seconds) when calling kv.set():

export default async function MyResolver(_, __, { kv }) {
  const key = '...'
  const value = '...'

  await kv.set(key, value, { ttl: 3600 })
}

The minimum ttl is 60 seconds.

Was this page helpful?