@superblocksteam/sdk-api 2.0.105 → 2.0.106-next.0
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.
- package/README.md +439 -89
- package/dist/api/definition.d.ts +11 -6
- package/dist/api/definition.d.ts.map +1 -1
- package/dist/api/definition.js +19 -12
- package/dist/api/definition.js.map +1 -1
- package/dist/api/definition.test.js +39 -15
- package/dist/api/definition.test.js.map +1 -1
- package/dist/errors.d.ts +1 -1
- package/dist/errors.js +1 -1
- package/dist/index.d.ts +10 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -5
- package/dist/index.js.map +1 -1
- package/dist/integrations/base/index.d.ts +2 -1
- package/dist/integrations/base/index.d.ts.map +1 -1
- package/dist/integrations/base/index.js +1 -0
- package/dist/integrations/base/index.js.map +1 -1
- package/dist/integrations/base/rest-api-client-base.d.ts +48 -0
- package/dist/integrations/base/rest-api-client-base.d.ts.map +1 -0
- package/dist/integrations/base/rest-api-client-base.js +98 -0
- package/dist/integrations/base/rest-api-client-base.js.map +1 -0
- package/dist/integrations/base/rest-api-integration-client.d.ts +10 -20
- package/dist/integrations/base/rest-api-integration-client.d.ts.map +1 -1
- package/dist/integrations/base/rest-api-integration-client.js +10 -65
- package/dist/integrations/base/rest-api-integration-client.js.map +1 -1
- package/dist/integrations/box/types.d.ts +1 -1
- package/dist/integrations/declarations.d.ts +5 -73
- package/dist/integrations/declarations.d.ts.map +1 -1
- package/dist/integrations/declarations.js +5 -68
- package/dist/integrations/declarations.js.map +1 -1
- package/dist/integrations/documentation.test.js +0 -2
- package/dist/integrations/documentation.test.js.map +1 -1
- package/dist/integrations/googledrive/types.d.ts +1 -1
- package/dist/integrations/index.d.ts +1 -11
- package/dist/integrations/index.d.ts.map +1 -1
- package/dist/integrations/index.js +1 -7
- package/dist/integrations/index.js.map +1 -1
- package/dist/integrations/registry.d.ts +1 -11
- package/dist/integrations/registry.d.ts.map +1 -1
- package/dist/integrations/registry.js +0 -29
- package/dist/integrations/registry.js.map +1 -1
- package/dist/integrations/slack/client.d.ts +13 -9
- package/dist/integrations/slack/client.d.ts.map +1 -1
- package/dist/integrations/slack/client.js +60 -8
- package/dist/integrations/slack/client.js.map +1 -1
- package/dist/integrations/slack/client.test.d.ts +11 -0
- package/dist/integrations/slack/client.test.d.ts.map +1 -0
- package/dist/integrations/slack/client.test.js +368 -0
- package/dist/integrations/slack/client.test.js.map +1 -0
- package/dist/integrations/slack/index.d.ts +2 -1
- package/dist/integrations/slack/index.d.ts.map +1 -1
- package/dist/integrations/slack/index.js +1 -0
- package/dist/integrations/slack/index.js.map +1 -1
- package/dist/integrations/slack/types.d.ts +127 -28
- package/dist/integrations/slack/types.d.ts.map +1 -1
- package/dist/integrations/slack/types.js +27 -1
- package/dist/integrations/slack/types.js.map +1 -1
- package/dist/integrations/snowflake/client.d.ts +2 -2
- package/dist/integrations/snowflake/client.js +2 -2
- package/dist/runtime/context.d.ts +1 -1
- package/dist/runtime/executor.d.ts +2 -2
- package/dist/types.d.ts +15 -6
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/api/definition.test.ts +40 -15
- package/src/api/definition.ts +19 -12
- package/src/errors.ts +1 -1
- package/src/index.ts +13 -33
- package/src/integrations/asana/README.md +12 -12
- package/src/integrations/base/index.ts +2 -1
- package/src/integrations/base/rest-api-client-base.ts +134 -0
- package/src/integrations/base/rest-api-integration-client.ts +12 -89
- package/src/integrations/bitbucket/README.md +19 -19
- package/src/integrations/box/README.md +24 -24
- package/src/integrations/box/types.ts +1 -1
- package/src/integrations/circleci/README.md +18 -18
- package/src/integrations/declarations.ts +5 -105
- package/src/integrations/documentation.test.ts +0 -2
- package/src/integrations/googledrive/README.md +25 -22
- package/src/integrations/googledrive/types.ts +1 -1
- package/src/integrations/graphql/README.md +2 -2
- package/src/integrations/groq/README.md +8 -8
- package/src/integrations/index.ts +0 -51
- package/src/integrations/perplexity/README.md +39 -48
- package/src/integrations/registry.ts +1 -39
- package/src/integrations/salesforce/README.md +11 -9
- package/src/integrations/slack/README.md +62 -19
- package/src/integrations/slack/client.test.ts +553 -0
- package/src/integrations/slack/client.ts +92 -12
- package/src/integrations/slack/index.ts +6 -1
- package/src/integrations/slack/types.ts +142 -29
- package/src/integrations/snowflake/client.ts +2 -2
- package/src/integrations/zoom/README.md +15 -15
- package/src/runtime/context.ts +1 -1
- package/src/runtime/executor.ts +2 -2
- package/src/types.ts +15 -6
- package/dist/integrations/couchbase/client.d.ts +0 -36
- package/dist/integrations/couchbase/client.d.ts.map +0 -1
- package/dist/integrations/couchbase/client.js +0 -148
- package/dist/integrations/couchbase/client.js.map +0 -1
- package/dist/integrations/couchbase/index.d.ts +0 -8
- package/dist/integrations/couchbase/index.d.ts.map +0 -1
- package/dist/integrations/couchbase/index.js +0 -7
- package/dist/integrations/couchbase/index.js.map +0 -1
- package/dist/integrations/couchbase/types.d.ts +0 -100
- package/dist/integrations/couchbase/types.d.ts.map +0 -1
- package/dist/integrations/couchbase/types.js +0 -5
- package/dist/integrations/couchbase/types.js.map +0 -1
- package/dist/integrations/kafka/client.d.ts +0 -25
- package/dist/integrations/kafka/client.d.ts.map +0 -1
- package/dist/integrations/kafka/client.js +0 -124
- package/dist/integrations/kafka/client.js.map +0 -1
- package/dist/integrations/kafka/index.d.ts +0 -8
- package/dist/integrations/kafka/index.d.ts.map +0 -1
- package/dist/integrations/kafka/index.js +0 -7
- package/dist/integrations/kafka/index.js.map +0 -1
- package/dist/integrations/kafka/types.d.ts +0 -113
- package/dist/integrations/kafka/types.d.ts.map +0 -1
- package/dist/integrations/kafka/types.js +0 -5
- package/dist/integrations/kafka/types.js.map +0 -1
- package/dist/integrations/kinesis/client.d.ts +0 -31
- package/dist/integrations/kinesis/client.d.ts.map +0 -1
- package/dist/integrations/kinesis/client.js +0 -101
- package/dist/integrations/kinesis/client.js.map +0 -1
- package/dist/integrations/kinesis/index.d.ts +0 -8
- package/dist/integrations/kinesis/index.d.ts.map +0 -1
- package/dist/integrations/kinesis/index.js +0 -7
- package/dist/integrations/kinesis/index.js.map +0 -1
- package/dist/integrations/kinesis/types.d.ts +0 -97
- package/dist/integrations/kinesis/types.d.ts.map +0 -1
- package/dist/integrations/kinesis/types.js +0 -7
- package/dist/integrations/kinesis/types.js.map +0 -1
- package/dist/integrations/python/client.d.ts +0 -42
- package/dist/integrations/python/client.d.ts.map +0 -1
- package/dist/integrations/python/client.js +0 -89
- package/dist/integrations/python/client.js.map +0 -1
- package/dist/integrations/python/client.test.d.ts +0 -5
- package/dist/integrations/python/client.test.d.ts.map +0 -1
- package/dist/integrations/python/client.test.js +0 -214
- package/dist/integrations/python/client.test.js.map +0 -1
- package/dist/integrations/python/index.d.ts +0 -6
- package/dist/integrations/python/index.d.ts.map +0 -1
- package/dist/integrations/python/index.js +0 -5
- package/dist/integrations/python/index.js.map +0 -1
- package/dist/integrations/python/types.d.ts +0 -85
- package/dist/integrations/python/types.d.ts.map +0 -1
- package/dist/integrations/python/types.js +0 -5
- package/dist/integrations/python/types.js.map +0 -1
- package/dist/integrations/redis/client.d.ts +0 -43
- package/dist/integrations/redis/client.d.ts.map +0 -1
- package/dist/integrations/redis/client.js +0 -142
- package/dist/integrations/redis/client.js.map +0 -1
- package/dist/integrations/redis/index.d.ts +0 -8
- package/dist/integrations/redis/index.d.ts.map +0 -1
- package/dist/integrations/redis/index.js +0 -7
- package/dist/integrations/redis/index.js.map +0 -1
- package/dist/integrations/redis/types.d.ts +0 -137
- package/dist/integrations/redis/types.d.ts.map +0 -1
- package/dist/integrations/redis/types.js +0 -5
- package/dist/integrations/redis/types.js.map +0 -1
- package/src/integrations/couchbase/README.md +0 -138
- package/src/integrations/couchbase/client.ts +0 -225
- package/src/integrations/couchbase/index.ts +0 -8
- package/src/integrations/couchbase/types.ts +0 -126
- package/src/integrations/kafka/README.md +0 -144
- package/src/integrations/kafka/client.ts +0 -216
- package/src/integrations/kafka/index.ts +0 -14
- package/src/integrations/kafka/types.ts +0 -128
- package/src/integrations/kinesis/README.md +0 -153
- package/src/integrations/kinesis/client.ts +0 -146
- package/src/integrations/kinesis/index.ts +0 -14
- package/src/integrations/kinesis/types.ts +0 -114
- package/src/integrations/python/README.md +0 -566
- package/src/integrations/python/client.test.ts +0 -341
- package/src/integrations/python/client.ts +0 -136
- package/src/integrations/python/index.ts +0 -6
- package/src/integrations/python/types.ts +0 -92
- package/src/integrations/redis/README.md +0 -200
- package/src/integrations/redis/client.ts +0 -208
- package/src/integrations/redis/index.ts +0 -8
- package/src/integrations/redis/types.ts +0 -167
package/README.md
CHANGED
|
@@ -183,7 +183,7 @@ interface ExecuteApiRequest {
|
|
|
183
183
|
executeQuery: (
|
|
184
184
|
integrationId: string,
|
|
185
185
|
request: Record<string, unknown>, // Plugin-specific request matching protobuf schema
|
|
186
|
-
bindings?: Record<string, unknown>, // For language plugins (
|
|
186
|
+
bindings?: Record<string, unknown>, // For language plugins (JavaScript)
|
|
187
187
|
) => Promise<unknown>;
|
|
188
188
|
}
|
|
189
189
|
```
|
|
@@ -276,6 +276,42 @@ export default api({
|
|
|
276
276
|
});
|
|
277
277
|
```
|
|
278
278
|
|
|
279
|
+
### Export style (default vs named exports)
|
|
280
|
+
|
|
281
|
+
**Default export** — one API per file. **Named exports** — several `api({ ... })` values in one module; import them by name into `server/apis/index.ts`. `useApi("X")` matches the **key** on the registry object; align `api({ name: "X" })` with that when practical.
|
|
282
|
+
|
|
283
|
+
```typescript
|
|
284
|
+
// server/apis/users/pair.ts
|
|
285
|
+
import { api, z } from "@superblocksteam/sdk-api";
|
|
286
|
+
|
|
287
|
+
export const ListProfiles = api({
|
|
288
|
+
name: "ListProfiles",
|
|
289
|
+
input: z.object({ orgId: z.string() }),
|
|
290
|
+
output: z.object({ profiles: z.array(z.object({ id: z.string() })) }),
|
|
291
|
+
async run() {
|
|
292
|
+
return { profiles: [] };
|
|
293
|
+
},
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
export const UpdateProfile = api({
|
|
297
|
+
name: "UpdateProfile",
|
|
298
|
+
input: z.object({ userId: z.string() }),
|
|
299
|
+
output: z.object({ ok: z.literal(true) }),
|
|
300
|
+
async run() {
|
|
301
|
+
return { ok: true as const };
|
|
302
|
+
},
|
|
303
|
+
});
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
```typescript
|
|
307
|
+
// server/apis/index.ts
|
|
308
|
+
import { ListProfiles, UpdateProfile } from "./users/pair.js";
|
|
309
|
+
|
|
310
|
+
const apis = { ListProfiles, UpdateProfile } as const;
|
|
311
|
+
export default apis;
|
|
312
|
+
export type ApiRegistry = typeof apis;
|
|
313
|
+
```
|
|
314
|
+
|
|
279
315
|
## API Reference
|
|
280
316
|
|
|
281
317
|
### `api(config)`
|
|
@@ -284,13 +320,14 @@ Defines a TypeScript-based API with input/output validation. Returns a complete
|
|
|
284
320
|
|
|
285
321
|
#### Required Fields
|
|
286
322
|
|
|
287
|
-
| Field | Type | Description
|
|
288
|
-
| -------------- | --------------------------------- |
|
|
289
|
-
| `name` | `string` | **Required.** Unique identifier for the API
|
|
290
|
-
| `
|
|
291
|
-
| `
|
|
292
|
-
| `
|
|
293
|
-
| `
|
|
323
|
+
| Field | Type | Description |
|
|
324
|
+
| -------------- | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
325
|
+
| `name` | `string` | **Required.** Unique identifier for the API |
|
|
326
|
+
| `description` | `string` | Optional. Plain-language summary of what this API does and which integrations it uses. Auto-generated by the AI agent when creating or editing an API. |
|
|
327
|
+
| `input` | `z.ZodType` | **Required.** Zod schema for input validation |
|
|
328
|
+
| `output` | `z.ZodType` | **Required.** Zod schema for output validation |
|
|
329
|
+
| `run` | `(ctx, input) => Promise<Output>` | **Required.** The implementation function |
|
|
330
|
+
| `integrations` | `Record<string, IntegrationRef>` | Optional. Integration declarations for the API |
|
|
294
331
|
|
|
295
332
|
```typescript
|
|
296
333
|
import { api, z } from "@superblocksteam/sdk-api";
|
|
@@ -325,7 +362,7 @@ const myApi = api({
|
|
|
325
362
|
|
|
326
363
|
The `run` function receives two arguments:
|
|
327
364
|
|
|
328
|
-
1. **`ctx`** - Context object with integrations, logging, and
|
|
365
|
+
1. **`ctx`** - Context object with integrations, logging, environment, and user information
|
|
329
366
|
2. **`input`** - Typed, validated input data matching your input schema
|
|
330
367
|
|
|
331
368
|
```typescript
|
|
@@ -340,7 +377,7 @@ async run(ctx, { userId, options }) {
|
|
|
340
377
|
Access typed integration clients via the **name (key)** you declared in the API config: `ctx.integrations.<name>`. For example, if you declared `integrations: { db: postgres(...), notifier: slack(...) }`, use `ctx.integrations.db` and `ctx.integrations.notifier` in your `run` function. Integrations must be declared upfront to enable type-safe access and pre-authentication.
|
|
341
378
|
|
|
342
379
|
```typescript
|
|
343
|
-
import { api, z, postgres, slack, openai, anthropic, stripe, github, notion
|
|
380
|
+
import { api, z, postgres, slack, openai, anthropic, stripe, github, notion } from "@superblocksteam/sdk-api";
|
|
344
381
|
|
|
345
382
|
// Integration IDs from the integrations panel - store in constants
|
|
346
383
|
const PROD_POSTGRES = "a1b2c3d4-5678-90ab-cdef-ghijklmnopqr";
|
|
@@ -350,7 +387,6 @@ const PROD_ANTHROPIC = "d4e5f6g7-8901-23ab-cdef-ghijklmnopqr";
|
|
|
350
387
|
const PROD_STRIPE = "e5f6g7h8-9012-34ab-cdef-ghijklmnopqr";
|
|
351
388
|
const PROD_GITHUB = "f6g7h8i9-0123-45ab-cdef-ghijklmnopqr";
|
|
352
389
|
const PROD_NOTION = "g7h8i9j0-1234-56ab-cdef-ghijklmnopqr";
|
|
353
|
-
const PYTHON_RUNTIME = "h8i9j0k1-2345-67ab-cdef-ghijklmnopqr";
|
|
354
390
|
|
|
355
391
|
export default api({
|
|
356
392
|
name: "ProcessUserWorkflow",
|
|
@@ -375,9 +411,6 @@ export default api({
|
|
|
375
411
|
|
|
376
412
|
// Productivity
|
|
377
413
|
wiki: notion(PROD_NOTION),
|
|
378
|
-
|
|
379
|
-
// Code Execution
|
|
380
|
-
runtime: python(PYTHON_RUNTIME),
|
|
381
414
|
},
|
|
382
415
|
|
|
383
416
|
input: z.object({ userId: z.string() }),
|
|
@@ -394,6 +427,22 @@ export default api({
|
|
|
394
427
|
});
|
|
395
428
|
```
|
|
396
429
|
|
|
430
|
+
#### `ctx.user`
|
|
431
|
+
|
|
432
|
+
Access the current user's identity (from the Superblocks JWT). This is the server-side equivalent of the `Global` object available in the frontend.
|
|
433
|
+
|
|
434
|
+
```typescript
|
|
435
|
+
async run(ctx, input) {
|
|
436
|
+
const userId = ctx.user.userId;
|
|
437
|
+
const email = ctx.user.email; // may be undefined
|
|
438
|
+
const name = ctx.user.name; // may be undefined
|
|
439
|
+
const groups = ctx.user.groups; // readonly string[]
|
|
440
|
+
const claims = ctx.user.customClaims; // custom JWT claims
|
|
441
|
+
|
|
442
|
+
ctx.log.info('Request from user', { userId, email });
|
|
443
|
+
}
|
|
444
|
+
```
|
|
445
|
+
|
|
397
446
|
#### `ctx.log`
|
|
398
447
|
|
|
399
448
|
Structured logging utilities.
|
|
@@ -407,6 +456,248 @@ async run(ctx, { userId }) {
|
|
|
407
456
|
}
|
|
408
457
|
```
|
|
409
458
|
|
|
459
|
+
#### `ctx.env`
|
|
460
|
+
|
|
461
|
+
Access environment variables configured for the application.
|
|
462
|
+
|
|
463
|
+
```typescript
|
|
464
|
+
async run(ctx) {
|
|
465
|
+
const apiKey = ctx.env.EXTERNAL_API_KEY;
|
|
466
|
+
if (!apiKey) {
|
|
467
|
+
throw new Error("EXTERNAL_API_KEY environment variable is not configured");
|
|
468
|
+
}
|
|
469
|
+
const environment = ctx.env.NODE_ENV;
|
|
470
|
+
}
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
#### `ctx.user`
|
|
474
|
+
|
|
475
|
+
User information extracted from the Superblocks JWT. This is the **secure, server-side** way to access the current user's identity in API implementations. Never pass user information from the frontend as API input — always use `ctx.user` instead.
|
|
476
|
+
|
|
477
|
+
```typescript
|
|
478
|
+
interface ApiUser {
|
|
479
|
+
/** Unique user identifier from JWT */
|
|
480
|
+
readonly userId: string;
|
|
481
|
+
|
|
482
|
+
/** User's email address (if available) */
|
|
483
|
+
readonly email?: string;
|
|
484
|
+
|
|
485
|
+
/** User's display name (if available) */
|
|
486
|
+
readonly name?: string;
|
|
487
|
+
|
|
488
|
+
/** User's group memberships from JWT */
|
|
489
|
+
readonly groups: readonly string[];
|
|
490
|
+
|
|
491
|
+
/** Custom claims from JWT */
|
|
492
|
+
readonly customClaims: Readonly<Record<string, unknown>>;
|
|
493
|
+
}
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
##### Basic Usage
|
|
497
|
+
|
|
498
|
+
```typescript
|
|
499
|
+
import { api, z, postgres } from "@superblocksteam/sdk-api";
|
|
500
|
+
|
|
501
|
+
const PROD_POSTGRES = "a1b2c3d4-5678-90ab-cdef-ghijklmnopqr";
|
|
502
|
+
|
|
503
|
+
export default api({
|
|
504
|
+
name: "GetMyProfile",
|
|
505
|
+
integrations: { db: postgres(PROD_POSTGRES) },
|
|
506
|
+
input: z.object({}),
|
|
507
|
+
output: z.object({
|
|
508
|
+
email: z.string(),
|
|
509
|
+
name: z.string(),
|
|
510
|
+
}),
|
|
511
|
+
|
|
512
|
+
async run(ctx) {
|
|
513
|
+
// Access user identity securely from the JWT — no frontend input needed
|
|
514
|
+
ctx.log.info("Fetching profile", { userId: ctx.user.userId });
|
|
515
|
+
|
|
516
|
+
const rows = await ctx.integrations.db.query(
|
|
517
|
+
"SELECT email, display_name FROM users WHERE id = $1",
|
|
518
|
+
z.object({ email: z.string(), display_name: z.string() }),
|
|
519
|
+
[ctx.user.userId],
|
|
520
|
+
);
|
|
521
|
+
|
|
522
|
+
if (rows.length === 0) {
|
|
523
|
+
throw new Error("User not found");
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
return { email: rows[0].email, name: rows[0].display_name };
|
|
527
|
+
},
|
|
528
|
+
});
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
##### Role-Based Access Control with Groups
|
|
532
|
+
|
|
533
|
+
Use `ctx.user.groups` to restrict API access based on the user's group memberships. Groups are populated from the user's identity provider (e.g., Okta, Azure AD) via the JWT.
|
|
534
|
+
|
|
535
|
+
```typescript
|
|
536
|
+
import { api, z, postgres } from "@superblocksteam/sdk-api";
|
|
537
|
+
|
|
538
|
+
const PROD_POSTGRES = "a1b2c3d4-5678-90ab-cdef-ghijklmnopqr";
|
|
539
|
+
|
|
540
|
+
export default api({
|
|
541
|
+
name: "GetAdminDashboard",
|
|
542
|
+
integrations: { db: postgres(PROD_POSTGRES) },
|
|
543
|
+
input: z.object({}),
|
|
544
|
+
output: z.object({
|
|
545
|
+
stats: z.object({
|
|
546
|
+
totalUsers: z.number(),
|
|
547
|
+
activeUsers: z.number(),
|
|
548
|
+
}),
|
|
549
|
+
}),
|
|
550
|
+
|
|
551
|
+
async run(ctx) {
|
|
552
|
+
// Check group membership for authorization
|
|
553
|
+
if (!ctx.user.groups.includes("admin")) {
|
|
554
|
+
throw new Error("Access denied: admin group membership required");
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
const stats = await ctx.integrations.db.query(
|
|
558
|
+
`SELECT
|
|
559
|
+
COUNT(*) as total_users,
|
|
560
|
+
COUNT(*) FILTER (WHERE status = 'active') as active_users
|
|
561
|
+
FROM users`,
|
|
562
|
+
z.object({ total_users: z.number(), active_users: z.number() }),
|
|
563
|
+
);
|
|
564
|
+
|
|
565
|
+
return {
|
|
566
|
+
stats: {
|
|
567
|
+
totalUsers: stats[0].total_users,
|
|
568
|
+
activeUsers: stats[0].active_users,
|
|
569
|
+
},
|
|
570
|
+
};
|
|
571
|
+
},
|
|
572
|
+
});
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
##### Custom Claims
|
|
576
|
+
|
|
577
|
+
`ctx.user.customClaims` contains additional claims from the JWT, such as department, role, or tenant information configured in your identity provider.
|
|
578
|
+
|
|
579
|
+
**Important limitations:**
|
|
580
|
+
|
|
581
|
+
- Custom claims are only available when the user is in an **enterprise account that uses SSO**, OR when the application is **deployed and uses embed tokens with SSO claims**.
|
|
582
|
+
- In the editor, custom claims will be empty for non-SSO users.
|
|
583
|
+
- Always write defensive code that handles missing custom claims gracefully.
|
|
584
|
+
|
|
585
|
+
```typescript
|
|
586
|
+
import { api, z, postgres } from "@superblocksteam/sdk-api";
|
|
587
|
+
|
|
588
|
+
const PROD_POSTGRES = "a1b2c3d4-5678-90ab-cdef-ghijklmnopqr";
|
|
589
|
+
|
|
590
|
+
export default api({
|
|
591
|
+
name: "GetTenantData",
|
|
592
|
+
integrations: { db: postgres(PROD_POSTGRES) },
|
|
593
|
+
input: z.object({}),
|
|
594
|
+
output: z.object({
|
|
595
|
+
tenantName: z.string(),
|
|
596
|
+
records: z.array(z.object({ id: z.string(), value: z.string() })),
|
|
597
|
+
}),
|
|
598
|
+
|
|
599
|
+
async run(ctx) {
|
|
600
|
+
// Access custom claims from the JWT — handle missing claims defensively
|
|
601
|
+
const tenantId = ctx.user.customClaims["tenant_id"];
|
|
602
|
+
if (typeof tenantId !== "string") {
|
|
603
|
+
throw new Error(
|
|
604
|
+
"Missing tenant_id claim. Custom claims require enterprise SSO or embed tokens with SSO claims.",
|
|
605
|
+
);
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
const department = ctx.user.customClaims["department"] as
|
|
609
|
+
| string
|
|
610
|
+
| undefined;
|
|
611
|
+
|
|
612
|
+
ctx.log.info("Fetching tenant data", {
|
|
613
|
+
userId: ctx.user.userId,
|
|
614
|
+
tenantId,
|
|
615
|
+
department: department ?? "unknown",
|
|
616
|
+
});
|
|
617
|
+
|
|
618
|
+
const records = await ctx.integrations.db.query(
|
|
619
|
+
"SELECT id, value FROM tenant_data WHERE tenant_id = $1",
|
|
620
|
+
z.object({ id: z.string(), value: z.string() }),
|
|
621
|
+
[tenantId],
|
|
622
|
+
);
|
|
623
|
+
|
|
624
|
+
const tenantNames = await ctx.integrations.db.query(
|
|
625
|
+
"SELECT name FROM tenants WHERE id = $1",
|
|
626
|
+
z.object({ name: z.string() }),
|
|
627
|
+
[tenantId],
|
|
628
|
+
);
|
|
629
|
+
|
|
630
|
+
return {
|
|
631
|
+
tenantName: tenantNames[0]?.name ?? "Unknown Tenant",
|
|
632
|
+
records,
|
|
633
|
+
};
|
|
634
|
+
},
|
|
635
|
+
});
|
|
636
|
+
```
|
|
637
|
+
|
|
638
|
+
##### Combining User Context for Row-Level Security
|
|
639
|
+
|
|
640
|
+
```typescript
|
|
641
|
+
import { api, z, postgres } from "@superblocksteam/sdk-api";
|
|
642
|
+
|
|
643
|
+
const PROD_POSTGRES = "a1b2c3d4-5678-90ab-cdef-ghijklmnopqr";
|
|
644
|
+
|
|
645
|
+
export default api({
|
|
646
|
+
name: "GetMyOrders",
|
|
647
|
+
integrations: { db: postgres(PROD_POSTGRES) },
|
|
648
|
+
input: z.object({
|
|
649
|
+
status: z.enum(["pending", "completed", "cancelled"]).optional(),
|
|
650
|
+
}),
|
|
651
|
+
output: z.object({
|
|
652
|
+
orders: z.array(
|
|
653
|
+
z.object({
|
|
654
|
+
id: z.string(),
|
|
655
|
+
total: z.number(),
|
|
656
|
+
status: z.string(),
|
|
657
|
+
createdAt: z.string(),
|
|
658
|
+
}),
|
|
659
|
+
),
|
|
660
|
+
}),
|
|
661
|
+
|
|
662
|
+
async run(ctx, { status }) {
|
|
663
|
+
// Use ctx.user for row-level security — users can only see their own orders
|
|
664
|
+
if (!ctx.user.email) {
|
|
665
|
+
throw new Error("User email is required to filter orders");
|
|
666
|
+
}
|
|
667
|
+
const params: unknown[] = [ctx.user.email];
|
|
668
|
+
let query =
|
|
669
|
+
"SELECT id, total, status, created_at FROM orders WHERE user_email = $1";
|
|
670
|
+
|
|
671
|
+
if (status) {
|
|
672
|
+
query += " AND status = $2";
|
|
673
|
+
params.push(status);
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
query += " ORDER BY created_at DESC LIMIT 50";
|
|
677
|
+
|
|
678
|
+
const orders = await ctx.integrations.db.query(
|
|
679
|
+
query,
|
|
680
|
+
z.object({
|
|
681
|
+
id: z.string(),
|
|
682
|
+
total: z.number(),
|
|
683
|
+
status: z.string(),
|
|
684
|
+
created_at: z.string(),
|
|
685
|
+
}),
|
|
686
|
+
params,
|
|
687
|
+
);
|
|
688
|
+
|
|
689
|
+
return {
|
|
690
|
+
orders: orders.map((o) => ({
|
|
691
|
+
id: o.id,
|
|
692
|
+
total: o.total,
|
|
693
|
+
status: o.status,
|
|
694
|
+
createdAt: o.created_at,
|
|
695
|
+
})),
|
|
696
|
+
};
|
|
697
|
+
},
|
|
698
|
+
});
|
|
699
|
+
```
|
|
700
|
+
|
|
410
701
|
## Integration Clients
|
|
411
702
|
|
|
412
703
|
### Integration Client Methods Reference
|
|
@@ -415,48 +706,73 @@ async run(ctx, { userId }) {
|
|
|
415
706
|
|
|
416
707
|
#### Method Availability by Client
|
|
417
708
|
|
|
418
|
-
| Client | Available Methods
|
|
419
|
-
| ---------------------------------- |
|
|
420
|
-
| **PostgresClient** | `query<T>(sql, schema, params?)`, `execute(sql, params?)`
|
|
421
|
-
| **SnowflakeClient** | `query<T>(sql, schema, params?)`, `execute(sql, params?)`
|
|
422
|
-
| **
|
|
423
|
-
| **
|
|
424
|
-
| **
|
|
425
|
-
| **
|
|
426
|
-
| **
|
|
427
|
-
| **
|
|
428
|
-
| **
|
|
429
|
-
| **
|
|
430
|
-
| **
|
|
431
|
-
| **
|
|
432
|
-
| **
|
|
433
|
-
| **
|
|
434
|
-
| **
|
|
435
|
-
| **
|
|
436
|
-
| **
|
|
437
|
-
| **
|
|
438
|
-
| **
|
|
439
|
-
| **
|
|
440
|
-
| **
|
|
441
|
-
| **
|
|
442
|
-
| **
|
|
443
|
-
| **
|
|
444
|
-
| **
|
|
445
|
-
| **
|
|
446
|
-
| **
|
|
447
|
-
| **
|
|
448
|
-
| **
|
|
449
|
-
| **
|
|
450
|
-
| **
|
|
451
|
-
| **
|
|
452
|
-
| **
|
|
453
|
-
| **
|
|
454
|
-
| **
|
|
455
|
-
| **
|
|
456
|
-
| **
|
|
457
|
-
| **
|
|
458
|
-
| **
|
|
459
|
-
| **
|
|
709
|
+
| Client | Available Methods | Notes |
|
|
710
|
+
| ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ |
|
|
711
|
+
| **PostgresClient** | `query<T>(sql, schema, params?)`, `execute(sql, params?)` | Schema parameter is REQUIRED for query() |
|
|
712
|
+
| **SnowflakeClient** | `query<T>(sql, schema, params?)`, `execute(sql, params?)` | Schema parameter is REQUIRED for query() |
|
|
713
|
+
| **AirtableClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
|
|
714
|
+
| **AnthropicClient** | `apiRequest(options, schema)` | Claude AI models (see [Streaming note](#streaming--sse)) |
|
|
715
|
+
| **AsanaClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
|
|
716
|
+
| **BitbucketClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
|
|
717
|
+
| **BoxClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
|
|
718
|
+
| **CircleCIClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
|
|
719
|
+
| **CohereClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
|
|
720
|
+
| **ConfluenceClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
|
|
721
|
+
| **DatadogClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
|
|
722
|
+
| **DropboxClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
|
|
723
|
+
| **ElasticSearchClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
|
|
724
|
+
| **FireworksClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
|
|
725
|
+
| **FrontClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
|
|
726
|
+
| **GeminiClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
|
|
727
|
+
| **GitHubClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest - no createIssue, etc. |
|
|
728
|
+
| **GoogleAnalyticsClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
|
|
729
|
+
| **GoogleDriveClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
|
|
730
|
+
| **GraphQLClient** | `query<T>(query, schema, variables?, metadata?)`, `mutation<T>(mutation, schema, variables?, metadata?)` | GraphQL-specific methods |
|
|
731
|
+
| **GroqClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
|
|
732
|
+
| **HubSpotClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
|
|
733
|
+
| **IntercomClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
|
|
734
|
+
| **JiraClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
|
|
735
|
+
| **LaunchDarklyClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
|
|
736
|
+
| **MistralClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
|
|
737
|
+
| **NotionClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest - no queryDatabase, etc. |
|
|
738
|
+
| **OpenAIClient** | `apiRequest(options, schema)` | GPT AI models (see [Streaming note](#streaming--sse)) |
|
|
739
|
+
| **PagerDutyClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
|
|
740
|
+
| **PerplexityClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
|
|
741
|
+
| **SegmentClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
|
|
742
|
+
| **SendGridClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
|
|
743
|
+
| **SlackClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest - no specialized methods |
|
|
744
|
+
| **StabilityAIClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
|
|
745
|
+
| **StripeClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest - no createCustomer, etc. |
|
|
746
|
+
| **TwilioClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
|
|
747
|
+
| **ZendeskClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
|
|
748
|
+
| **ZoomClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
|
|
749
|
+
| **MySQLClient** | `query<T>(sql, schema, params?)`, `execute(sql, params?)` | Schema parameter is REQUIRED for query() |
|
|
750
|
+
| **MariaDBClient** | `query<T>(sql, schema, params?)`, `execute(sql, params?)` | Schema parameter is REQUIRED for query() |
|
|
751
|
+
| **MSSQLClient** | `query<T>(sql, schema, params?)`, `execute(sql, params?)` | Uses `@PARAM_1` placeholder syntax |
|
|
752
|
+
| **CockroachDBClient** | `query<T>(sql, schema, params?)`, `execute(sql, params?)` | Schema parameter is REQUIRED for query() |
|
|
753
|
+
| **OracleDBClient** | `query<T>(sql, schema, params?)`, `execute(sql, params?)` | Uses `:PARAM_1` placeholder syntax |
|
|
754
|
+
| **RedshiftClient** | `query<T>(sql, schema, params?)`, `execute(sql, params?)` | Schema parameter is REQUIRED for query() |
|
|
755
|
+
| **AthenaClient** | `query<T>(sql, schema, params?)`, `execute(sql, params?)` | Uses `?` placeholder syntax |
|
|
756
|
+
| **DatabricksClient** | `query<T>(sql, schema, params?)`, `execute(sql, params?)` | Uses `:param` placeholder syntax |
|
|
757
|
+
| **BigQueryClient** | `query<T>(sql, schema, params?)`, `execute(sql, params?)` | Uses `@param` placeholder syntax |
|
|
758
|
+
| **LakebaseClient** | `query<T>(sql, schema, params?)`, `execute(sql, params?)` | Schema parameter is REQUIRED for query() |
|
|
759
|
+
| **SnowflakePostgresClient** | `query<T>(sql, schema, params?)`, `execute(sql, params?)` | Schema parameter is REQUIRED for query() |
|
|
760
|
+
| **MongoDBClient** | `run(collection, action, schema, params, metadata?)` | 13 actions (find, insertOne, updateOne, etc.) |
|
|
761
|
+
| **DynamoDBClient** | `query<T>(sql, schema)`, `getItem(params, schema)`, `putItem(params)`, `updateItem(params)`, `deleteItem(params)`, `scan(params, schema)`, `queryTable(params, schema)`, `batchWriteItem(params)`, `listTables(schema)`, `describeTable(params, schema)`, `deleteTable(params)` | 11 methods — supports both PartiQL and native CRUD |
|
|
762
|
+
| **CosmosDBClient** | `apiRequest(options, schema?)` | Azure Cosmos DB — use custom headers for queries |
|
|
763
|
+
| **CouchbaseClient** | `query<T>(bucket, sql, schema, params?)`, `insert(key, value, identifier)`, `get<T>(key, schema, identifier)`, `remove(key, identifier)` | N1QL queries + key-value operations |
|
|
764
|
+
| **S3Client** | `listBuckets(schema)`, `listObjects(params, schema)`, `getObject(params, schema)`, `deleteObject(params)`, `uploadObject(params)`, `uploadMultipleObjects(params)`, `generatePresignedUrl(params, schema)` | 7 methods for AWS S3 bucket/object operations |
|
|
765
|
+
| **GCSClient** | `listBuckets(schema)`, `listObjects(params, schema)`, `getObject(params, schema)`, `deleteObject(params)`, `uploadObject(params)`, `uploadMultipleObjects(params)`, `generateSignedUrl(params, schema)` | 7 methods for Google Cloud Storage operations |
|
|
766
|
+
| **SalesforceClient** | `query<T>(soql, schema)`, `create(params)`, `read(params, schema)`, `update(params)`, `remove(params)`, `bulkCreate(params)`, `bulkUpdate(params)`, `bulkDelete(params)`, `bulkUpsert(params)` | 9 methods — SOQL queries + CRUD + bulk operations |
|
|
767
|
+
| **KinesisClient** | `putRecord(params)`, `getRecords(params)` | AWS Kinesis stream put/get |
|
|
768
|
+
| **GoogleSheetsClient** | `run(action, schema, params, metadata?)` | 6 actions (READ_SPREADSHEET, APPEND, etc.) |
|
|
769
|
+
| **KafkaClient** | `consume(schema, params, metadata?)`, `produce(schema, params, metadata?)` | Kafka message consumption and production |
|
|
770
|
+
| **ConfluentClient** | `consume(schema, params, metadata?)`, `produce(schema, params, metadata?)` | Confluent Kafka — same API as KafkaClient |
|
|
771
|
+
| **RedpandaClient** | `consume(schema, params, metadata?)`, `produce(schema, params, metadata?)` | Redpanda — same API as KafkaClient |
|
|
772
|
+
| **SmtpClient** | `send(params)` | Send emails via SMTP |
|
|
773
|
+
| **SuperblocksOCRClient** | `apiRequest(options, schema?)` | OCR document processing |
|
|
774
|
+
| **SnowflakeCortexClient** | `apiRequest(options, schema?)` | Snowflake Cortex AI |
|
|
775
|
+
| **RestApiIntegrationPluginClient** | `apiRequest(options, schema)` | Generic REST API Integration - works with any configured HTTP API. |
|
|
460
776
|
|
|
461
777
|
#### Common Mistakes to Avoid
|
|
462
778
|
|
|
@@ -478,7 +794,7 @@ await myApi.request({ ... }); // .request() does NOT exist on any client
|
|
|
478
794
|
```typescript
|
|
479
795
|
// Use the generic apiRequest method
|
|
480
796
|
await openai.apiRequest(
|
|
481
|
-
{ method: 'POST', path: '/
|
|
797
|
+
{ method: 'POST', path: '/chat/completions', body: { ... } },
|
|
482
798
|
{ response: ResponseSchema }
|
|
483
799
|
);
|
|
484
800
|
```
|
|
@@ -521,7 +837,18 @@ Each integration client has detailed documentation with examples and common pitf
|
|
|
521
837
|
#### SQL Databases
|
|
522
838
|
|
|
523
839
|
- [PostgreSQL](./src/integrations/postgres/README.md) - Query PostgreSQL databases with schema validation
|
|
840
|
+
- [MySQL](./src/integrations/mysql/README.md) - Query MySQL databases
|
|
841
|
+
- [MariaDB](./src/integrations/mariadb/README.md) - Query MariaDB databases
|
|
842
|
+
- [MSSQL](./src/integrations/mssql/README.md) - Query Microsoft SQL Server databases
|
|
843
|
+
- [CockroachDB](./src/integrations/cockroachdb/README.md) - Query CockroachDB databases
|
|
844
|
+
- [OracleDB](./src/integrations/oracledb/README.md) - Query Oracle databases
|
|
524
845
|
- [Snowflake](./src/integrations/snowflake/README.md) - Query Snowflake data warehouses
|
|
846
|
+
- [Redshift](./src/integrations/redshift/README.md) - Query Amazon Redshift
|
|
847
|
+
- [BigQuery](./src/integrations/bigquery/README.md) - Query Google BigQuery
|
|
848
|
+
- [Databricks](./src/integrations/databricks/README.md) - Query Databricks SQL warehouses
|
|
849
|
+
- [Athena](./src/integrations/athena/README.md) - Query Amazon Athena
|
|
850
|
+
- [Lakebase](./src/integrations/lakebase/README.md) - Query Lakebase databases
|
|
851
|
+
- [SnowflakePostgres](./src/integrations/snowflakepostgres/README.md) - Query Snowflake via Postgres protocol
|
|
525
852
|
|
|
526
853
|
#### GraphQL
|
|
527
854
|
|
|
@@ -575,24 +902,50 @@ Each integration client has detailed documentation with examples and common pitf
|
|
|
575
902
|
- [Datadog](./src/integrations/datadog/README.md) - Metrics, monitors, and logs
|
|
576
903
|
- [Segment](./src/integrations/segment/README.md) - Customer data platform
|
|
577
904
|
|
|
905
|
+
#### NoSQL Databases
|
|
906
|
+
|
|
907
|
+
- [MongoDB](./src/integrations/mongodb/README.md) - Document database with 13 actions (find, insertOne, updateOne, etc.)
|
|
908
|
+
- [DynamoDB](./src/integrations/dynamodb/README.md) - AWS DynamoDB with PartiQL and native CRUD
|
|
909
|
+
- [CosmosDB](./src/integrations/cosmosdb/README.md) - Azure Cosmos DB via apiRequest
|
|
910
|
+
- [Couchbase](./src/integrations/couchbase/README.md) - N1QL queries and key-value operations
|
|
911
|
+
|
|
578
912
|
#### Cloud Storage
|
|
579
913
|
|
|
914
|
+
- [S3](./src/integrations/s3/README.md) - AWS S3 bucket and object operations
|
|
915
|
+
- [GCS](./src/integrations/gcs/README.md) - Google Cloud Storage operations
|
|
580
916
|
- [Google Drive](./src/integrations/googledrive/README.md) - Files, folders, and sharing
|
|
581
917
|
- [Dropbox](./src/integrations/dropbox/README.md) - Cloud file storage
|
|
582
918
|
- [Box](./src/integrations/box/README.md) - Enterprise content management
|
|
583
919
|
|
|
584
|
-
#### Code Execution
|
|
585
|
-
|
|
586
|
-
- [Python](./src/integrations/python/README.md) - Execute Python scripts with type-safe inputs and outputs
|
|
587
|
-
|
|
588
920
|
#### Generic HTTP
|
|
589
921
|
|
|
590
922
|
- [REST API Integration](./src/integrations/restapiintegration/README.md) - Make HTTP requests to any configured REST API
|
|
591
923
|
|
|
924
|
+
#### CRM
|
|
925
|
+
|
|
926
|
+
- [Salesforce](./src/integrations/salesforce/README.md) - SOQL queries, CRUD, and bulk operations
|
|
927
|
+
|
|
928
|
+
#### Event Streaming
|
|
929
|
+
|
|
930
|
+
- [Kafka](./src/integrations/kafka/README.md) - Consume and produce Kafka messages
|
|
931
|
+
- [Kinesis](./src/integrations/kinesis/README.md) - AWS Kinesis stream put/get
|
|
932
|
+
- Confluent — same API as [Kafka](./src/integrations/kafka/README.md)
|
|
933
|
+
- Redpanda — same API as [Kafka](./src/integrations/kafka/README.md)
|
|
934
|
+
|
|
935
|
+
#### Email
|
|
936
|
+
|
|
937
|
+
- [SMTP](./src/integrations/smtp/README.md) - Send emails via SMTP
|
|
938
|
+
|
|
592
939
|
#### Other
|
|
593
940
|
|
|
594
941
|
- [Elasticsearch](./src/integrations/elasticsearch/README.md) - Search and analytics engine
|
|
595
942
|
- [Stability AI](./src/integrations/stabilityai/README.md) - Image generation and editing
|
|
943
|
+
- [Superblocks OCR](./src/integrations/superblocks-ocr/README.md) - OCR document processing
|
|
944
|
+
- [Snowflake Cortex](./src/integrations/snowflakecortex/README.md) - Snowflake Cortex AI
|
|
945
|
+
|
|
946
|
+
### Streaming / SSE
|
|
947
|
+
|
|
948
|
+
> **Note:** The SDK's `apiRequest()` method does **not** support streaming or Server-Sent Events (SSE) responses. All AI provider calls (OpenAI, Anthropic, Gemini, Groq, Mistral, Cohere, Perplexity, Fireworks) return complete responses — the SDK waits for the full response before returning. If you are building a chat interface that needs real-time token streaming, you will need to handle streaming at the frontend/UI layer rather than through the SDK.
|
|
596
949
|
|
|
597
950
|
## Generic API Requests
|
|
598
951
|
|
|
@@ -623,7 +976,6 @@ export default api({
|
|
|
623
976
|
});
|
|
624
977
|
|
|
625
978
|
const PostMessageResponseSchema = z.object({
|
|
626
|
-
ok: z.boolean(),
|
|
627
979
|
ts: z.string(),
|
|
628
980
|
channel: z.string(),
|
|
629
981
|
});
|
|
@@ -644,7 +996,11 @@ export default api({
|
|
|
644
996
|
},
|
|
645
997
|
);
|
|
646
998
|
|
|
647
|
-
|
|
999
|
+
if (!result.ok) {
|
|
1000
|
+
throw new Error(`Slack API error: ${result.error}`);
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
// result is fully typed as { ok: true } & PostMessageResponseSchema
|
|
648
1004
|
return { ts: result.ts };
|
|
649
1005
|
},
|
|
650
1006
|
});
|
|
@@ -711,7 +1067,7 @@ const result = await ctx.integrations.slack.apiRequest(
|
|
|
711
1067
|
{ label: "Send alert to Slack" },
|
|
712
1068
|
);
|
|
713
1069
|
|
|
714
|
-
// Other clients (MongoDB,
|
|
1070
|
+
// Other clients (MongoDB, S3, etc.)
|
|
715
1071
|
const doc = await ctx.integrations.mongo.run(
|
|
716
1072
|
"users",
|
|
717
1073
|
"findOne",
|
|
@@ -719,10 +1075,6 @@ const doc = await ctx.integrations.mongo.run(
|
|
|
719
1075
|
{ filter: { _id: userId } },
|
|
720
1076
|
{ label: "Look up user by ID" },
|
|
721
1077
|
);
|
|
722
|
-
|
|
723
|
-
await ctx.integrations.cache.get("session:" + sessionId, {
|
|
724
|
-
label: "Check session cache",
|
|
725
|
-
});
|
|
726
1078
|
```
|
|
727
1079
|
|
|
728
1080
|
### When to Use
|
|
@@ -746,6 +1098,7 @@ import {
|
|
|
746
1098
|
QueryValidationError,
|
|
747
1099
|
RestApiValidationError,
|
|
748
1100
|
IntegrationNotFoundError,
|
|
1101
|
+
IntegrationError,
|
|
749
1102
|
ExecutionError,
|
|
750
1103
|
ErrorCode,
|
|
751
1104
|
} from "@superblocksteam/sdk-api";
|
|
@@ -788,7 +1141,7 @@ Thrown when REST API request bodies or responses fail schema validation. Include
|
|
|
788
1141
|
```typescript
|
|
789
1142
|
try {
|
|
790
1143
|
const result = await ctx.integrations.ai.apiRequest(
|
|
791
|
-
{ method: 'POST', path: '/
|
|
1144
|
+
{ method: 'POST', path: '/chat/completions', body: { ... } },
|
|
792
1145
|
{ body: RequestSchema, response: ResponseSchema }
|
|
793
1146
|
);
|
|
794
1147
|
} catch (error) {
|
|
@@ -1078,28 +1431,20 @@ The app template provides automatic type inference for API calls using a tRPC-st
|
|
|
1078
1431
|
|
|
1079
1432
|
### Adding a New API (2 Steps)
|
|
1080
1433
|
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
**Step 2**: Add it to the registry at `server/apis/index.ts`:
|
|
1434
|
+
1. Add a module under `server/apis/` (any layout; see [export styles](#export-style-default-vs-named-exports)).
|
|
1435
|
+
2. Register it in `server/apis/index.ts` (`.js` specifiers for ESM):
|
|
1084
1436
|
|
|
1085
1437
|
```typescript
|
|
1086
|
-
import
|
|
1087
|
-
import
|
|
1438
|
+
import CreateOrder from "./orders/create-order.js";
|
|
1439
|
+
import GetUsers from "./users/get-users.js";
|
|
1440
|
+
import { ListProfiles, UpdateProfile } from "./users/pair.js";
|
|
1088
1441
|
|
|
1089
|
-
const apis = {
|
|
1090
|
-
GetUsers,
|
|
1091
|
-
CreateOrder,
|
|
1092
|
-
// Add new APIs here
|
|
1093
|
-
} as const;
|
|
1442
|
+
const apis = { CreateOrder, GetUsers, ListProfiles, UpdateProfile } as const;
|
|
1094
1443
|
|
|
1095
1444
|
export default apis;
|
|
1096
1445
|
export type ApiRegistry = typeof apis;
|
|
1097
1446
|
```
|
|
1098
1447
|
|
|
1099
|
-
That's it! Types automatically flow to the frontend.
|
|
1100
|
-
|
|
1101
|
-
> **Note**: Always use `.js` extension in imports (required for ESM module resolution).
|
|
1102
|
-
|
|
1103
1448
|
### Calling APIs from React Components
|
|
1104
1449
|
|
|
1105
1450
|
Import `useApi` from the template's pre-configured hook:
|
|
@@ -1142,13 +1487,15 @@ The hook uses `import type` to pull in only the type information from the regist
|
|
|
1142
1487
|
|
|
1143
1488
|
### Alternative: Explicit Type Parameter
|
|
1144
1489
|
|
|
1145
|
-
|
|
1490
|
+
When you cannot use the registry hook, pass the compiled API type:
|
|
1146
1491
|
|
|
1147
1492
|
```typescript
|
|
1148
1493
|
import { useApi } from "@superblocksteam/library";
|
|
1149
|
-
import type GetUsersApi from "../../server/apis/
|
|
1494
|
+
import type GetUsersApi from "../../server/apis/users/get-users.js";
|
|
1495
|
+
import type { ListProfiles } from "../../server/apis/users/pair.js";
|
|
1150
1496
|
|
|
1151
|
-
const
|
|
1497
|
+
const getUsers = useApi<typeof GetUsersApi>("GetUsers");
|
|
1498
|
+
const listProfiles = useApi<typeof ListProfiles>("ListProfiles");
|
|
1152
1499
|
```
|
|
1153
1500
|
|
|
1154
1501
|
## Complete Example
|
|
@@ -1193,7 +1540,6 @@ const CustomerSchema = z.object({
|
|
|
1193
1540
|
});
|
|
1194
1541
|
|
|
1195
1542
|
const SlackPostMessageResponseSchema = z.object({
|
|
1196
|
-
ok: z.boolean(),
|
|
1197
1543
|
ts: z.string().optional(),
|
|
1198
1544
|
});
|
|
1199
1545
|
|
|
@@ -1250,7 +1596,7 @@ export default api({
|
|
|
1250
1596
|
const orderId = `ord_${Date.now()}`;
|
|
1251
1597
|
|
|
1252
1598
|
// Notify via Slack
|
|
1253
|
-
await ctx.integrations.notifier.apiRequest(
|
|
1599
|
+
const slackResult = await ctx.integrations.notifier.apiRequest(
|
|
1254
1600
|
{
|
|
1255
1601
|
method: "POST",
|
|
1256
1602
|
path: "/chat.postMessage",
|
|
@@ -1262,13 +1608,17 @@ export default api({
|
|
|
1262
1608
|
{ response: SlackPostMessageResponseSchema },
|
|
1263
1609
|
);
|
|
1264
1610
|
|
|
1611
|
+
if (!slackResult.ok) {
|
|
1612
|
+
throw new Error(`Slack API error: ${slackResult.error}`);
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1265
1615
|
// Generate AI summary if requested
|
|
1266
1616
|
let aiSummary: string | undefined;
|
|
1267
1617
|
if (sendAiSummary) {
|
|
1268
1618
|
const completion = await ctx.integrations.ai.apiRequest(
|
|
1269
1619
|
{
|
|
1270
1620
|
method: "POST",
|
|
1271
|
-
path: "/
|
|
1621
|
+
path: "/chat/completions",
|
|
1272
1622
|
body: {
|
|
1273
1623
|
model: "gpt-4",
|
|
1274
1624
|
messages: [
|