Back to Mercurius

Integrating GQLoom with Mercurius

docs/integrations/gqloom.md

16.8.05.7 KB
Original Source

Integrating GQLoom with Mercurius

You can easily use GQLoom in combination with Mercurius.
GQLoom is an ergonomic code-first GraphQL Schema Loom that weaves runtime types from the TypeScript/JavaScript ecosystem into GraphQL Schema. It supports various runtime type sources such as Zod, Valibot, Prisma, MikroORM, and Drizzle, allowing you to build type-safe GraphQL APIs using your familiar validation and ORM libraries.

Installation

GQLoom supports multiple runtime type libraries. Choose the one that fits your needs:

Using Zod:

bash
# use npm
npm i @gqloom/core @gqloom/zod zod graphql fastify mercurius

# use pnpm
pnpm add @gqloom/core @gqloom/zod zod graphql fastify mercurius

# use yarn
yarn add @gqloom/core @gqloom/zod zod graphql fastify mercurius

Using Valibot:

bash
# use npm
npm i @gqloom/core @gqloom/valibot valibot graphql fastify mercurius

# use pnpm
pnpm add @gqloom/core @gqloom/valibot valibot graphql fastify mercurius

# use yarn
yarn add @gqloom/core @gqloom/valibot valibot graphql fastify mercurius

Quick Start

Here's a simple example to get you started. The main difference between Zod and Valibot is the import and weaver you use:

With Zod:

ts
// schema.ts
import { resolver, query, weave } from "@gqloom/core"
import { ZodWeaver } from "@gqloom/zod"
import * as z from "zod"

export const helloResolver = resolver({
  hello: query(z.string())
    .input({
      name: z
        .string()
        .nullish()
        .transform((value) => value ?? "World"),
    })
    .resolve(({ name }) => `Hello, ${name}!`),
})

export const schema = weave(ZodWeaver, helloResolver)

With Valibot:

ts
// schema.ts
import { resolver, query, weave } from "@gqloom/core"
import { ValibotWeaver } from "@gqloom/valibot"
import * as v from "valibot"

export const helloResolver = resolver({
  hello: query(v.string())
    .input({ name: v.nullish(v.string(), "World") })
    .resolve(({ name }) => `Hello, ${name}!`),
})

export const schema = weave(ValibotWeaver, helloResolver)

Then, link the schema to Mercurius:

ts
// index.ts
import Fastify from "fastify"
import mercurius from "mercurius"
import { schema } from "./resolvers"

const app = Fastify()

app.register(mercurius, {
  schema,
  graphiql: true,
})

app.listen({ port: 3000 }, (err, address) => {
  if (err) {
    console.error(err)
    process.exit(1)
  }
  console.log(`Server listening at ${address}`)
})

Run your server:

bash
ts-node index.ts

Working with Objects and Mutations

You can define object types and mutations using your chosen runtime validation library.

With Zod:

ts
// schema.ts
import * as z from "zod"

export const Cat = z.object({
  __typename: z.literal("Cat").nullish(),
  name: z.string(),
  age: z.int(),
  loveFish: z.boolean().nullish(),
})
ts
// schema.ts
import { resolver, query, mutation, weave } from "@gqloom/core"
import { ZodWeaver } from "@gqloom/zod"
import * as z from "zod"
import { Cat } from "./schema"

export const catResolver = resolver({
  cat: query(Cat.nullable())
    .input({ id: z.number() })
    .resolve(({ id }) => {
      // Your data fetching logic here
      return {
        name: "Fluffy",
        age: 3,
        loveFish: true,
      }
    }),

  createCat: mutation(Cat)
    .input({ data: Cat})
    .resolve(({ data }) => {
    // Your mutation logic here
    return data
  }),
})

export const schema = weave(ZodWeaver, catResolver)

With Valibot:

ts
// schema.ts
import * as v from "valibot"

export const Cat = v.object({
  __typename: v.nullish(v.literal("Cat")),
  name: v.string(),
  age: v.pipe(v.number(), v.integer()),
  loveFish: v.nullish(v.boolean()),
})
ts
// schema.ts
import { resolver, query, mutation, weave } from "@gqloom/core"
import { ValibotWeaver } from "@gqloom/valibot"
import * as v from "valibot"
import { Cat } from "./schema"

export const catResolver = resolver({
  cat: query(Cat.nullable())
    .input({ id: v.pipe(v.number(), v.integer()) })
    .resolve(({ id }) => {
      // Your data fetching logic here
      return {
        name: "Fluffy",
        age: 3,
        loveFish: true,
      }
    }),

  createCat: mutation(Cat)
    .input({ data: Cat })
    .resolve(({ data }) => {
    // Your mutation logic here
    return data
  }),
})

export const schema = weave(ValibotWeaver, catResolver)

Accessing Context

When using GQLoom with Mercurius, you can access the Mercurius context in your resolvers using the useContext hook.

First, create a helper function to access context:

ts
// context.ts
import { useContext } from "@gqloom/core/context"
import type { MercuriusContext } from "mercurius"

export function useAuthorization() {
  return useContext<MercuriusContext>().reply.request.headers.authorization
}

Then use it in your resolvers:

ts
// schema.ts
import { resolver, query, weave } from "@gqloom/core"
import { ZodWeaver } from "@gqloom/zod"
import * as z from "zod"
import { useAuthorization } from "./context"

export const authResolver = resolver({
  me: query(z.string())
    .resolve(() => {
      const authHeader = useAuthorization()
      return `Authorized with: ${authHeader}`
    }),
})

export const schema = weave(ZodWeaver, authResolver)

For more information about context in Mercurius, see the Context documentation.

Learn More