# 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.

- Skill: `microsoft/fabric-sdk` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add microsoft/fabric-sdk`
- Raw SKILL.md: https://api.skillmd.com/api/skills/microsoft/fabric-sdk/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs
- Author: Microsoft (https://skillmd.com/u/microsoft)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/microsoft/fabric-sdk

---


# Fabric SDK Usage

## Overview

`@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).

## Quick Start

```typescript
import { FabricClient } from "@microsoft/fabric-app-data";

const client = new FabricClient({
  proxy,                               // provided by host environment
  semanticModels: {
    sales: { workspaceId: "...", itemId: "..." },
  },
});

const result = await client.semanticModel("sales").query(
  "EVALUATE SUMMARIZECOLUMNS(Product[Category], \"Total\", [Sales Amount])"
);

if (result.status === "success") {
  // result.table.columns — [{name, dataType}]
  // result.table.rows    — unknown[][]
} else {
  // result.error.category — "api" | "query" | "overflow" | "network" | "unknown"
  // result.error.message
}
```

## Key Concepts

### FabricClient Configuration

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:

```typescript
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)
  },
});
```

- `workspaceId` can be a GUID or `"me"` for My Workspace items.

### Managing Connections

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.

```typescript
const client = new FabricClient({
  proxy,
  semanticModels: {
    sales: { workspaceId: "00c98f7c-...", itemId: "03f2dc11-..." },
  },
});
```

### Querying

There is **one method** for DAX queries:

```typescript
const result = await client.semanticModel("alias").query(dax);

// To skip the cache:
const fresh = await client.semanticModel("alias").query(dax, { bypassCache: true });
```

### Result Handling

**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 metadata
- `rows`: `unknown[][]` — row-major array, values match column order by index

```typescript
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"
}
```

### Caching

Results are cached in memory by default (LRU, 64 entries).

```typescript
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
```

**What gets cached:**
- ✅ Success results
- ✅ Query errors (bad DAX — won't fix itself)
- ❌ API errors (401, 500 — transient)
- ❌ Network errors (transient)

### Error Categories

| 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 |

### Data Types and DateTime Semantics

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.

```typescript
// DateTime column values look like:
"2024-01-15T10:30:00.000"   // no timezone — interpret as-is
"2023-06-01T00:00:00.000"
```

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.

## Common Patterns

### Multiple Models

```typescript
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 ...");
```

### My Workspace Items

```typescript
semanticModels: {
  myModel: { workspaceId: "me", itemId: "..." },
}
```

### Disabling Cache

```typescript
new FabricClient({
  proxy,
  cache: { enabled: false },
  semanticModels: { ... },
});
```

## Anti-Patterns

- **Don't catch errors from `query()`** — it never throws. Check `result.status`.
- **Don't hardcode endpoint URLs** — the proxy handles transport.
- **Don't construct `SemanticModelClient` directly** — use `client.semanticModel(alias)`.
- **Don't import from internal paths** — only import from `"@microsoft/fabric-app-data"`.

## Type Reference

All types are exported from the `"@microsoft/fabric-app-data"` package entry point.

Key types to import when needed:
```typescript
import type {
  FabricClientConfig,
  FabricItemRef,
  QueryResult,
  CachedQueryResult,
  QueryCacheOptions,
  QueryTable,
  QueryColumn,
  QueryError,
} from "@microsoft/fabric-app-data";
```

For full type definitions, see `references/types.md` in this skill.

