This guide takes a configured Trino data source all the way to a working, write-enabled endpoint. You set writeConfig, author the endpoint definition, generate the files, and grant the matching permission.
Writes are Trino-only today. The Fabric adapter's capability table lists Writes as "No — read-only adapter." See Microsoft Fabric for its capability table.
Choose a Write Strategy #
The Trino adapter offers two write strategies, selected by the data source's writeConfig. Pick one before you define the endpoint:
| Strategy | When to use it |
|---|---|
| INSERT | No object storage available, or small/transactional batches; writes via INSERT INTO. |
| Storage | Large or append-heavy loads into object storage; serializes to Parquet and registers via DDL. |
For the mechanics every write-capable adapter shares, see Writing. For the full option list and the storage-strategy snippet, see Write Config.
This walkthrough uses INSERT, because it needs no object-storage infrastructure and keeps every step self-contained. Declare the data source the rest of this guide references by name, with an INSERT writeConfig:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20import { defineConfig } from "@lakeql/config"
import { trinoAdapterDefinition } from "@lakeql/adapter-trino"
export default defineConfig({
sourcePath: "src",
adapters: [trinoAdapterDefinition],
dataSources: {
warehouse: {
adapter: "trino",
connection: {
host: "https://trino.example.com",
port: 8080,
catalog: "hive",
auth: { type: "basic", username: "svc", password: "..." },
},
writeConfig: { strategy: "insert", batchSize: 500 },
},
},
})
For the storage strategy the shape is identical. You swap writeConfig for the object-storage form. See Write Config for that exact snippet and the full option list.
Author the Endpoint Definition #
Write an endpoint.json that sets dataSource explicitly and adds a mutation block. The schema requires both loadStrategy and basePath, even for the INSERT strategy. INSERT ignores basePath, but the definition does not validate without it.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"version": "1.0",
"catalog": "hive",
"schema": "sales",
"tableName": "orders",
"dataSource": "warehouse",
"fields": [
{ "name": "id", "type": "Integer", "options": { "required": true } },
{ "name": "status", "type": "String", "options": { "required": true } }
],
"mutation": {
"loadStrategy": "append",
"basePath": "sales/orders"
}
}
See Mutations for the full mutation field table and load-strategy definitions.
Generate the Files #
Generate the endpoint from the definition with create-endpoint:
pnpm lakeql-cli create-endpoint --from-file ./endpoint.jsonThe generated files land under schemas/custom/<catalog>/<schema>/<table>/, here schemas/custom/hive/sales/orders/.
Grant a Mutation Permission #
Writes are denied by default, so add a positive Mutation rule scoped to the data source and table you generated. It uses the same createPermission/rule builder as a Query rule, grouped under Mutation:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { createPermission } from "@lakeql/api/helpers"
import type { Permission } from "@lakeql/config"
export const permissions: Permission[] = [
{
name: "ingestion-service",
useSystemUser: false,
permissions: {
Query: [],
Mutation: [
createPermission("warehouse", (rule) => [
rule({ schema: "sales", tables: ["orders"] }),
]),
],
},
},
]
See Granting Write Access for the shape and the deny-by-default default.
Replace a Pulled Endpoint with a Mutation-Enabled One #
If you already ran pull for this table, you have a read-only endpoint under schemas/generated/<dataSource>/<catalog>/<schema>/<table>/. The preceding steps added a mutation-enabled custom one under schemas/custom/<catalog>/<schema>/<table>/. Follow these steps to avoid registering the same table twice:
Add the data source back to the pulled definition
pull does not serialize the dataSource it used into endpoint.json. Add it back by hand before you generate the custom endpoint. See the <Warning> on Definition Format.
Remove the pulled directory
After you generate the custom endpoint, delete the old schemas/generated/<dataSource>/<catalog>/<schema>/<table>/ directory, or avoid re-running pull for that table. The server imports every query-schema.ts/mutation-schema.ts under schemas/**. The GraphQL field name comes only from schema + tableName, which is identical for the pulled and custom versions of the same table. So leaving both in place registers the same field name twice.
Rebuild the registry
Re-run create-registry, or let create-endpoint run it by default, so config-registry.ts reflects only the surviving custom entry.
Where to Next #
How LakeQL persists data — the two choices that define a write.
The two writeConfig shapes the Trino adapter supports — INSERT and external storage.
The optional mutation block and its load strategies.
Write data back through LakeQL.
Grant technical users scoped read and write access per table.