@microsoft/fabric-app-data
v1.1.0
Published
Fabric Apps - Analytics SDK — auth-agnostic client for Semantic Models, Lakehouses, and Warehouses
Readme
@microsoft/fabric-app-data
Auth-agnostic SDK for querying Fabric Semantic Models, Lakehouses, and Warehouses
Quick Reference
Package:
@microsoft/fabric-app-dataPurpose: Auth-agnostic SDK for querying Fabric Semantic Models, Lakehouses, and Warehouses. Use when: You need to query data from Microsoft Fabric. This is the core SDK — it handles client creation, query execution, result caching, and error handling. Do NOT use when: You need a proxy implementation — use@microsoft/fabric-app-data-cli-proxy(Node.js local dev) or@microsoft/fabric-app-data-proxy(embedded iframe). This package requires one of those proxies to function. Key exports:FabricClient,SemanticModelClient,IFabricApiProxy,QueryResult,CachedQueryResult,FabricProxyErrorPeer dependencies: None (but requires anIFabricApiProxyimplementation at runtime) Install:npm install @microsoft/fabric-app-data
Ecosystem Context
@microsoft/fabric-app-data is the core SDK package for Fabric Apps - Analytics. It defines the client classes, query/result types, proxy contracts, and proxy error contract, but it does not perform auth or HTTP/TDS transport itself.
You must inject an IFabricApiProxy implementation:
@microsoft/fabric-app-data-cli-proxy— local development and testing; calls Fabric APIs directly from Node.js with a token provider@microsoft/fabric-app-data-proxy— embedded scenarios; forwards calls throughpostMessagevia the embed client/iframe boundary
It is typically used alongside:
@microsoft/fabric-visuals— render semantic-model query results as Vega/Vega-Lite visuals@microsoft/fabric-datagrid— render the same tabular results in a grid
Installation
npm install @microsoft/fabric-app-dataPublic API
Client classes
FabricClient
export class FabricClient {
constructor(config: FabricClientConfig);
semanticModel(alias: string): SemanticModelClient;
clearCache(): void;
}Notes:
semanticModel(alias)returns a cachedSemanticModelClientinstance for the alias.FabricClientConfig.semanticModelsis currently consumed by the exported client API.FabricConfigandIFabricApiProxyalready define lakehouse and warehouse contracts for proxy implementations.daxProtocoldefaults to"json".
SemanticModelClient
export class SemanticModelClient {
constructor(
alias: string,
ref: FabricItemRef,
daxExecutor: DaxExecutor,
cacheStore: QueryCacheStore | null,
protocol: DaxProtocol,
);
query(dax: string, options?: QueryCacheOptions): Promise<CachedQueryResult>;
clearCache(): void;
}Notes:
- In normal usage, obtain this class from
fabric.semanticModel(alias). query()selects Arrow vs JSON from the parentFabricClient'sdaxProtocol.query()returns a result object and does not throw for query/API/network failures.
Types and interfaces
Connection model
export type WorkspaceId = "me" | (string & {});export interface FabricItemRef {
workspaceId: WorkspaceId;
itemId: string;
}export interface FabricConfig {
semanticModels?: Record<string, FabricItemRef>;
lakehouses?: Record<string, FabricItemRef>;
warehouses?: Record<string, FabricItemRef>;
}export type DaxProtocol = "arrow" | "json";export interface FabricClientConfig extends FabricConfig {
proxy: IFabricApiProxy;
cache?: QueryCacheConfig;
daxProtocol?: DaxProtocol;
}Proxy contracts
export interface DaxProxyResponse {
data: ArrayBuffer;
requestId: string;
}export interface DaxJsonProxyResponse {
data: unknown;
requestId: string;
}export interface SqlProxyResponse {
columns: Array<{ name: string; dataType: string }>;
rows: unknown[][];
requestId: string;
}export interface ISemanticModelApiProxy {
executeDax(
workspaceId: WorkspaceId,
itemId: string,
query: string,
options?: DaxQueryOptions,
): Promise<DaxProxyResponse>;
executeDaxJson(
workspaceId: WorkspaceId,
itemId: string,
query: string,
): Promise<DaxJsonProxyResponse>;
}export interface ILakehouseApiProxy {
executeSql(
workspaceId: WorkspaceId,
itemId: string,
sql: string,
params?: SqlParameters,
options?: SqlQueryOptions,
): Promise<SqlProxyResponse>;
}export interface IWarehouseApiProxy {
executeSql(
workspaceId: WorkspaceId,
itemId: string,
sql: string,
params?: SqlParameters,
options?: SqlQueryOptions,
): Promise<SqlProxyResponse>;
}export interface IFabricApiProxy {
semanticModel: ISemanticModelApiProxy;
lakehouse: ILakehouseApiProxy;
warehouse: IWarehouseApiProxy;
}Query and cache types
export interface DaxQueryOptions {
culture?: string;
schemaOnly?: boolean;
queryTimeout?: number;
resultSetRowCountLimit?: number;
}export interface QueryColumn {
name: string;
dataType: string;
}export interface QueryTable {
columns: QueryColumn[];
rows: unknown[][];
}export interface QueryError {
category: "api" | "query" | "network" | "overflow" | "unknown";
message: string;
code?: string;
details?: string;
}export type QueryResult =
| { status: "success"; table: QueryTable; requestId: string }
| { status: "error"; error: QueryError; requestId: string };export type CachedQueryResult = QueryResult & {
fromCache: boolean;
cachedAt?: Date;
};export interface QueryCacheOptions {
bypassCache?: boolean;
}export interface QueryCacheConfig {
maxEntries?: number;
enabled?: boolean;
}export interface SqlQueryOptions {
queryTimeout?: number;
}export type SqlParameters = Record<string, string | number | boolean | null>;Error types
export type FabricProxyError =
| FabricApiProxyError
| FabricNetworkProxyError
| FabricGenericProxyError;export class FabricApiProxyError extends Error {
readonly status: number;
readonly requestId: string;
readonly sessionId: string;
readonly body: string;
constructor(opts: {
status: number;
requestId: string;
sessionId: string;
body: string;
message?: string;
});
}export class FabricNetworkProxyError extends Error {
readonly requestId: string;
readonly sessionId: string;
readonly details: string;
readonly cause?: unknown;
constructor(opts: {
requestId: string;
sessionId: string;
message: string;
details?: string;
cause?: unknown;
});
}export class FabricGenericProxyError extends Error {
readonly requestId?: string;
readonly sessionId?: string;
readonly cause?: unknown;
constructor(opts: {
message: string;
requestId?: string;
sessionId?: string;
cause?: unknown;
});
}Protocol and contract behavior
These behavior contracts are public even where the implementation classes are internal:
IFabricApiProxy.semanticModel.executeDax()returns Arrow IPC bytes inDaxProxyResponse.dataIFabricApiProxy.semanticModel.executeDaxJson()returns rawexecuteQueriesJSON inDaxJsonProxyResponse.dataSemanticModelClient.query()chooses which proxy method to call fromFabricClientConfig.daxProtocol- Arrow responses are parsed in-package, including LZ4-compressed Arrow streams
QueryResult may be an error — always check status
query() returns a QueryResult discriminated union. Query failures, API errors, and network errors are returned as { status: "error" } rather than thrown — so you must always check result.status.
// ✅ DO: Always check result.status — it may be an error
const result = await fabric.semanticModel("sales").query("EVALUATE ...");
if (result.status === "success") {
console.log(result.table.rows);
} else {
// result.status === "error"
// result.error.category is one of:
// "query" — DAX syntax or semantic error
// "api" — Fabric service rejected the request
// "network" — fetch failed, timed out, or connection was lost
// "overflow" — numeric value exceeded safe JS precision
// "unknown" — unrecognized failure
console.error(result.error.category, result.error.message);
}
// ❌ DON'T: Assume a returned result is successful without checking statusKey Constraints & Gotchas
A proxy is always required
// ✅ DO: Always provide a proxy implementation
import { FabricClient } from "@microsoft/fabric-app-data";
import { DirectFabricApiProxy } from "@microsoft/fabric-app-data-cli-proxy";
const fabric = new FabricClient({ proxy: new DirectFabricApiProxy(tokenProvider), semanticModels: { ... } });
// ❌ DON'T: Try to create a FabricClient without a proxy
const fabric = new FabricClient({ semanticModels: { ... } }); // Error: proxy is requiredCurrent exported client surface is semantic-model focused: FabricConfig and IFabricApiProxy include lakehouse and warehouse contracts, but the exported FabricClient currently exposes semanticModel(alias) only.
Check result.status instead of try/catch
// ✅ DO: Check the discriminated union
const result = await fabric.semanticModel("sales").query("EVALUATE ...");
if (result.status === "success") {
console.log(result.table.rows);
} else {
console.error(result.error.category, result.error.message);
}
// ❌ DON'T: Wrap in try/catch expecting thrown errors
try {
const result = await fabric.semanticModel("sales").query("EVALUATE ...");
} catch (e) { /* query() does not throw for query/API/network failures */ }JSON is the default DAX protocol
// ✅ DO: Explicitly set daxProtocol if you want Arrow
const fabric = new FabricClient({ proxy, daxProtocol: "arrow", semanticModels: { ... } });
// Default behavior (no daxProtocol) uses "json"Results are single-table
- Each
query()call executes oneEVALUATEstatement and returns onetable. result.table(notresult.tables) contains{ columns: QueryColumn[], rows: unknown[][] }.- The success payload is already in the same row-major
columns+rowsshape used across the visuals stack, soresult.tablecan feed rendering pipelines built around@microsoft/fabric-visuals-core/@microsoft/fabric-datagrid.
Cache behavior
- Results are cached in-memory by default (LRU, max 64 entries).
- Cache keys are derived from
(alias, query, protocol, options). bypassCache: trueskips reading from cache but still writes the fresh result to cache.- Only
"success"and"query"errors are cached;"api","network","unknown"errors are not. fabric.clearCache()clears all aliases;fabric.semanticModel("x").clearCache()clears only that alias.
Arrow vs JSON type fidelity
- Arrow: typed columns (
Int64,Double,Decimal,Boolean,String,DateTime). Int64/Decimal →number, Date/DateTime → ISO string without trailingZ. - JSON: all columns have
dataType: "unknown"because the legacy endpoint lacks type metadata. - Unsafe numeric overflows return
error.category === "overflow"instead of corrupting data.
workspaceId supports My Workspace
// ✅ Both are valid
{ workspaceId: "me", itemId: "..." } // My Workspace
{ workspaceId: "guid-here", itemId: "..." } // Named workspaceMinimal Usage Examples
Create a client with an injected proxy
import { FabricClient, type IFabricApiProxy } from "@microsoft/fabric-app-data";
declare const proxy: IFabricApiProxy;
const fabric = new FabricClient({
proxy,
semanticModels: {
sales: {
workspaceId: "me",
itemId: "00000000-0000-0000-0000-000000000000",
},
},
});Run a semantic-model query
const result = await fabric.semanticModel("sales").query("EVALUATE ROW(\"x\", 1)");
if (result.status === "success") {
console.log(result.table.columns);
console.log(result.table.rows);
} else {
console.error(result.error.category, result.error.message);
}Opt into Arrow and bypass cache for a fresh read
const fabric = new FabricClient({
proxy,
daxProtocol: "arrow",
semanticModels: {
sales: { workspaceId: "me", itemId: "00000000-0000-0000-0000-000000000000" },
},
});
const fresh = await fabric.semanticModel("sales").query("EVALUATE ROW(\"x\", 1)", {
bypassCache: true,
});Trademarks
This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft trademarks or logos is subject to and must follow Microsoft's Trademark & Brand Guidelines. Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. Any use of third-party trademarks or logos are subject to those third-party's policies.
Security
Microsoft takes the security of our software products and services seriously, which includes all source code repositories in our GitHub organizations.
Please do not report security vulnerabilities through public GitHub issues.
For security reporting information, locations, contact information, and policies, please review the latest guidance for Microsoft repositories at https://aka.ms/SECURITY.md.
Code of conduct
We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
