Many platforms ship a GraphQL layer of their own. When that layer covers a single source and the consumers of the API own their own client typing, the built-in option is often enough. LakeQL earns its place when a project spans several sources, needs a typed client workflow, or needs an authorization layer that it owns.
This page frames that trade-off honestly. It starts with the value that holds across every adapter, then narrows to a per-adapter comparison.
What LakeQL Adds Across Adapters #
LakeQL is a universal data access layer. The same four properties hold whether a data source points at Trino or Microsoft Fabric.
-
One unified schema across sources.
A project registers several named
dataSourcesinlakeql.config.tsand serves them through one GraphQL endpoint. The sources can mix engines, so a Trino source and a Fabric source live in the same schema. A consumer configures one endpoint rather than one per source. - A typed codegen workflow. The CLI introspects each source and writes typed artifacts into the repository: TypeScript interfaces, GraphQL query schemas, and the configuration that registers each endpoint. The typed layer is committed and regenerated when a table changes, so client typing comes from the same pull that builds the schema.
-
A self-hosted application.
createLakeQLAppcomposes a server from explicit building blocks that deploy anywhere a Node.js service runs. The runtime, not a hosted control plane, owns the data-source connections. - An authorization layer in front of the engine. LakeQL checks per-table read and write permissions before a resolver runs, then resolves the identity the query runs under. See the authorization model and permissions .
LakeQL and the Native Fabric API for GraphQL #
Microsoft Fabric includes an API for GraphQL that auto-generates a schema from attached sources. It is a capable managed service, and for a single Fabric source consumed by clients that supply their own typing, it covers the common case. This comparison is about scope, not quality. Details on the native service come from Microsoft Learn.
| Capability | LakeQL | Native Fabric API for GraphQL |
|---|---|---|
| Sources behind one endpoint | Several dataSources, mixing engines | One GraphQL item per set of attached sources; Microsoft recommends multiple items for multiple sources |
| Supported engines | Trino and Fabric today, adapter-agnostic | Fabric Data Warehouses, Lakehouses, Mirrored Databases, Fabric SQL Databases, and Azure SQL Databases |
| Client typing | Typed artifacts generated by the CLI and committed | Server-side schema; client typing left to the consumer |
| Hosting | Self-hosted application deployed anywhere | Managed service inside Fabric |
| Authentication | Per-user pass-through identity and service-principal auth | Per-user pass-through identity and service-principal auth |
| Writes | Mutations where the adapter and source support them | Queries and mutations, with read-only sources behind SQL analytics endpoints |
| Authorization | LakeQL permission rules compose with the engine's own access control | The source's own access control |
A few rows deserve context so the table reads fairly.
Authentication is equivalent. Both sides support a caller that is a user principal or a service principal, and both can pass the caller's identity through to the data source. The native service uses Microsoft Entra ID with single sign-on or saved credentials; LakeQL resolves identity through its impersonation model, and a Fabric source carries the on-behalf-of flow on its connection. Neither side holds an advantage here.
Writes depend on the source on both sides. The native service supports queries and mutations, but sources exposed through SQL analytics endpoints, such as Lakehouses and Mirrored Databases, are read-only, and auto-generated mutations on a Warehouse need a primary key. LakeQL supports mutations where the adapter and source support them. Confirm the write path for the source in question before relying on it.
Resolvers. The native service does not expose customizable resolvers; custom logic is handled through stored procedures. LakeQL composes its schema from generated endpoints and supports custom resolvers in the application.
The native service also documents operational limits a reader should size against: a default page of 100 rows up to 100,000 paginated, a 64 MB response ceiling, a 100-second request timeout, a maximum query depth of 10, and up to 1,000 attached objects per GraphQL item. The 1,000-object-per-item limit is the reason Microsoft's own guidance points to multiple GraphQL items for multiple sources, which is the point the first table row compares against.
Consolidating Several Fabric Sources #
The clearest Fabric case for LakeQL is a project that spans several Fabric workspaces. Each workspace, warehouse, or lakehouse becomes a separate Fabric data source with its own endpoint and credentials, and the sources merge into one LakeQL schema. A consumer then configures one endpoint instead of one per source.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42// lakeql.config.ts
import { defineConfig } from "@lakeql/config"
import { fabricAdapterDefinition } from "@lakeql/adapter-fabric"
import { env } from "./src/env"
export default defineConfig({
adapters: [fabricAdapterDefinition],
dataSources: {
salesReporting: {
adapter: "fabric",
namePrefix: "sales",
connection: {
host: env.FABRIC_ENDPOINT_WS1,
port: 1433,
catalog: "sales_warehouse",
auth: {
type: "oauth2",
tenantId: "...",
clientId: "...",
clientSecret: "...",
},
},
},
financeReporting: {
adapter: "fabric",
namePrefix: "finance",
connection: {
host: env.FABRIC_ENDPOINT_WS2,
port: 1433,
catalog: "finance_warehouse",
auth: {
type: "oauth2",
tenantId: "...",
clientId: "...",
clientSecret: "...",
},
},
},
},
})
Each source carries its own host, catalog, and auth, driven by separate environment variables, so the two workspaces stay fully independent. Generated files are organized by data source, so tables never collide on disk. Two sources that expose a table with the same schema.tableName would otherwise produce colliding GraphQL names, so set a namePrefix on each source to disambiguate them. The prefix is the mechanism that keeps a merged, multi-workspace schema unambiguous.
LakeQL and Direct Trino Access #
Trino has no GraphQL layer of its own, so the comparison here is different from the Fabric one. The alternative to LakeQL is reaching the Trino coordinator directly: through an ODBC or JDBC driver, or by calling the Trino REST API from application code. LakeQL stands in for the work that path requires, and the following points describe that work rather than a shortcoming in Trino.
| Capability | LakeQL | Direct Trino access |
|---|---|---|
| Interface | GraphQL over HTTP | SQL over the REST API, or an ODBC/JDBC driver |
| Driver dependency | None; the adapter is an HTTP client over the REST API | A driver to install and maintain, itself an HTTP client over the same REST API |
| Result handling | Pagination and column decoding handled by the client | Caller follows nextUri pages and decodes column-oriented rows |
| Query authoring | SQL generated from the GraphQL query | Caller writes and maintains SQL |
| Authentication | Request-scoped identity resolved by LakeQL in front of the connection | Driver or REST credentials supplied per connection |
Three points carry most of the difference.
No driver to maintain. The Trino adapter reaches the coordinator through an HTTP client over the Trino REST API, so a project adds no ODBC or JDBC driver. The honest nuance: those drivers are themselves HTTP clients over the same REST API, so the saving is one fewer dependency to install and track, not a protocol-level gain.
No hand-built result handling. A Trino query returns results across several pages linked by a nextUri, with rows delivered as positional arrays alongside separate column metadata. The client follows that pagination to completion and maps each positional row onto its column names, so application code receives keyed records. A direct REST consumer writes that pagination loop and column decoding.
SQL comes from the GraphQL query. The Trino endpoint accepts SQL and nothing else, so a direct consumer authors and maintains the SQL for every query. LakeQL compiles the SQL from the incoming GraphQL query, so the GraphQL schema is the interface a consumer works against.
Authentication differs from the Fabric case and the table reflects that. Direct Trino access authenticates with the credentials the driver or REST call carries. LakeQL resolves a request-scoped identity in front of the connection and can impersonate the caller against Trino. See Trino user impersonation for how that identity reaches the coordinator.
See Getting Started to scaffold a project and the data-source overview for adapter details.