@microsoft/rayfin-guide 1.36.0-alpha.1756 → 1.36.0-alpha.1818

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,11 +4,18 @@ sidebar_position: 2
4
4
 
5
5
  # Add a Fabric resource
6
6
 
7
- Connect a function to a **Microsoft Fabric item** — a Lakehouse, Warehouse, SQL Database, OneLake files, or an Eventhouse (KQL) — and call it **as the signed-in user**.
7
+ Connect a deployed function to a **Microsoft Fabric item** — a Lakehouse, Warehouse, SQL Database, or OneLake files.
8
8
 
9
- **Before you start:** you need the item's coordinates (SQL endpoint, OneLake path, or Kusto query URI). See [Get Fabric info](./get-fabric-info.md) for how to pull them from the Fabric REST API.
9
+ For the app's Rayfin DB, use [`ctx.getDataClient()`](../writing-functions.md#accessing-data-and-request-context) instead.
10
10
 
11
- All the patterns below follow the same model: declare the connection, read the token with `ctx.getToken()`, use it. See [Connecting to external resources](./index.md) for the shared model and the [`ContextTokenCredential`](./index.md#wrapping-the-token-for-azure-sdk-clients) helper referenced here.
11
+ Grant the [application identity](../index.md#application-authentication) the required access to the workspace, item, or database for the operation you need.
12
+
13
+ **Before you start:** you need the item's coordinates (SQL endpoint or OneLake path).
14
+ See [Get Fabric info](./get-fabric-info.md) for how to pull them from the Fabric REST API.
15
+
16
+ All the patterns below follow the same model: declare the audience in the `RayfinContext` annotation, read the token from `ctx.Tokens`, use it. See [Connecting to external resources](./index.md) for the shared model and the [`ContextTokenCredential`](./index.md#wrapping-the-token-for-azure-sdk-clients) helper referenced here.
17
+
18
+ `AppSchema` below is your app's data schema — the same type you pass to `RayfinClient<AppSchema>`. See [Writing functions](../writing-functions.md#accessing-data-and-request-context).
12
19
 
13
20
  ## SQL databases
14
21
 
@@ -16,7 +23,7 @@ Covers Fabric **Lakehouse** (SQL analytics endpoint), **Warehouse**, **SQL Datab
16
23
 
17
24
  - **Package:** `mssql@^12.6.0` (which pulls `tedious >= 19.2.2`). Older `tedious` (`<= 19.1.2`) has a LOGIN7 FeatureExt bug that causes "socket hang up" errors against Fabric endpoints.
18
25
  - **Encryption:** `encrypt: true` (not `'strict'`). This matches ODBC `Encrypt=yes`.
19
- - **Auth:** `azure-active-directory-access-token` with the token from `ctx.getToken(AudienceType.Sql)`.
26
+ - **Auth:** `azure-active-directory-access-token` with the token from `ctx.Tokens.Sql`.
20
27
 
21
28
  Install the driver in the functions project:
22
29
 
@@ -52,10 +59,10 @@ const DATABASE = "<item-guid-or-db-name>";
52
59
  udf.func(
53
60
  "queryData",
54
61
  async (
55
- ctx: RayfinContext,
62
+ ctx: RayfinContext<AppSchema, AudienceType.Sql>,
56
63
  query: string,
57
64
  ): Promise<Record<string, unknown>[]> => {
58
- const token = ctx.getToken(AudienceType.Sql);
65
+ const token = ctx.Tokens.Sql;
59
66
  const pool = await sql.connect({
60
67
  server: SQL_SERVER,
61
68
  database: DATABASE,
@@ -69,7 +76,7 @@ udf.func(
69
76
  await pool.close();
70
77
  return result.recordset;
71
78
  },
72
- [udf.connection({ audienceType: AudienceType.Sql })],
79
+ [],
73
80
  );
74
81
  ```
75
82
 
@@ -79,7 +86,7 @@ Read and write files in a Lakehouse's OneLake storage using `AudienceType.Storag
79
86
 
80
87
  The OneLake DFS URL has the form `https://onelake.dfs.fabric.microsoft.com/<workspaceId>/<itemId>/Files/<path>` — get it from `oneLakeFilesPath` via [Get Fabric info](./get-fabric-info.md#lakehouse), or construct it from the workspace and item GUIDs.
81
88
 
82
- Call the DFS endpoint directly with the delegated token — no SDK required:
89
+ Call the DFS endpoint directly with the app-identity token — no SDK required:
83
90
 
84
91
  ```ts
85
92
  import {
@@ -92,8 +99,11 @@ const udf = new UserDataFunctions();
92
99
 
93
100
  udf.func(
94
101
  "readFile",
95
- async (ctx: RayfinContext, fileUrl: string): Promise<string> => {
96
- const token = ctx.getToken(AudienceType.Storage);
102
+ async (
103
+ ctx: RayfinContext<AppSchema, AudienceType.Storage>,
104
+ fileUrl: string,
105
+ ): Promise<string> => {
106
+ const token = ctx.Tokens.Storage;
97
107
  const res = await fetch(fileUrl, {
98
108
  headers: { Authorization: `Bearer ${token}` },
99
109
  });
@@ -102,59 +112,16 @@ udf.func(
102
112
  }
103
113
  return await res.text();
104
114
  },
105
- [udf.connection({ audienceType: AudienceType.Storage })],
115
+ [],
106
116
  );
107
117
  ```
108
118
 
109
- ## Eventhouse and KQL
110
-
111
- Query a Fabric **Eventhouse** (KQL database) or a standalone Azure Data Explorer cluster using `AudienceType.Kusto`.
112
-
113
- Get the cluster's **query URI** from the Eventhouse's `properties.queryServiceUri` — see [Get Fabric info → Eventhouse](./get-fabric-info.md#eventhouse).
114
-
115
- `azure-kusto-data` accepts a token provider, so hand it a callback that returns `ctx.getToken`:
116
-
117
- ```ts
118
- import {
119
- UserDataFunctions,
120
- AudienceType,
121
- type RayfinContext,
122
- } from "@microsoft/fabric-user-data-functions";
123
- import { Client, KustoConnectionStringBuilder } from "azure-kusto-data";
124
-
125
- const udf = new UserDataFunctions();
126
-
127
- // Read these from the Eventhouse item — see "Get Fabric info".
128
- const CLUSTER_URI = "https://<cluster>.z5.kusto.fabric.microsoft.com";
129
- const DATABASE = "<kql-database-name>";
130
-
131
- udf.func(
132
- "queryKusto",
133
- async (ctx: RayfinContext, query: string): Promise<unknown[]> => {
134
- const kcsb = KustoConnectionStringBuilder.withTokenProvider(
135
- CLUSTER_URI,
136
- async () => ctx.getToken(AudienceType.Kusto),
137
- );
138
- const client = new Client(kcsb);
139
- const response = await client.execute(DATABASE, query);
140
- return response.primaryResults[0].toJSON().data;
141
- },
142
- [udf.connection({ audienceType: AudienceType.Kusto })],
143
- );
144
- ```
145
-
146
- Install the SDK in the functions project:
147
-
148
- ```bash
149
- cd rayfin/functions
150
- npm install azure-kusto-data
151
- ```
152
-
153
119
  ## Fabric REST API
154
120
 
155
- Call the [Fabric REST API](https://learn.microsoft.com/en-us/rest/api/fabric/) as the calling user — for example to list items or read item metadata from inside a function — using `AudienceType.Fabric`.
121
+ Call the [Fabric REST API](https://learn.microsoft.com/en-us/rest/api/fabric/) as the app identity — for example to list items or read item metadata from inside a function — using `AudienceType.Fabric`.
122
+ Check that the specific API supports application identities and that the app identity has the required access.
156
123
 
157
- The API base is fixed at `https://api.fabric.microsoft.com/v1`; `ctx.getToken(AudienceType.Fabric)` returns a token scoped for it. Send it as a bearer token with `fetch`:
124
+ The API base is fixed at `https://api.fabric.microsoft.com/v1`; `ctx.Tokens.Fabric` returns a token scoped for it. Send it as a bearer token with `fetch`:
158
125
 
159
126
  ```ts
160
127
  import {
@@ -169,8 +136,11 @@ const FABRIC_API = "https://api.fabric.microsoft.com/v1";
169
136
 
170
137
  udf.func(
171
138
  "listWorkspaceItems",
172
- async (ctx: RayfinContext, workspaceId: string): Promise<unknown> => {
173
- const token = ctx.getToken(AudienceType.Fabric);
139
+ async (
140
+ ctx: RayfinContext<AppSchema, AudienceType.Fabric>,
141
+ workspaceId: string,
142
+ ): Promise<unknown> => {
143
+ const token = ctx.Tokens.Fabric;
174
144
  const res = await fetch(`${FABRIC_API}/workspaces/${workspaceId}/items`, {
175
145
  headers: { Authorization: `Bearer ${token}` },
176
146
  });
@@ -179,8 +149,8 @@ udf.func(
179
149
  }
180
150
  return res.json();
181
151
  },
182
- [udf.connection({ audienceType: AudienceType.Fabric })],
152
+ [],
183
153
  );
184
154
  ```
185
155
 
186
- > This is the same API used in [Get Fabric info](./get-fabric-info.md) — the difference is that there you call it at **authoring time** (with an `az` token) to gather endpoints, whereas here the **function** calls it at runtime with the user's delegated token.
156
+ > This is the same API used in [Get Fabric info](./get-fabric-info.md) — the difference is that there you call it at **authoring time** (with an `az` token) to gather endpoints, whereas here the **deployed function** calls it at runtime with the app identity's token and resource permissions.
@@ -4,10 +4,14 @@ sidebar_position: 4
4
4
 
5
5
  # Add Azure AI Foundry
6
6
 
7
- Call an **Azure AI Foundry** (Azure OpenAI / Azure AI) resource from a function **as the signed-in user**, using `AudienceType.AzureAI`.
7
+ Call an **Azure AI Foundry** (Azure OpenAI / Azure AI) resource from a deployed function using `AudienceType.AzureAI`.
8
+
9
+ Grant the [application identity](../index.md#application-authentication) the permissions required by your Azure AI resource and API.
8
10
 
9
11
  Provide the resource **endpoint** (Azure AI Foundry → your resource → _Endpoint_). The token is a standard bearer token — send it with `fetch`, or wrap it with the [`ContextTokenCredential`](./index.md#wrapping-the-token-for-azure-sdk-clients) helper for an Azure AI SDK client.
10
12
 
13
+ `AppSchema` below is your app's data schema — the same type you pass to `RayfinClient<AppSchema>`. See [Writing functions](../writing-functions.md#accessing-data-and-request-context).
14
+
11
15
  ```ts
12
16
  import {
13
17
  UserDataFunctions,
@@ -22,8 +26,11 @@ const AI_ENDPOINT = "https://<resource>.services.ai.azure.com";
22
26
 
23
27
  udf.func(
24
28
  "callAzureAi",
25
- async (ctx: RayfinContext, prompt: string): Promise<unknown> => {
26
- const token = ctx.getToken(AudienceType.AzureAI);
29
+ async (
30
+ ctx: RayfinContext<AppSchema, AudienceType.AzureAI>,
31
+ prompt: string,
32
+ ): Promise<unknown> => {
33
+ const token = ctx.Tokens.AzureAI;
27
34
  const res = await fetch(`${AI_ENDPOINT}/...`, {
28
35
  method: "POST",
29
36
  headers: {
@@ -37,7 +44,7 @@ udf.func(
37
44
  }
38
45
  return res.json();
39
46
  },
40
- [udf.connection({ audienceType: AudienceType.AzureAI })],
47
+ [],
41
48
  );
42
49
  ```
43
50
 
@@ -4,12 +4,14 @@ sidebar_position: 1
4
4
 
5
5
  # Finding resource coordinates
6
6
 
7
- Every connection recipe needs a real endpoint — the SQL server, the OneLake path, the Key Vault URI, the Kusto query URI, and so on.
7
+ Every connection recipe needs a real endpoint, such as the SQL server or the OneLake path.
8
8
  This page explains **how to obtain those values from a Fabric item** so you (or an agent authoring a function) can fill them in with real data instead of guessing.
9
9
 
10
- **You only need the workspace and item _display names_ to start** — for example, "lakehouseA in workspaceB". Everything else (the workspace ID, item ID, SQL endpoint, OneLake path, Kusto URI) is derivable from here. Resolve those values rather than asking for anything you can look up.
10
+ **You only need the workspace and item _display names_ to start** — for example, "lakehouseA in workspaceB".
11
+ Everything else (the workspace ID, item ID, SQL endpoint, OneLake path) is derivable from here.
12
+ Resolve those values rather than asking for anything you can look up.
11
13
 
12
- > **Why this matters:** don't hardcode a guessed endpoint. `ctx.getToken()` gives you an access token, but you still have to point the SDK at the correct URL — and that URL comes from the Fabric item's metadata, not from `process.env`.
14
+ > **Why this matters:** don't hardcode a guessed endpoint. `ctx.Tokens` gives you an access token, but you still have to point the SDK at the correct URL — and that URL comes from the Fabric item's metadata, not from `process.env`.
13
15
 
14
16
  ## Ways to get coordinates
15
17
 
@@ -36,7 +38,7 @@ az account get-access-token \
36
38
  --query accessToken -o tsv
37
39
  ```
38
40
 
39
- > This is an **authoring-time** lookup — you are gathering coordinates to write into the function. At **runtime** the function itself uses `ctx.getToken(AudienceType.X)` (or, for the Fabric API specifically, `ctx.getToken(AudienceType.Fabric)` — see [Add a Fabric resource → Fabric REST API](./add-fabric-resource.md#fabric-rest-api)).
41
+ > This is an **authoring-time** lookup — you are gathering coordinates to write into the function. At **runtime** the function itself declares the audience on its `RayfinContext` annotation and reads `ctx.Tokens.<Audience>` (for the Fabric API specifically, `ctx.Tokens.Fabric` — see [Add a Fabric resource → Fabric REST API](./add-fabric-resource.md#fabric-rest-api)).
40
42
 
41
43
  ## Step 1 — find the workspace and item ID
42
44
 
@@ -52,7 +54,7 @@ List items of a given type in a workspace (to resolve a display name to its ID):
52
54
  GET https://api.fabric.microsoft.com/v1/workspaces/{workspaceId}/items?type=Lakehouse
53
55
  ```
54
56
 
55
- `type` accepts any Fabric item type — `Lakehouse`, `Warehouse`, `SQLDatabase`, `Eventhouse`, `KQLDatabase`, and so on.
57
+ `type` accepts any Fabric item type — for these recipes, use `Lakehouse`, `Warehouse`, or `SQLDatabase`.
56
58
  Each entry returns `id`, `displayName`, and `type`. Take the `id` of the item you want.
57
59
 
58
60
  ## Step 2 — GET the item to read its coordinates
@@ -120,25 +122,6 @@ GET https://api.fabric.microsoft.com/v1/workspaces/{workspaceId}/sqlDatabases/{s
120
122
 
121
123
  - **Server** → `properties.serverFqdn`, **database** → `properties.databaseName`. Both are also embedded in `properties.connectionString`.
122
124
 
123
- ### Eventhouse
124
-
125
- ```http
126
- GET https://api.fabric.microsoft.com/v1/workspaces/{workspaceId}/eventhouses/{eventhouseId}
127
- ```
128
-
129
- ```json
130
- {
131
- "type": "Eventhouse",
132
- "properties": {
133
- "queryServiceUri": "https://xxxxx.z5.kusto.fabric.microsoft.com",
134
- "ingestionServiceUri": "https://ingest-xxxxx.z5.kusto.fabric.microsoft.com",
135
- "databasesItemIds": ["<kql-database-item-id>"]
136
- }
137
- }
138
- ```
139
-
140
- - **Cluster / query URI** → `properties.queryServiceUri`. Use with [Eventhouse and KQL](./add-fabric-resource.md#eventhouse-and-kql). Each KQL database inside the eventhouse is listed in `databasesItemIds`.
141
-
142
125
  ## Coordinate lookup table
143
126
 
144
127
  | You need | Item type | Endpoint | Field |
@@ -147,7 +130,6 @@ GET https://api.fabric.microsoft.com/v1/workspaces/{workspaceId}/eventhouses/{ev
147
130
  | SQL server (Warehouse) | Warehouse | `.../warehouses/{id}` | `properties.connectionString` |
148
131
  | SQL server + database (SQL DB) | SQL Database | `.../sqlDatabases/{id}` | `properties.serverFqdn` / `properties.databaseName` |
149
132
  | OneLake Files / Tables URL | Lakehouse | `.../lakehouses/{id}` | `properties.oneLakeFilesPath` / `oneLakeTablesPath` |
150
- | Kusto query URI | Eventhouse | `.../eventhouses/{id}` | `properties.queryServiceUri` |
151
133
  | Item GUID (any item) | any | `.../items?type={Type}` | `id` |
152
134
 
153
135
  For item types not listed here, browse the [Fabric REST API item reference](https://learn.microsoft.com/en-us/rest/api/fabric/) — each item's **Get** operation returns its coordinates under `properties`.
@@ -4,12 +4,15 @@ sidebar_position: 5
4
4
 
5
5
  # Connecting to external resources
6
6
 
7
- Functions can call external Azure and Fabric resources **as the calling user** using delegated authentication.
8
- You declare a connection on the function, and at invocation time the runtime exchanges the user's identity for a resource-scoped on-behalf-of (OBO) token.
7
+ Deployed Functions call external Azure and Fabric resources **as the app identity** using application authentication.
8
+ You declare the audiences a function needs and consume platform-provided, resource-scoped tokens through `ctx.Tokens`.
9
+ See [Application authentication](../index.md#application-authentication) for configuration, resource permissions, and the distinction from caller-scoped Rayfin DB access.
10
+
11
+ Developer CLI login and authoring-time endpoint discovery are separate from deployed runtime access; verify the deployed app identity's permissions even when a developer can access the resource.
9
12
 
10
13
  ## The pattern
11
14
 
12
- Declare an audience-scoped connection in the third argument of `udf.func()`, then read its token with `ctx.getToken()`:
15
+ Declare the audiences in the `RayfinContext` annotation, then read the scoped token off `ctx.Tokens`:
13
16
 
14
17
  ```ts
15
18
  import {
@@ -21,19 +24,123 @@ import {
21
24
  const udf = new UserDataFunctions();
22
25
 
23
26
  udf.func(
24
- "myFunction",
25
- async (ctx: RayfinContext): Promise<string> => {
26
- const token = ctx.getToken(AudienceType.KeyVault);
27
+ "accessStorage",
28
+ async (
29
+ ctx: RayfinContext<AppSchema, AudienceType.Storage>,
30
+ ): Promise<string> => {
31
+ const token: string = ctx.Tokens.Storage;
27
32
  // Use `token` with the resource's SDK or REST API.
28
33
  return "ok";
29
34
  },
30
- [udf.connection({ audienceType: AudienceType.KeyVault })],
35
+ [],
36
+ );
37
+ ```
38
+
39
+ **The annotation is the declaration.** Listing an audience in `RayfinContext<Schema, Audiences>` is what registers the connection binding — there is nothing to add to the third argument of `udf.func()`.
40
+
41
+ `ctx.Tokens` is narrowed to exactly the audiences you declared, so an undeclared audience is a compile error rather than a runtime throw:
42
+
43
+ ```ts
44
+ async (
45
+ ctx: RayfinContext<AppSchema, AudienceType.Sql | AudienceType.Storage>,
46
+ ) => {
47
+ const sqlToken: string = ctx.Tokens.Sql; // ok
48
+ const storageToken: string = ctx.Tokens.Storage; // ok
49
+ ctx.Tokens.Fabric; // compile error — not declared
50
+ };
51
+ ```
52
+
53
+ Values are typed `string`, not `string | undefined`.
54
+ Declaring an audience registers its binding, but does not guarantee token availability or resource access.
55
+ If the host does not supply a declared token, reading its `ctx.Tokens` property throws.
56
+
57
+ ### The schema argument
58
+
59
+ `RayfinContext` takes your app schema first — the same one you pass to `RayfinClient<AppSchema>` — so the data client stays typed while you add audiences:
60
+
61
+ ```ts
62
+ async (ctx: RayfinContext<AppSchema, AudienceType.Sql>) => {
63
+ const data = ctx.getDataClient(); // typed by AppSchema
64
+ const token = ctx.Tokens.Sql;
65
+ };
66
+ ```
67
+
68
+ If a function needs audiences but no data access, pass the default schema explicitly:
69
+
70
+ ```ts
71
+ async (ctx: RayfinContext<Record<string, any>, AudienceType.Fabric>) => {
72
+ const token = ctx.Tokens.Fabric;
73
+ };
74
+ ```
75
+
76
+ ## The third argument
77
+
78
+ `udf.func()` still takes a third argument, and it stays `[]`. Audience-scoped connections are declared entirely in the context annotation — there is nothing to add there:
79
+
80
+ ```ts
81
+ udf.func(
82
+ "syncWarehouse",
83
+ async (ctx: RayfinContext<AppSchema, AudienceType.Sql>): Promise<void> => {
84
+ const token = ctx.Tokens.Sql;
85
+ },
86
+ [],
87
+ );
88
+ ```
89
+
90
+ ## Supported audiences
91
+
92
+ `AudienceType` is the source of truth — inventing a member will not compile:
93
+
94
+ `Sql`, `Storage`, `Fabric`, `AzureAI`, `ADO`.
95
+
96
+ A Power BI semantic model is **not** reachable this way; use the `fabric-semanticmodel` connector instead.
97
+
98
+ ## How audiences reach the deployment
99
+
100
+ Type arguments are erased before your function runs, so the audiences have to be recovered at build time. `npx rayfin up` resolves them with the TypeScript compiler and writes the resolved union into the deployment metadata, which is what the worker binds against.
101
+
102
+ Because the compiler does the resolving, a type alias works:
103
+
104
+ ```ts
105
+ type SqlAccess = AudienceType.Sql;
106
+
107
+ // Resolves to AudienceType.Sql and binds a Sql connection.
108
+ async (ctx: RayfinContext<AppSchema, SqlAccess>) => {
109
+ const token = ctx.Tokens.Sql;
110
+ };
111
+ ```
112
+
113
+ Writing audiences **literally** is still the clearer default, and there is one case where it is required: if the functions project has no `tsconfig.json`, the CLI has no compiler to ask and falls back to reading the annotation's syntax. An alias then resolves to its own name rather than the audience it stands for. The CLI warns when it is in that mode — treat the warning as a real problem, not noise.
114
+
115
+ The context parameter must also carry an explicit `RayfinContext<...>` annotation. Typegen identifies the context parameter by that annotation; an unannotated parameter is treated as a request-body parameter instead.
116
+
117
+ ## Migrating from `ctx.getToken()`
118
+
119
+ `ctx.getToken(audienceType)` is **deprecated**. It is flagged by `@typescript-eslint/no-deprecated`, which the scaffolded ESLint config reports as an error.
120
+
121
+ ```ts
122
+ // Before
123
+ udf.func(
124
+ "queryData",
125
+ async (ctx: RayfinContext): Promise<void> => {
126
+ const token = ctx.getToken(AudienceType.Sql);
127
+ },
128
+ [udf.connection({ audienceType: AudienceType.Sql })],
129
+ );
130
+
131
+ // After
132
+ udf.func(
133
+ "queryData",
134
+ async (ctx: RayfinContext<AppSchema, AudienceType.Sql>): Promise<void> => {
135
+ const token = ctx.Tokens.Sql;
136
+ },
137
+ [],
31
138
  );
32
139
  ```
33
140
 
34
- - `udf.connection({ audienceType })` declares the delegated connection.
35
- - `ctx.getToken(audienceType)` returns the OBO access token as a `string`. It throws if no connection was declared for that audience.
36
- - The connection declaration is what makes the token available — declaring the audience and calling `getToken` with the same audience always go together.
141
+ Move the audience from the third argument into the annotation, then replace the call with the property.
142
+
143
+ **Both halves are required.** The array keeps working at *runtime* — array audiences are unioned with the annotated ones, so a deployed function still binds the connection. But it no longer carries any type information: `TokenTypes` defaults to `never`, so a handler left as `ctx: RayfinContext<AppSchema>` stops compiling at `ctx.getToken(AudienceType.Sql)` even with `Sql` declared in the array. Add the audience to the annotation first; the array alone is not enough.
37
144
 
38
145
  ## Wrapping the token for Azure SDK clients
39
146
 
@@ -51,12 +158,12 @@ class ContextTokenCredential implements TokenCredential {
51
158
  }
52
159
  ```
53
160
 
54
- Pass `new ContextTokenCredential(ctx.getToken(AudienceType.X))` wherever an Azure SDK client asks for a credential.
161
+ Pass `new ContextTokenCredential(ctx.Tokens.X)` wherever an Azure SDK client asks for a credential.
55
162
  The per-resource guides below use this helper.
56
163
 
57
164
  ## Choose a recipe
58
165
 
59
- Find what you want to connect to. Each row links to a task-oriented walkthrough; the `AudienceType` is the value you pass to `udf.connection()` and `ctx.getToken()`. A single audience can back more than one use case (for example `Storage` covers both Fabric OneLake and Azure Blob).
166
+ Find what you want to connect to. Each row links to a task-oriented walkthrough; the `AudienceType` is the value you put in the context annotation and the key you read off `ctx.Tokens`. A single audience can back more than one use case (for example `Storage` covers both Fabric OneLake and Azure Blob).
60
167
 
61
168
  | I want to connect to… | `AudienceType` | Recipe |
62
169
  | ------------------------------------------------ | -------------- | -------------------------------------------------------------------- |
@@ -64,26 +171,24 @@ Find what you want to connect to. Each row links to a task-oriented walkthrough;
64
171
  | Azure SQL Database | `Sql` | [Add a Fabric resource](./add-fabric-resource.md#sql-databases) |
65
172
  | Fabric OneLake files | `Storage` | [Add a Fabric resource](./add-fabric-resource.md#onelake-files) |
66
173
  | Azure Blob / Table / Queue | `Storage` | [Add an Azure resource](./add-azure-resource.md#blob-storage) |
67
- | Fabric Eventhouse (KQL) | `Kusto` | [Add a Fabric resource](./add-fabric-resource.md#eventhouse-and-kql) |
68
- | Azure Data Explorer | `Kusto` | [Add a Fabric resource](./add-fabric-resource.md#eventhouse-and-kql) |
69
174
  | Microsoft Fabric REST API | `Fabric` | [Add a Fabric resource](./add-fabric-resource.md#fabric-rest-api) |
70
- | Azure Key Vault | `KeyVault` | [Add an Azure resource](./add-azure-resource.md#key-vault) |
71
- | Azure Cosmos DB | `CosmosDB` | [Add an Azure resource](./add-azure-resource.md#cosmos-db) |
72
- | Azure Event Grid | `EventGrid` | [Add an Azure resource](./add-azure-resource.md#event-grid) |
73
175
  | Azure AI Foundry | `AzureAI` | [Add Azure AI Foundry](./add-foundry.md) |
74
176
  | Azure DevOps | `ADO` | [Add Azure DevOps](./add-ado.md) |
75
- | WorkIQ | `WorkIQ` | [Add WorkIQ](./add-work-iq.md) |
76
177
 
77
- To look up a Fabric item's connection coordinates (SQL endpoint, OneLake path, Kusto query URI), see [Get Fabric info](./get-fabric-info.md).
178
+ To look up a Fabric item's connection coordinates (SQL endpoint or OneLake path), see [Get Fabric info](./get-fabric-info.md).
78
179
 
79
180
  ## Rules
80
181
 
182
+ - **Declare generic audiences in the annotation** and read them with `ctx.Tokens.<Audience>`. Prefer literal `AudienceType.X`, and keep a `tsconfig.json` in the functions project so the CLI can resolve anything that isn't.
183
+ - **Pass `[]` as the third argument** to `udf.func()`.
81
184
  - **Use real endpoint URLs** — don't hardcode a guess or read them from `process.env`. For Fabric items, [Get Fabric info](./get-fabric-info.md) shows how to look them up.
82
- - Declare every connection in the third argument to `udf.func()`.
83
- - Use `ctx.getToken(AudienceType.X)` — never acquire tokens manually.
185
+ - Never acquire tokens manually.
186
+ - Grant resource access to the app identity, and keep its tokens server-side; never return them as function results.
84
187
  - Install resource SDK packages in `rayfin/functions/package.json`, not the project root.
85
188
 
86
189
  ## Local development
87
190
 
88
- When you run `npx rayfin dev functions apply`, the local host acquires delegated tokens interactively for debugging, so `ctx.getToken()` works against your declared audiences without a deployment.
191
+ `npx rayfin dev` and `npx rayfin dev functions apply` start a local Azure Functions Core Tools host.
192
+ External resource tokens use the identity and permissions of the account the builder signs in with in the local Functions host.
193
+ Deployed Functions instead use the [application identity](../index.md#application-authentication).
89
194
  See [`npx rayfin dev functions apply`](../../cli/functions/dev-apply.md).
@@ -29,6 +29,44 @@ npx rayfin functions init
29
29
  This creates `rayfin/functions/`, installs dependencies, and generates the initial `types.ts`.
30
30
  See [`npx rayfin functions init`](../cli/functions/init.md) for the full command reference.
31
31
 
32
+ ## Application authentication
33
+
34
+ Enabled Functions require explicit application authentication in `rayfin/rayfin.yml`:
35
+
36
+ ```yaml
37
+ services:
38
+ functions:
39
+ enabled: true
40
+ auth:
41
+ type: application
42
+ buildCommand: npm run build
43
+ ```
44
+
45
+ New Functions scaffolds set `auth.type: application`.
46
+ For an existing app, set it explicitly; loading the configuration does not add a default or migrate an older auth mode.
47
+ Disabled Functions may omit `auth`, but any supplied `auth` must include `type: application`.
48
+ No feature flag is needed.
49
+
50
+ Full `npx rayfin up` and normal `npx rayfin dev` validate this setting before applying project settings to either the Fabric or Docker backend.
51
+ Run a full `npx rayfin up` to apply the YAML auth mode to an existing remote app.
52
+ Standalone `npx rayfin dev functions apply`, `npx rayfin up functions deploy`, and `npx rayfin up staticapp deploy` do not perform this project-settings validation or change the remote Functions auth mode.
53
+
54
+ Deployed Functions use two separate authentication paths:
55
+
56
+ | Access path | Credential | Identity and permissions |
57
+ | --- | --- | --- |
58
+ | External connections through `ctx.Tokens.*` | Platform-provided resource token | Application identity and its permissions on the external resource |
59
+ | Rayfin DB through `ctx.getDataClient()` | The invocation's Rayfin token | The caller's identity and permissions on the Rayfin DB |
60
+
61
+ For current Fabric apps, the app identity is the owner of the Fabric app item.
62
+ External connections therefore use the item owner's permissions, not those of whichever app user invokes the function.
63
+ Grant that identity the permissions required by each external resource and API your functions call.
64
+ Declaring an audience or deploying Functions does not grant those permissions.
65
+ App sign-in and authorization to invoke a function remain separate from the app identity's external resource access.
66
+ Rayfin DB access always uses the Rayfin token and preserves the caller's identity and database permissions.
67
+ Setting `services.functions.auth.type: application` does not switch Rayfin DB access to the application identity.
68
+ See [Connecting to external resources](./connections/index.md) for audience declarations, permissions, and local development.
69
+
32
70
  ## Project structure
33
71
 
34
72
  After `npx rayfin functions init`, the functions project lives at `rayfin/functions/`:
@@ -86,6 +124,6 @@ See [`npx rayfin dev functions apply`](../cli/functions/dev-apply.md) for detail
86
124
 
87
125
  - [Writing functions](./writing-functions.md) — the `udf.func()` API, `RayfinContext`, and data access.
88
126
  - [Type generation](./typegen.md) — how `types.ts` is generated and kept in sync.
89
- - [Connecting to external resources](./connections/index.md) — delegated access to SQL, Storage, Key Vault, and more.
127
+ - [Connecting to external resources](./connections/index.md) — app-identity access to SQL, Storage, the Fabric REST API, and more.
90
128
  - [Secrets](./secrets.md) — reading secrets from inside a function.
91
129
  - [Invoking functions from the frontend](./invoking-from-frontend.md) — calling functions with `RayfinClient`.
@@ -4,7 +4,7 @@ sidebar_position: 3
4
4
 
5
5
  # Secrets
6
6
 
7
- Functions read secret values through `ctx.getSecret(name)` on [`RayfinContext`](./writing-functions.md#rayfincontext-api).
7
+ Functions read secret values as typed properties on `ctx.Secrets`, part of [`RayfinContext`](./writing-functions.md#rayfincontext-api).
8
8
 
9
9
  ```ts
10
10
  import {
@@ -16,23 +16,34 @@ const udf = new UserDataFunctions();
16
16
 
17
17
  udf.func(
18
18
  "readApiKey",
19
- async (ctx: RayfinContext): Promise<string> => {
20
- const apiKey = ctx.getSecret("THIRD_PARTY_API_KEY");
21
- if (!apiKey) {
22
- throw new Error("Missing THIRD_PARTY_API_KEY secret.");
23
- }
24
- return apiKey;
19
+ async (ctx: RayfinContext<AppSchema>): Promise<string> => {
20
+ return ctx.Secrets.THIRD_PARTY_API_KEY;
25
21
  },
26
22
  [],
27
23
  );
28
24
  ```
29
25
 
26
+ There is nothing to add to the function's declaration and nothing to add to your data schema — declaring the secret with the CLI is what types it.
27
+
28
+ ## How the names are typed
29
+
30
+ `npx rayfin secret set <NAME>` records the name in `rayfin.yml`, and the CLI regenerates `rayfin/functions/src/secrets.generated.ts`. That file augments the SDK's `RayfinSecretRegistry` interface, which is what narrows `ctx.Secrets`:
31
+
32
+ ```ts
33
+ ctx.Secrets.THIRD_PARTY_API_KEY; // string
34
+ ctx.Secrets.NOT_DECLARED; // compile error
35
+ ```
36
+
37
+ - Values are typed `string`, not `string | undefined` — the name was declared, so the value is modelled as present. Reading a declared secret that was not supplied throws a diagnosable error rather than silently yielding `undefined`.
38
+ - Nothing imports `secrets.generated.ts`; it only has to be part of the compilation. **Never hand-edit it** — the CLI overwrites it, and the typegen watcher deliberately ignores it so regenerating doesn't loop.
39
+ - Deleting a secret narrows the type, so stale references stop compiling instead of failing at invocation.
40
+
30
41
  ## How resolution works
31
42
 
32
- `ctx.getSecret(name)` returns `string | undefined`. It resolves in this order:
43
+ `ctx.Secrets.<NAME>` resolves in this order:
33
44
 
34
45
  1. The host-provided secret bag delivered with the invocation.
35
- 2. `process.env[name]` as a fallback.
46
+ 2. `process.env[NAME]` as a fallback.
36
47
 
37
48
  ## Setting secrets
38
49
 
@@ -48,7 +59,7 @@ See [Managing secrets](../cli/secrets.md) for the full `npx rayfin secret` comma
48
59
  ## Secrets in local development
49
60
 
50
61
  The host-provided secret bag is only present for deployed invocations.
51
- When you debug locally with `npx rayfin dev functions apply`, add the same key under `Values` in `rayfin/functions/local.settings.json` so it flows into `process.env` and is picked up by the `getSecret` fallback:
62
+ When you debug locally with `npx rayfin dev functions apply`, add the same key under `Values` in `rayfin/functions/local.settings.json` so it flows into `process.env` and is picked up by the fallback:
52
63
 
53
64
  ```jsonc
54
65
  {
@@ -64,7 +75,24 @@ When you debug locally with `npx rayfin dev functions apply`, add the same key u
64
75
  `local.settings.json` is git-ignored, so these values stay on your machine.
65
76
  See [Secrets in local development](../cli/secrets.md#using-secrets-in-local-development) for more.
66
77
 
78
+ ## The deprecated `ctx.getSecret()`
79
+
80
+ `ctx.getSecret(name)` returns `string | undefined` and is **deprecated** — it is flagged by `@typescript-eslint/no-deprecated`, which the scaffolded ESLint config reports as an error.
81
+
82
+ ```ts
83
+ // Before
84
+ const apiKey = ctx.getSecret("THIRD_PARTY_API_KEY");
85
+ if (!apiKey) {
86
+ throw new Error("Missing THIRD_PARTY_API_KEY secret.");
87
+ }
88
+
89
+ // After
90
+ const apiKey = ctx.Secrets.THIRD_PARTY_API_KEY;
91
+ ```
92
+
93
+ It remains valid for a value deliberately **not** modelled in `rayfin.yml` — a host-injected environment variable, for example. Suppress the lint at that one call site rather than project-wide.
94
+
67
95
  ## Best practices
68
96
 
69
- - Prefer secrets set with `npx rayfin secret set <NAME>` as the source of truth for deployed functions.
97
+ - Declare every secret with `npx rayfin secret set <NAME>` and read it from `ctx.Secrets` — never hard-code a secret name string.
70
98
  - Use the `process.env` fallback only for local debugging — do not make it your primary production secret source.