Custom resolvers add new root fields. Sometimes you instead want a field on a type you already pulled. You can add a computed displayName to a generated Order, or a related field that reaches another source.
This guide walks through one worked task: add a computed displayName to a generated Order type.
pull recursively deletes and recreates
the endpoint's per-table generated directory
(schemas/generated/<dataSource>/<catalog>/<schema>/<table>). Anything you put
in that directory is lost on the next pull. Keep your extensions in your own
modules.Find the generated reference
Locate the generated object reference for the type you want to extend, such as OrderRef. It lives in the generated files for that table under schemas/generated. You import it to tell the builder which type the new field belongs to.
Create your own module
Put the extension in a file you own, such as src/extensions.ts. Do not edit the generated files, because a later pull deletes that directory.
1
2
3
// src/extensions.ts
import { builder } from "@lakeql/api/builder"
Add the field
Extend the generated type with builder.objectField. The resolver receives the parent object, so you can compute the value from its fields.
1
2
3
4
5
6
7
8
9
// src/extensions.ts
import { builder } from "@lakeql/api/builder"
builder.objectField(OrderRef, "displayName", (t) =>
t.string({
resolve: (order) => `#${order.id} — ${order.status}`,
})
)
The field participates in the same schema as the generated ones. It shares the scope-auth scopes, the scalars, and the validation plugin. See extending core for the full objectField mechanics.
Import the module before you start the server
Add the import to your entry point, before the call to startServer(). The server only registers modules it imports.
1
2
3
4
import "./extensions"
await server.startServer()
See the two prerequisites for the discovery rule.
Run it
Start the dev server and query the new field.
pnpm dev1
2
3
4
5
6
7
8
query {
orders {
id
status
displayName
}
}
Reach another source #
A field on a generated type can also read from a different source. Resolve it through the context accessor, the same one custom resolvers use.
1
2
3
4
5
6
7
8
9
10
11
builder.objectField(OrderRef, "customer", (t) =>
t.field({
type: CustomerRef,
resolve: async (order, _args, context) => {
const strategy = context.getReadStrategy("crm")
// read the related record with the strategy
// ...
},
})
)
This keeps the join in your own module, so a later pull leaves it untouched.