LakeQL
  • Overview
Server Setup
  • Create Server
  • Define Config
  • Yoga Configuration
  • createLakeQLApp
Authentication
  • Authorization Model
  • JWT Authentication
  • Permissions
  • Scope Authorization
  • Trino User Impersonation
  • Debugging Identity
Schema Builder
  • Builder Configuration
  • Scalar Types
  • Comparison Types
  • Pagination
  • Input Validation
Customization
  • Custom Queries & Mutations
  • Extending Core
  • CORS Configuration
  • Error Handling
  • API Reference
GitHub
LakeQL
  1. API
  2. Customization
  3. Custom Queries & Mutations

On this page

  1. Two Prerequisites
  2. Authorization
  3. Validation
  4. Accessing Data

Custom Queries & Mutations

Add your own fields alongside the generated schema.

The generated endpoints attach to the shared builder. You add custom queries and mutations the same way. Define fields on the builder, and they join the schema the server serves.

1
2
3
4
5
6
7
8
import { builder } from "@lakeql/api/builder"

builder.queryField("ping", (t) =>
  t.string({
    resolve: () => "pong",
  })
)

Because it is the same builder, your custom fields get the same facilities as the generated ones. That covers the scope-auth scopes, the validation plugin, and the registered scalar and comparison types.

Two Prerequisites #

Custom fields do not reach the served schema by themselves:

  1. Import your module before startServer(). The server discovers schema modules by globbing the generated query-schema / mutation-schema files under its schema path. It does not import arbitrary files. A module like src/custom-fields.ts is only included if your entry point imports it before starting the server, or if you name it to match the glob.
    1
    2
    3
    import "./custom-fields" // registers the builder fields
    await server.startServer()
    
  2. The Mutation root type exists only when a generated mutation-schema file is present. If your project has no generated mutations, builder.mutationField(...) has no Mutation type to attach to and Pothos throws "Mutation type not defined". Add a generated mutation first, or register the mutation type yourself.

Authorization #

Declare the scopes a custom field requires through Pothos's authScopes field option. For example, require an authenticated user, or a read permission on a particular table. See scope authorization.

Validation #

Attach a Zod schema to an argument via its validate option to validate input before the resolver runs. See input validation.

Accessing Data #

Resolvers reach the runtime through the GraphQL context. It exposes getReadStrategy(name) and getWriteStrategy(name). There is no context.runtime.

1
2
3
4
5
6
7
8
9
10
11
builder.queryField("orderCount", (t) =>
  t.int({
    resolve: async (_root, _args, context) => {
      const strategy = context.getReadStrategy("warehouse")
      // use the strategy to read, the same path generated resolvers take
      // ...
      return count
    },
  })
)

Previous page

Customization

Next page

Extending Core