npx skills add ...
npx skills add microsoft/fabric-apps-analytic-templates --skill fabric-sdk
npx skills add microsoft/fabric-apps-analytic-templates --skill fabric-sdk
How to use @microsoft/fabric-app-data to connect web applications to Fabric data sources at runtime. Build browser-based apps that query semantic models via DAX using the FabricClient API.
@microsoft/fabric-app-data lets web applications query Fabric semantic models at
runtime. The SDK provides a FabricClient that handles querying, result
parsing, caching, and error handling. Transport and authentication are
delegated to an IFabricApiProxy implementation, keeping the SDK
environment-agnostic (browser, Node.js, Fabric extensions).
The FabricClient is constructed with a config object. The proxy and
connection details are typically provided by the host environment — your
code just needs to pass them through:
workspaceId can be a GUID or "me" for My Workspace items.The FabricClient accepts connection config as a plain object. How you
manage those IDs (environment variables, config files, codegen) is up to
your project. The only requirement is that workspaceId and itemId
are provided for each named connection.
There is one method for DAX queries:
Always check result.status — queries never throw.
Each query must contain exactly one EVALUATE statement. On success, the
result contains a single table with:
columns: Array<{ name: string, dataType: string }> — column metadatarows: unknown[][] — row-major array, values match column order by indexResults are cached in memory by default (LRU, 64 entries).
What gets cached:
| Category | Meaning | Cached? | Example |
|---|---|---|---|
query | Invalid DAX syntax | Yes | "Syntax error at position 18" |
overflow | Integer/decimal exceeds safe range | Yes | Value > MAX_SAFE_INTEGER |
api | HTTP error from Fabric | No | 401 Unauthorized, 500 Server Error |
network | Connection failure | No | DNS resolution, timeout |
unknown | Unexpected error | No | Parse failure |
The SDK converts all DAX data types to standard JS values:
| DAX type | JS value type | Example |
|---|---|---|
| Integer (Int64) | number | 42 |
| Double (Float64) | number | 3.14 |
| Currency/Decimal | number | 100.50 |
| Boolean | boolean | true |
| String | string | "hello" |
| DateTime/Date | string (ISO) | "2024-01-15T10:30:00.000" |
| BLANK | null | null |
DateTime values are returned as ISO 8601 strings without a timezone
suffix (no Z, no ±HH:MM). This matches the semantics of Analysis
Services semantic models, where datetimes are timezone-unaware.
This format is directly usable as a temporal type in charting libraries
and ensures consistent display across all browser timezones. The JSON and
Arrow protocols both return the same format.
Integer overflow: If an integer value exceeds ±Number.MAX_SAFE_INTEGER
(±9,007,199,254,740,991), the query returns an error with
category: "overflow" rather than silently losing precision.
query() — it never throws. Check result.status.SemanticModelClient directly — use client.semanticModel(alias)."@microsoft/fabric-app-data".All types are exported from the "@microsoft/fabric-app-data" package entry point.
Key types to import when needed:
For full type definitions, see references/types.md in this skill.
new FabricClient({
proxy, // Provided by host environment
semanticModels: { // Named connections
alias: { workspaceId, itemId },
},
cache: { // Optional
enabled: true, // Default: true
maxEntries: 64, // Default: 64 (LRU eviction)
},
});const client = new FabricClient({
proxy,
semanticModels: {
sales: { workspaceId: "00c98f7c-...", itemId: "03f2dc11-..." },
},
});const result = await client.semanticModel("alias").query(dax);
// To skip the cache:
const fresh = await client.semanticModel("alias").query(dax, { bypassCache: true });const result = await model.query("EVALUATE ...");
if (result.status === "success") {
// result.table.columns = [{ name: "Product[Name]", dataType: "String" },
// { name: "[Sales]", dataType: "Int64" }]
// result.table.rows = [["Widget", 42], ["Gadget", 17]]
// result.table.rows[0][0] → "Widget" (matches columns[0])
// result.table.rows[0][1] → 42 (matches columns[1])
} else {
console.error(result.error.message);
// result.error.category: "query" (bad DAX), "overflow" (value too large),
// "api" (HTTP error), "network" (connectivity), "unknown"
}const r1 = await model.query("EVALUATE T"); // r1.fromCache === false
const r2 = await model.query("EVALUATE T"); // r2.fromCache === true
// Check cache age
if (r2.fromCache && r2.cachedAt) {
const ageMs = Date.now() - r2.cachedAt.getTime();
}
// Force fresh result
const fresh = await model.query("EVALUATE T", { bypassCache: true });
// Clear cache
client.clearCache(); // all sub-clients
model.clearCache(); // this model only// DateTime column values look like:
"2024-01-15T10:30:00.000" // no timezone — interpret as-is
"2023-06-01T00:00:00.000"const client = new FabricClient({
proxy,
semanticModels: {
sales: { workspaceId: "ws-1", itemId: "item-1" },
inventory: { workspaceId: "ws-2", itemId: "item-2" },
},
});
const salesResult = await client.semanticModel("sales").query("EVALUATE ...");
const invResult = await client.semanticModel("inventory").query("EVALUATE ...");semanticModels: {
myModel: { workspaceId: "me", itemId: "..." },
}new FabricClient({
proxy,
cache: { enabled: false },
semanticModels: { ... },
});import type {
FabricClientConfig,
FabricItemRef,
QueryResult,
CachedQueryResult,
QueryCacheOptions,
QueryTable,
QueryColumn,
QueryError,
} from "@microsoft/fabric-app-data";