@squidcloud/cli 1.0.446 → 1.0.447

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.
@@ -1,2503 +1,96 @@
1
1
  ---
2
- name: Squid Development
3
- description: Provides comprehensive, code-verified knowledge about developing applications with Squid. Use this skill proactively before writing, editing, or creating any code that uses Squid's Client SDK, Backend SDK, or CLI to ensure compliance with Squid's APIs and best practices.
2
+ name: squid-development
3
+ description: Provides code-verified knowledge for developing Squid applications. Use proactively when writing code with @squidcloud/client, @squidcloud/backend, or using the Squid CLI.
4
4
  ---
5
5
 
6
- # Squid Development Skill (Verified)
7
-
8
- This skill provides comprehensive, code-verified knowledge about developing applications with Squid.
6
+ # Squid Development Skill
9
7
 
10
8
  ## Overview
11
9
 
12
10
  Squid is a backend-as-a-service platform that provides:
13
- - **Client SDK** (`@squidcloud/client`) - TypeScript/JavaScript SDK for frontend
14
- - **Backend SDK** (`@squidcloud/backend`) - TypeScript SDK with decorators for backend
15
- - **CLI** (`@squidcloud/cli`) - Local development and deployment tools
16
- - **Built-in integrations** - Databases, queues, storage, AI, APIs
17
-
18
- ## Squid Architecture: The Three Parts
19
-
20
- When developing with Squid, you work with three distinct but interconnected parts:
21
-
22
- ### 1. Client (Frontend/External Services)
23
-
24
- **What it is:** Any application that needs to interact with your Squid backend - web apps, mobile apps, desktop apps, or even other servers.
25
-
26
- **How it works:**
27
- - Initializes the Squid Client SDK (`@squidcloud/client`)
28
- - Communicates with Squid functionality over HTTP/WebSocket
29
- - Can interact with AI agents, modify agent settings, query databases, call backend functions, etc.
30
- - Optionally uses authentication (OAuth2.0) for user-specific access
31
- - Respects security rules defined in the backend
32
-
33
- **Common use cases:**
34
- ```typescript
35
- // Web/mobile application
36
- const squid = new Squid({
37
- appId: 'your-app-id',
38
- region: 'us-east-1.aws',
39
- authProvider: { /* user auth */ }
40
- });
41
-
42
- // Interact with agents
43
- const agent = squid.ai().agent('support-agent');
44
- const response = await agent.ask('How do I reset my password?');
45
-
46
- // Query databases
47
- const users = await squid.collection('users').query().eq('active', true).snapshot();
48
-
49
- // Call backend functions
50
- const result = await squid.executeFunction('processPayment', paymentData);
51
- ```
52
-
53
- **Key point:** Clients have limited, controlled access based on security rules you define in the backend.
54
-
55
- ### 2. Squid Backend (Your Server-Side Code)
56
-
57
- **What it is:** TypeScript code that runs on Squid's infrastructure, containing your business logic, security rules, and integrations.
58
-
59
- **How it works:**
60
- - Contains services that extend `SquidService`
61
- - Uses **decorators** to define functionality:
62
- - `@executable()` - Backend functions callable from clients
63
- - `@webhook()` - HTTP endpoints for external services
64
- - `@trigger()` - React to database changes
65
- - `@scheduler()` - Run code on a schedule (cron jobs)
66
- - `@secureDatabase()`, `@secureCollection()` - Define who can access data
67
- - `@secureAiAgent()` - Control agent access
68
- - `@aiFunction()` - Functions that AI agents can call
69
- - And many more decorators for security, rate limiting, etc.
70
- - Has **full access** to all Squid functionality via pre-initialized `this.squid` client (with API key permissions)
71
- - Deployed to Squid infrastructure using `squid deploy`
72
-
73
- **Example backend service:**
74
- ```typescript
75
- import { SquidService, executable, scheduler, trigger, secureCollection } from '@squidcloud/backend';
76
-
77
- export class MyService extends SquidService {
78
- // Backend function callable from client
79
- @executable()
80
- async processPayment(amount: number, userId: string): Promise<PaymentResult> {
81
- // Full access to Squid - this.squid has API key permissions
82
- const user = await this.squid.collection('users').doc({ id: userId }).snapshot();
83
-
84
- // Your business logic here
85
- return { success: true, transactionId: '...' };
86
- }
87
-
88
- // React to database changes
89
- @trigger({ id: 'new-user', collection: 'users', mutationTypes: ['insert'] })
90
- async onNewUser(request: TriggerRequest<User>): Promise<void> {
91
- await sendWelcomeEmail(request.docAfter);
92
- }
93
-
94
- // Scheduled task (cron job)
95
- @scheduler({ id: 'daily-report', cron: '0 0 * * *' })
96
- async generateDailyReport(): Promise<void> {
97
- // Runs every day at midnight
98
- const stats = await this.calculateStats();
99
- await this.sendReport(stats);
100
- }
101
-
102
- // Security rule - who can read users collection
103
- @secureCollection('users', 'read')
104
- allowUserRead(context: QueryContext): boolean {
105
- const userId = this.getUserAuth()?.userId;
106
- // Only allow users to read their own data
107
- return context.isSubqueryOf('id', '==', userId);
108
- }
109
-
110
- // Function that AI agents can call
111
- @aiFunction('Gets order status', [
112
- { name: 'orderId', type: 'string', required: true }
113
- ])
114
- async getOrderStatus({ orderId }: { orderId: string }): Promise<string> {
115
- const order = await this.squid.collection('orders').doc({ id: orderId }).snapshot();
116
- return order?.status || 'not found';
117
- }
118
- }
119
- ```
120
-
121
- **Key points:**
122
- - Backend code has **full permissions** (API key access)
123
- - Defines **what clients can and cannot do** through security decorators
124
- - Contains **business logic** that shouldn't be exposed to clients
125
- - Automatically deployed and scaled by Squid
126
-
127
- ### 3. Squid Console (Web UI)
128
-
129
- **What it is:** A web-based management interface at https://console.getsquid.ai/ for setting up and managing your Squid applications.
130
-
131
- **What you can do in the Console:**
132
- - **Organizations & Applications:**
133
- - Create organizations (teams/workspaces)
134
- - Create applications (projects)
135
- - Manage API keys and secrets
136
- - View application logs and metrics
137
-
138
- - **AI Studio:**
139
- - Create and configure AI agents visually
140
- - Set up agent instructions, models, and guardrails
141
- - Test agents in real-time
142
- - Connect agents to functions and knowledge bases
143
-
144
- - **Knowledge Bases:**
145
- - Create knowledge bases for RAG (Retrieval Augmented Generation)
146
- - Upload documents and manage contexts
147
- - Configure embedding models
148
-
149
- - **Integrations:**
150
- - Connect databases (PostgreSQL, MySQL, MongoDB, etc.)
151
- - Connect SaaS services (Stripe, Slack, etc.)
152
- - Configure OAuth integrations
153
- - Set up storage providers (AWS S3, etc.)
154
- - Configure message queues (Kafka, etc.)
155
-
156
- - **Monitoring & Debugging:**
157
- - View real-time logs
158
- - Monitor API usage
159
- - Debug agent conversations
160
- - Track performance metrics
161
-
162
- **Console vs SDK:**
163
- - **Console is great for:** Initial setup, visual agent configuration, quick testing, monitoring
164
- - **SDK is more powerful for:** Programmatic control, CI/CD pipelines, dynamic configuration, complex automation
165
-
166
- **Example: Everything in Console can be done via SDK (except creating orgs/apps):**
167
-
168
- ```typescript
169
- // Console: Click to create an agent with AI Studio
170
- // SDK equivalent:
171
- const agent = squid.ai().agent('my-agent');
172
- await agent.upsert({
173
- description: 'Customer support agent',
174
- options: {
175
- model: 'gpt-4o',
176
- instructions: 'You are a helpful support agent...'
177
- }
178
- });
179
-
180
- // Console: Upload document to knowledge base
181
- // SDK equivalent:
182
- const kb = squid.ai().knowledgeBase('docs');
183
- await kb.upsertContext({
184
- contextId: 'doc-1',
185
- text: 'Product documentation...'
186
- }, pdfFile);
187
-
188
- // Console: Configure database integration
189
- // SDK equivalent:
190
- const integrations = squid.admin().integrations();
191
- await integrations.upsertIntegration({
192
- integrationId: 'my-db',
193
- type: 'postgres',
194
- connectionString: 'postgresql://...'
195
- });
196
- ```
197
-
198
- **Key point:** The Console provides a visual interface for management, and everything can be automated through the SDK for more flexibility and control. For organization and application management, use the **ManagementClient** (see below).
199
-
200
- ### 4. ManagementClient (Programmatic Org/App Management)
201
-
202
- **What it is:** A specialized client for programmatically managing Squid organizations and applications using management API keys.
203
-
204
- **How it works:**
205
- - Uses management API keys (created in Console Profile Settings)
206
- - Provides methods to create organizations and applications
207
- - Works independently of the main Squid client
208
- - Authenticated via management API key (starts with `squid_mgmt_`)
209
-
210
- **Creating Management API Keys:**
211
- 1. Go to Squid Console → Profile Settings → API Keys
212
- 2. Click "Create API Key"
213
- 3. Copy the key value (shown only once)
214
- 4. Key format: `squid_mgmt_<uuid>`
215
-
216
- **Usage:**
217
-
218
- ```typescript
219
- import { ManagementClient } from '@squidcloud/client';
220
-
221
- // Initialize with management API key
222
- const client = new ManagementClient({
223
- apiKey: 'squid_mgmt_xxx...', // From Console Profile Settings
224
- region: 'us-east-1.aws' // Your Squid region
225
- });
226
-
227
- // Create an organization
228
- const { organizationId } = await client.createOrganization({
229
- name: 'My Organization' // 2-50 characters
230
- });
231
- console.log('Created org:', organizationId);
232
-
233
- // Create an application in the organization
234
- const { appId, devApiKey, prodApiKey } = await client.createApplication({
235
- organizationId,
236
- name: 'My Application',
237
- region: 'us-east-1.aws' // SquidRegion (e.g., 'us-east-1.aws', 'us-central1.gcp')
238
- });
239
- console.log('Created app:', appId);
240
-
241
- // Delete an application
242
- await client.deleteApplication(appId);
243
- ```
244
-
245
- **Available Methods:**
246
-
247
- | Method | Description |
248
- |--------|-------------|
249
- | `createOrganization({ name })` | Creates a new organization. User becomes admin. |
250
- | `createApplication({ organizationId, name, region })` | Creates an application in an organization. Returns `{ appId, devApiKey, prodApiKey }`. |
251
- | `deleteApplication(appId)` | Deletes an application. Requires admin role. |
252
-
253
- **TypeScript Types:**
254
-
255
- ```typescript
256
- import {
257
- ManagementClient,
258
- ManagementClientOptions,
259
- ManagementCreateOrganizationRequest,
260
- ManagementCreateOrganizationResponse,
261
- ManagementCreateApplicationRequest,
262
- ManagementCreateApplicationResponse,
263
- } from '@squidcloud/client';
264
- ```
265
-
266
- **Key Points:**
267
- - Management API keys are tied to a user account
268
- - The user associated with the key becomes the organization admin
269
- - Keys can be suspended/reactivated/deleted in Console Profile Settings
270
- - Different from app API keys (which bypass security rules)
271
- - Use for CI/CD pipelines, automation scripts, and programmatic management
272
-
273
- **Use Cases:**
274
- - Automated tenant provisioning (multi-tenant SaaS)
275
- - CI/CD pipeline integration
276
- - Infrastructure-as-code workflows
277
- - Programmatic organization management
278
- - Automated testing environments
279
-
280
- ### How They Work Together
281
-
282
- ```
283
- ┌─────────────────────┐ ┌─────────────────────┐
284
- │ CLIENT (Web/ │ │ MANAGEMENT CLIENT │
285
- │ Mobile/Server) │ │ (Automation) │
286
- │ │ │ │
287
- │ - Squid SDK init │ │ - ManagementClient │
288
- │ - User auth │ │ - Management API │
289
- │ - Limited access │ │ key auth │
290
- └──────────┬──────────┘ │ - Create orgs/apps │
291
- │ └──────────┬──────────┘
292
- │ │
293
- │ HTTP/WebSocket │ HTTP
294
- │ │
295
- ▼ ▼
296
- ┌─────────────────────────────────────────────────┐
297
- │ SQUID BACKEND │
298
- │ (Your Code) │
299
- │ │
300
- │ - SquidService - Decorators │
301
- │ - Full permissions - Business logic │
302
- │ - Security rules - Webhooks │
303
- └──────────────────────────┬──────────────────────┘
304
-
305
- │ Deployed via CLI
306
- │ or managed via
307
-
308
- ┌─────────────────────────────────────────────────┐
309
- │ SQUID CONSOLE │
310
- │ (Web UI) │
311
- │ │
312
- │ - Manage apps - AI Studio │
313
- │ - Integrations - Monitoring │
314
- │ - Management API Keys - Profile Settings │
315
- └─────────────────────────────────────────────────┘
316
- ```
317
-
318
- **Development workflow:**
319
- 1. Create application in **Squid Console** (or programmatically via **ManagementClient**)
320
- 2. Develop backend services in **Squid Backend** (local development with `squid start`)
321
- 3. Deploy backend to Squid infrastructure (`squid deploy`)
322
- 4. Build **Client** applications that use the Squid Client SDK
323
- 5. Use **Console** for monitoring and management, or use **SDK** for programmatic control
324
- 6. Use **ManagementClient** for automated org/app provisioning (CI/CD, multi-tenant SaaS)
325
-
326
- ## CLI Commands
327
-
328
- ### Installation
329
- ```bash
330
- npm install -g @squidcloud/cli
331
- ```
332
-
333
- ### `squid init <project-name>`
334
- Creates new backend project with `.env` and example service.
335
- The backend project has access to an already initialized client SDK with API key (full permissions)
336
-
337
- ```bash
338
- squid init backend --appId YOUR_APP_ID --apiKey YOUR_API_KEY --environmentId dev --squidDeveloperId YOUR_DEV_ID --region us-east-1.aws
339
- ```
340
-
341
- ### `squid start`
342
- Runs backend locally with hot-reload, connects to Squid Cloud via reverse proxy.
343
-
344
- ```bash
345
- cd backend
346
- squid start
347
- ```
348
-
349
- **Extended logging in `.env`:**
350
- ```env
351
- SQUID_LOG_TYPES=QUERY,MUTATION,AI,API,ERROR
352
- ```
353
-
354
- ### `squid deploy`
355
- Builds and deploys to Squid Cloud.
356
-
357
- ```bash
358
- squid deploy [--apiKey KEY] [--environmentId prod] [--skipBuild]
359
- ```
360
-
361
- ### `squid build`
362
- Builds backend project.
363
-
364
- ```bash
365
- squid build [--dev] [--skip-version-check]
366
- ```
367
-
368
- ## Client SDK
369
-
370
- ### Where Can You Use the SDK?
371
-
372
- The Squid Client SDK can be instantiated in various environments:
373
- - **Web applications** (vanilla JavaScript, React, Vue, Angular, etc.)
374
- - **Node.js servers** (Express, Fastify, etc.)
375
- - **Mobile applications** (React Native, etc.)
376
- - **Desktop applications** (Electron, etc.)
377
-
378
- **Important:** When writing backend code that runs on Squid's infrastructure, the Squid client is **already initialized and available with API key and full permissions** for you (see Backend SDK section). You don't need to manually create a new instance.
379
-
380
- ### Initialization
381
-
382
- #### Configuration Options
383
-
384
- **Required Parameters:**
385
- - **`appId`** (string): Your Squid application identifier. Can be found in the Squid Console.
386
- - **`region`** (string): The region where your Squid application is deployed:
387
- - AWS regions: `'us-east-1.aws'`, `'ap-south-1.aws'`, `'eu-central-1.aws'`
388
- - GCP regions: `'us-central1.gcp'`
389
-
390
- **Optional Parameters:**
391
- - **`apiKey`** (string): API key that bypasses security rules and provides full administrative access.
392
- - **IMPORTANT**: Has **complete control over your application** and bypasses all security rules
393
- - Only use in trusted environments (backend servers, admin tools, development)
394
- - **Never expose in client-side code** (web browsers, mobile apps)
395
- - **`authProvider`** (object): Authentication provider that supplies OAuth2.0 tokens for the current user
396
- - `integrationId` (string): The ID of your auth integration
397
- - `getToken` (function): Returns a promise that resolves to the user's auth token (or undefined if not authenticated)
398
- - Authentication details are **available in backend functions** via the execution context
399
- - **`environmentId`** (string): Environment identifier (dev, staging, prod). Defaults to production.
400
- - **`squidDeveloperId`** (string): Your developer identifier, used for local development and debugging.
401
- - **`consoleRegion`** (string): Console region (optional)
402
-
403
- #### Basic Setup
404
-
405
- ```typescript
406
- import { Squid } from '@squidcloud/client';
407
-
408
- const squid = new Squid({
409
- appId: 'your-app-id',
410
- region: 'us-east-1.aws',
411
- environmentId: 'dev' // optional
412
- });
413
- ```
414
-
415
- #### Authentication: API Key vs Auth Provider
416
-
417
- **Using API Key (Server-Side Only):**
418
- ```typescript
419
- const squid = new Squid({
420
- appId: 'your-app-id',
421
- region: 'us-east-1.aws',
422
- apiKey: 'your-api-key' // Full admin access
423
- });
424
- ```
425
- - **Use cases**: Backend servers, admin tools, scripts, development environments
426
- - **Security**: Has full control - bypasses all security rules
427
- - **Warning**: Never use in client-side code
428
-
429
- **Using Auth Provider (Client-Side):**
430
- ```typescript
431
- const squid = new Squid({
432
- appId: 'your-app-id',
433
- region: 'us-east-1.aws',
434
- authProvider: {
435
- integrationId: 'auth-integration-id',
436
- getToken: async () => {
437
- // Return user's OAuth token
438
- return await getCurrentUserToken();
439
- }
440
- }
441
- });
442
- ```
443
- - **Use cases**: Web apps, mobile apps, any user-facing application
444
- - **Security**: Respects security rules based on authenticated user
445
- - **Backend access**: User authentication details are available in backend functions through the execution context
446
-
447
- **Setting Auth Provider After Initialization:**
448
- ```typescript
449
- const squid = new Squid({
450
- appId: 'your-app-id',
451
- region: 'us-east-1.aws'
452
- });
453
-
454
- // Later, when user logs in
455
- squid.setAuthProvider({
456
- integrationId: 'auth-integration-id',
457
- getToken: async () => userAuthToken
458
- });
459
- ```
460
-
461
- ### Database & Data Management
462
-
463
- Squid provides database functionality similar to Firestore but more powerful, with collections, document references, and real-time capabilities. Squid supports multiple database types including NoSQL databases (like MongoDB) and relational databases (like PostgreSQL, MySQL).
464
-
465
- #### Security Rules
466
-
467
- Every database operation in Squid can be secured using **security rules**. Security rules are backend functions decorated with `@secureDatabase` or `@secureCollection` that contain business logic to determine whether an operation is allowed.
468
-
469
- ```typescript
470
- // Example security rule for a collection (read operations)
471
- @secureCollection('users', 'read')
472
- async canReadUsers(context: QueryContext<User>): Promise<boolean> {
473
- // Your business logic here
474
- // context.collectionName, context.integrationId, context.limit, etc.
475
- return true; // or false based on your logic
476
- }
477
-
478
- // Example security rule for mutations (insert, update, delete)
479
- @secureCollection('users', 'update')
480
- async canUpdateUsers(context: MutationContext): Promise<boolean> {
481
- // Access document before and after the mutation
482
- const before = context.before;
483
- const after = context.after;
484
-
485
- // Check if specific paths were affected
486
- if (context.affectsPath('email')) {
487
- return false; // Don't allow email changes
488
- }
489
-
490
- return true;
491
- }
492
-
493
- // Database-wide security rule
494
- @secureDatabase('insert', 'my-integration-id')
495
- async canInsertAnywhere(context: MutationContext): Promise<boolean> {
496
- // Your business logic here
497
- return context.after?.createdBy === 'admin';
498
- }
499
- ```
500
-
501
- These decorated functions return a boolean (or `Promise<boolean>`) indicating whether the operation is allowed. The developer has full control to implement any business logic needed for authorization.
502
-
503
- **Context types:**
504
- - `QueryContext` - Used for 'read' and 'all' operations (exported from `@squidcloud/backend`)
505
- - `MutationContext` - Used for 'insert', 'update', 'delete' operations (exported from `@squidcloud/backend`)
506
-
507
- Security decorators (`@secureDatabase`, `@secureCollection`) are exported from `@squidcloud/backend`.
508
-
509
- #### Collection Access
510
-
511
- A collection represents a set of documents (similar to a table in relational databases or a collection in NoSQL databases).
512
-
513
- ```typescript
514
- // Get collection reference in the built-in database
515
- const users = squid.collection<User>('users');
516
-
517
- // Get collection reference in a specific integration
518
- const orders = squid.collection<Order>('orders', 'postgres-db');
519
- ```
520
-
521
- **Type parameter**: The generic type `<T>` defines the structure of documents in the collection.
522
-
523
- **Document References - The `doc()` method has different behaviors:**
524
-
525
- **1. No parameters - `doc()`**
526
- Generates a new document ID (useful for creating new documents):
527
- ```typescript
528
- // For built_in_db without schema - generates a random ID
529
- const newDocRef = collection.doc();
530
- await newDocRef.insert({ name: 'John', age: 30 });
531
-
532
- // For other integrations - creates a placeholder that gets resolved on insert
533
- const newDocRef = collection.doc();
534
- ```
535
-
536
- **2. String parameter - `doc(stringId)`**
537
- **Only supported for `built_in_db` integration without a defined schema:**
538
- ```typescript
539
- // Valid only for built_in_db collections without schema
540
- const docRef = collection.doc('my-doc-id');
541
- const docRef = collection.doc('user-12345');
542
- ```
543
- **Important**: For collections with defined schemas or non-built_in_db integrations, you must use object format.
544
-
545
- **3. Object parameter - `doc({id: value})`**
546
- Used for collections with defined primary keys. The object maps primary key field names to their values:
547
- ```typescript
548
- // Single primary key field "id"
549
- const docRef = collection.doc({ id: 'user-123' });
550
-
551
- // Single primary key field "userId"
552
- const docRef = collection.doc({ userId: 42 });
553
-
554
- // Composite primary key (id1, id2)
555
- const docRef = collection.doc({ id1: 'part1', id2: 'part2' });
556
-
557
- // Partial primary key - missing fields generated on server if supported
558
- const docRef = collection.doc({ id1: 'part1' }); // id2 generated on insert
559
- ```
560
-
561
- **Key points about document IDs:**
562
- - For `built_in_db` without schema: can use string IDs or object format
563
- - For `built_in_db` with schema: must use object format matching primary key fields
564
- - For other integrations: must use object format matching primary key fields
565
- - Partial primary keys: missing fields may be auto-generated on insert (if integration supports it)
566
- - Empty `doc()`: generates a placeholder ID that gets resolved when document is created
567
-
568
- ```typescript
569
- // Examples:
570
-
571
- // Insert
572
- await userDoc.insert({ name: 'John', email: 'john@example.com' });
573
-
574
- // Update (partial)
575
- await userDoc.update({ name: 'Jane' });
576
-
577
- // Update specific path
578
- await userDoc.setInPath('address.street', 'Main St');
579
-
580
- // Delete specific path
581
- await userDoc.deleteInPath('tempData');
582
-
583
- // Delete document
584
- await userDoc.delete();
585
-
586
- // Get data (promise)
587
- const userData = await userDoc.snapshot();
588
- console.log(userData);
589
-
590
- // Get data (observable - realtime)
591
- userDoc.snapshots().subscribe(data => {
592
- console.log(data);
593
- });
594
-
595
- // Get cached data (no server fetch)
596
- const cached = userDoc.peek();
597
-
598
- // Check if document has data populated
599
- if (userDoc.hasData) {
600
- console.log('Document is loaded');
601
- // Access data directly (with defensive copy)
602
- console.log(userDoc.data);
603
- }
604
-
605
- // Bulk operations
606
- await users.insertMany([
607
- { id: 'user-1', data: { name: 'Alice' } },
608
- { id: 'user-2', data: { name: 'Bob' } }
609
- ]);
610
-
611
- await users.deleteMany(['user-1', 'user-2']);
612
- ```
613
-
614
- #### Real-time Subscriptions
615
-
616
- Squid provides real-time data synchronization similar to Firestore. Subscribe to document or query changes and receive updates automatically.
617
-
618
- **Document Subscriptions:**
619
- ```typescript
620
- // Subscribe to document changes
621
- const subscription = docRef.snapshots().subscribe((userData) => {
622
- if (userData) {
623
- console.log('Document updated:', userData);
624
- } else {
625
- console.log('Document deleted or does not exist');
626
- }
627
- });
628
-
629
- // Unsubscribe when done
630
- subscription.unsubscribe();
631
- ```
632
-
633
- **Query Subscriptions:**
634
- ```typescript
635
- // Subscribe to query results - ALWAYS use dereference()
636
- const subscription = collection
637
- .query()
638
- .eq('status', 'active')
639
- .gte('age', 18)
640
- .dereference() // Important: Converts DocumentReferences to actual data
641
- .snapshots()
642
- .subscribe((users) => {
643
- console.log('Active users updated:', users);
644
- // users is Array<User> with actual data
645
- });
646
-
647
- // Unsubscribe when done
648
- subscription.unsubscribe();
649
- ```
650
-
651
- **Real-time Updates:**
652
- When data changes on the server (from any client or backend operation):
653
- - Document subscriptions receive the updated document data
654
- - Query subscriptions receive the updated query results
655
- - Updates are pushed to clients via WebSocket connections
656
- - Changes are automatically reflected in the local data
657
-
658
- **Important**: Always unsubscribe from subscriptions when they're no longer needed to prevent memory leaks.
659
-
660
- ### Database - Queries
661
-
662
- **ALL Available Operators:**
663
- - Comparison: `eq`, `neq`, `gt`, `gte`, `lt`, `lte`
664
- - Arrays: `in`, `nin`, `arrayIncludesSome`, `arrayIncludesAll`, `arrayNotIncludes`
665
- - Patterns: `like`, `notLike` (% = any chars, _ = one char)
666
-
667
- ```typescript
668
- // Basic query
669
- const activeUsers = await users.query()
670
- .eq('status', 'active')
671
- .gt('age', 18)
672
- .sortBy('name', true) // true = ascending
673
- .limit(100) // max 20000, default 1000
674
- .snapshot();
675
-
676
- activeUsers.data.forEach(doc => {
677
- console.log(doc.data); // Document data
678
- });
679
-
680
- // Realtime query
681
- users.query()
682
- .eq('status', 'active')
683
- .snapshots().subscribe(snapshot => {
684
- snapshot.data.forEach(doc => console.log(doc.data));
685
- });
686
-
687
- // Pattern matching (CASE-INSENSITIVE by default)
688
- const results = await users.query()
689
- .like('email', '%.com') // case-insensitive by default
690
- .snapshot();
691
-
692
- // Case-sensitive pattern matching
693
- const caseSensitive = await users.query()
694
- .like('name', 'John%', true) // third parameter: caseSensitive = true
695
- .snapshot();
696
-
697
- // Array operators
698
- const tagged = await posts.query()
699
- .in('category', ['tech', 'news'])
700
- .arrayIncludesSome('tags', ['urgent', 'important'])
701
- .snapshot();
702
-
703
- // Using where() method (all operators above are shortcuts for where)
704
- const results1 = await users.query().eq('status', 'active').snapshot();
705
- const results2 = await users.query().where('status', '==', 'active').snapshot();
706
- // These are equivalent
707
-
708
- // OR queries - combine multiple queries with OR logic
709
- const query1 = users.query().eq('status', 'active');
710
- const query2 = users.query().eq('status', 'pending');
711
- const orResults = await users.or(query1, query2)
712
- .dereference()
713
- .snapshot();
714
- // Returns documents matching either query
715
- // Note: Results are merged and deduplicated
716
-
717
- // Multiple sort
718
- const sorted = await users.query()
719
- .sortBy('lastName', true)
720
- .sortBy('firstName', true)
721
- .limit(50)
722
- .snapshot();
723
-
724
- // Join Queries - combine data from multiple collections
725
- // Start with joinQuery() and alias
726
- const results = await teachers
727
- .joinQuery('teacher') // Alias for teachers collection
728
- .join(
729
- students.query(),
730
- 'students', // Alias for students collection
731
- { left: 'id', right: 'teacherId' } // Join condition
732
- )
733
- .dereference() // Important: converts refs to actual data
734
- .snapshot();
735
- // Results: Array<{ teacher: Teacher, students?: Student }>
736
-
737
- // Inner join (only matching records)
738
- const innerResults = await teachers
739
- .joinQuery('teacher')
740
- .join(
741
- students.query(),
742
- 'students',
743
- { left: 'id', right: 'teacherId' },
744
- { isInner: true } // Inner join option
745
- )
746
- .dereference()
747
- .snapshot();
748
-
749
- // Multi-level joins
750
- const threeWay = await collection1
751
- .joinQuery('a')
752
- .join(collection2.query(), 'b', { left: 'id', right: 'parentId' })
753
- .join(collection3.query(), 'c', { left: 'id', right: 'grandParentId' })
754
- .dereference()
755
- .snapshot();
756
-
757
- // Grouped joins (nests one-to-many as arrays)
758
- const grouped = await teachers
759
- .joinQuery('teacher')
760
- .join(students.query(), 'students', { left: 'id', right: 'teacherId' })
761
- .grouped()
762
- .dereference()
763
- .snapshot();
764
- // Results: Array<{ teacher: Teacher, students: Student[] }>
765
-
766
- // Dereference - Converts DocumentReferences to actual data
767
- // WITHOUT dereference: returns Array<DocumentReference<User>>
768
- const refs = await users.query().eq('active', true).snapshot();
769
- // refs.data[0].data to access actual data
770
-
771
- // WITH dereference: returns Array<User> directly
772
- const userData = await users.query()
773
- .eq('active', true)
774
- .dereference()
775
- .snapshot();
776
- // userData[0] is the actual user object
777
-
778
- // ALWAYS use dereference() for:
779
- // - Real-time subscriptions (makes working with data easier)
780
- // - When you need document data directly
781
- // DON'T use dereference() if:
782
- // - You need DocumentReference methods like .update() or .delete()
783
-
784
- // Pagination
785
- const pagination = users.query()
786
- .sortBy('createdAt', false)
787
- .dereference()
788
- .paginate({
789
- pageSize: 50, // Number of items per page (default: 100)
790
- subscribe: true // Subscribe to real-time updates (default: true)
791
- });
792
-
793
- const firstPage = await pagination.first(); // Jump to first page
794
- const nextPage = await pagination.next(); // Go to next page
795
- const prevPage = await pagination.prev(); // Go to previous page
796
-
797
- // Check pagination state
798
- console.log(firstPage.hasNext); // boolean
799
- console.log(firstPage.hasPrev); // boolean
800
-
801
- // Watch changes
802
- users.query()
803
- .eq('status', 'active')
804
- .changes()
805
- .subscribe(changes => {
806
- console.log('Inserts:', changes.inserts);
807
- console.log('Updates:', changes.updates);
808
- console.log('Deletes:', changes.deletes);
809
- });
810
- ```
811
-
812
- **Note:** `offset()` does NOT exist - use `paginate()` for pagination.
813
-
814
- ### Database - Transactions
815
-
816
- ```typescript
817
- await squid.runInTransaction(async (txId) => {
818
- const userRef = squid.collection('users').doc('user-1');
819
- const accountRef = squid.collection('accounts').doc('acc-1');
820
-
821
- // Pass txId as last parameter to each operation
822
- // Use incrementInPath/decrementInPath for numeric operations
823
- await userRef.decrementInPath('balance', 100, txId);
824
- await accountRef.incrementInPath('balance', 100, txId);
825
-
826
- // Both commit together or rollback together
827
- });
828
- ```
829
-
830
- ### Database - Numeric Operations
831
-
832
- ```typescript
833
- // Increment/decrement
834
- await userDoc.incrementInPath('loginCount', 1);
835
- await userDoc.decrementInPath('credits', 50);
836
-
837
- // For arrays/objects, use update() with full new value
838
- const currentData = await userDoc.snapshot();
839
- await userDoc.update({
840
- tags: [...currentData.tags, 'newTag'],
841
- notifications: [...currentData.notifications, { msg: 'Hi' }]
842
- });
843
- ```
844
-
845
- ### Database - Native Queries
846
-
847
- ```typescript
848
- // SQL (PostgreSQL, MySQL, etc.) - uses ${param} syntax
849
- const users = await squid.executeNativeRelationalQuery<User[]>(
850
- 'postgres-db',
851
- 'SELECT * FROM users WHERE age > ${minAge}',
852
- { minAge: 18 }
853
- );
854
-
855
- // MongoDB aggregation
856
- const orders = await squid.executeNativeMongoQuery<Order[]>(
857
- 'mongo-db',
858
- 'orders',
859
- [
860
- { $match: { status: 'completed' } },
861
- { $group: { _id: '$userId', total: { $sum: '$amount' } } }
862
- ]
863
- );
864
-
865
- // Elasticsearch
866
- const products = await squid.executeNativeElasticQuery(
867
- 'elastic-db',
868
- 'products',
869
- { query: { match: { name: 'laptop' } } },
870
- '_search', // endpoint (optional)
871
- 'GET' // method (optional)
872
- );
873
-
874
- // Pure (Finos Legend Pure Language) - uses ${param} syntax
875
- const items = await squid.executeNativePureQuery(
876
- 'my-db',
877
- 'from products->filter(p | $p.price < ${maxPrice})',
878
- { maxPrice: 1000 }
879
- );
880
- ```
881
-
882
- ### Database - Security (@secureDatabase, @secureCollection)
883
-
884
- Secure your database operations with backend decorators. These are backend-only decorators that define who can access your data.
885
-
886
- **Backend - Secure entire database:**
887
- ```typescript
888
- import { SquidService, secureDatabase, QueryContext, MutationContext } from '@squidcloud/backend';
889
-
890
- export class MyService extends SquidService {
891
- @secureDatabase('read')
892
- allowRead(context: QueryContext<User>): boolean {
893
- const userId = this.getUserAuth()?.userId;
894
- if (!userId) return false;
895
- return context.isSubqueryOf('userId', '==', userId);
896
- }
897
-
898
- @secureDatabase('write')
899
- allowWrite(context: MutationContext<User>): boolean {
900
- return this.isAuthenticated();
901
- }
902
-
903
- @secureDatabase('all')
904
- allowAll(): boolean {
905
- return this.isAuthenticated();
906
- }
907
- }
908
- ```
909
-
910
- **Types:** `'read'`, `'write'`, `'update'`, `'insert'`, `'delete'`, `'all'`
911
-
912
- **Backend - Secure specific collection:**
913
- ```typescript
914
- // QueryContext methods (for 'read' operations)
915
- @secureCollection('users', 'read')
916
- allowUserRead(context: QueryContext): boolean {
917
- const userId = this.getUserAuth()?.userId;
918
- if (!userId) return false;
919
- // Check if query filters on a specific field
920
- return context.isSubqueryOf('id', '==', userId);
921
- }
922
-
923
- // MutationContext methods (for 'insert', 'update', 'delete' operations)
924
- @secureCollection('users', 'update')
925
- allowUserUpdate(context: MutationContext<User>): boolean {
926
- const userId = this.getUserAuth()?.userId;
927
- if (!userId) return false;
928
-
929
- // context.before: document state before mutation
930
- // context.after: document state after mutation
931
-
932
- // Check if specific paths were affected
933
- if (context.affectsPath('email')) {
934
- return false; // Don't allow email changes
935
- }
936
-
937
- // Check ownership
938
- return context.after?.id === userId;
939
- }
940
- ```
941
-
942
- **Context types (exported from `@squidcloud/backend`):**
943
- - `QueryContext<T>` - Used for 'read' and 'all' operations
944
- - `isSubqueryOf(field, operator, value)` - Check if query filters on field
945
- - `collectionName`, `integrationId`, `limit` properties
946
- - `MutationContext<T>` - Used for 'insert', 'update', 'delete' operations
947
- - `affectsPath(path)` - Check if specific field path was modified
948
- - `before` - Document before mutation (undefined for insert)
949
- - `after` - Document after mutation (undefined for delete)
950
-
951
- ### Backend Functions (@executable)
952
-
953
- Backend functions allow you to write custom server-side logic that can be called from the client.
954
-
955
- **Backend - Define the function:**
956
- ```typescript
957
- import { SquidService, executable, SquidFile } from '@squidcloud/backend';
958
-
959
- export class MyService extends SquidService {
960
- @executable()
961
- async greetUser(name: string): Promise<string> {
962
- return `Hello, ${name}`;
963
- }
964
-
965
- @executable()
966
- async uploadFile(file: SquidFile): Promise<Result> {
967
- console.log(file.originalName, file.size, file.data);
968
- return { success: true };
969
- }
970
- }
971
- ```
972
-
973
- **Client - Call the function:**
974
- ```typescript
975
- // Execute @executable() decorated function
976
- const result = await squid.executeFunction('greetUser', 'John');
977
- const typedResult = await squid.executeFunction<string>('greetUser', 'John');
978
-
979
- // With headers
980
- const result = await squid.executeFunctionWithHeaders(
981
- 'processPayment',
982
- { 'X-Custom-Header': 'value' },
983
- paymentData
984
- );
985
- ```
986
-
987
- ### Webhooks (@webhook)
988
-
989
- Webhooks allow you to create publicly accessible HTTP endpoints that can receive data from external services.
990
-
991
- **Backend - Define the webhook:**
992
- ```typescript
993
- import { SquidService, webhook, WebhookRequest } from '@squidcloud/backend';
994
-
995
- export class MyService extends SquidService {
996
- @webhook('github-events')
997
- async handleGithub(request: WebhookRequest): Promise<any> {
998
- console.log(request.body);
999
- console.log(request.headers);
1000
- console.log(request.queryParams);
1001
- console.log(request.httpMethod); // 'post' | 'get' | 'put' | 'delete'
1002
- console.log(request.files);
1003
-
1004
- return this.createWebhookResponse({ received: true }, 200);
1005
- }
1006
- }
1007
- ```
1008
-
1009
- **Webhook URL:** `https://<appId>.<region>.squid.cloud/webhooks/<webhook-id>`
1010
-
1011
- **Client - Get webhook URL:**
1012
- ```typescript
1013
- // Get URL for a specific webhook
1014
- const webhookUrl = squid.getWebhookUrl('github-events');
1015
- // Returns: https://<appId>.<region>.squid.cloud/webhooks/github-events
1016
-
1017
- // Get base webhooks URL (no webhook ID)
1018
- const baseUrl = squid.getWebhookUrl();
1019
- // Returns: https://<appId>.<region>.squid.cloud/webhooks
1020
- ```
1021
-
1022
- **Client - Call webhook programmatically (optional):**
1023
- ```typescript
1024
- // Usually webhooks are called by external services, but you can also call them from client
1025
- const result = await squid.executeWebhook<Response>('github-events', {
1026
- headers: { 'X-GitHub-Event': 'push' },
1027
- queryParams: { ref: 'main' },
1028
- body: { commits: [...] },
1029
- files: [file1, file2]
1030
- });
1031
- ```
1032
-
1033
- ### AI - Supported Models
1034
-
1035
- Squid supports multiple AI providers and models. The available models are defined as TypeScript constants in the SDK and can be imported from `@squidcloud/client`:
1036
-
1037
- ```typescript
1038
- import {
1039
- // Chat model constants (by provider)
1040
- OPENAI_CHAT_MODEL_NAMES,
1041
- ANTHROPIC_CHAT_MODEL_NAMES,
1042
- GEMINI_CHAT_MODEL_NAMES,
1043
- GROK_CHAT_MODEL_NAMES,
1044
- VENDOR_AI_CHAT_MODEL_NAMES, // All chat models combined
1045
-
1046
- // Embedding model constants
1047
- AI_EMBEDDINGS_MODEL_NAMES,
1048
- OPENAI_EMBEDDINGS_MODEL_NAMES,
1049
- VOYAGE_EMBEDDING_MODEL_NAMES,
1050
-
1051
- // Image generation model constants
1052
- AI_IMAGE_MODEL_NAMES,
1053
- OPENAI_IMAGE_MODEL_NAMES,
1054
- STABLE_DIFFUSION_MODEL_NAMES,
1055
- FLUX_MODEL_NAMES,
1056
-
1057
- // Audio model constants
1058
- AI_AUDIO_TRANSCRIPTION_MODEL_NAMES,
1059
- OPENAI_AUDIO_CREATE_SPEECH_MODEL_NAMES,
1060
-
1061
- // Type definitions
1062
- AiChatModelName,
1063
- AiEmbeddingsModelName,
1064
- AiImageModelName,
1065
- AiAudioTranscriptionModelName,
1066
- AiAudioCreateSpeechModelName,
1067
- } from '@squidcloud/client';
1068
- ```
1069
-
1070
- These constants are the source of truth for available models. Check your installed SDK version for the current list of supported models.
1071
-
1072
- **Model Categories:**
1073
- - **Chat Models**: Used for AI Agents, AI Query, etc. (OpenAI, Anthropic, Gemini, Grok)
1074
- - **Embedding Models**: Used for Knowledge Bases (OpenAI, Voyage)
1075
- - **Image Generation Models**: Used for image creation (DALL-E, Stable Diffusion, Flux)
1076
- - **Audio Models**: Transcription (Whisper, GPT-4o) and Text-to-Speech (TTS-1, GPT-4o-mini-tts)
1077
-
1078
- ### AI - Agents
1079
-
1080
- AI Agents are powerful, configurable AI assistants that can interact with your data, call functions, connect to integrations, and collaborate with other agents.
1081
-
1082
- **An AI Agent can:**
1083
- - Maintain conversation history (**memory is enabled by default**)
1084
- - Call backend functions decorated with `@aiFunction`
1085
- - Query databases and APIs through connected integrations
1086
- - Search knowledge bases for relevant information
1087
- - Collaborate with other AI agents
1088
- - Process voice input and generate voice output
1089
- - Accept files as part of chat requests
1090
-
1091
- **Creating and Managing Agents:**
1092
-
1093
- ```typescript
1094
- const aiClient = squid.ai();
1095
-
1096
- // Get the built-in agent (no ID required)
1097
- const builtInAgent = aiClient.agent();
1098
-
1099
- // Get a custom agent by ID
1100
- const myAgent = aiClient.agent('my-agent-id');
1101
-
1102
- // Get an agent with an Agent API key (allows calling without App API key, bypasses @secureAiAgent methods)
1103
- const agentWithKey = aiClient.agent('my-agent-id', {
1104
- apiKey: 'your-agent-api-key'
1105
- });
1106
-
1107
- // Create or update an agent
1108
- await myAgent.upsert({
1109
- description: 'Customer support assistant',
1110
- isPublic: false, // Whether the agent is publicly accessible
1111
- auditLog: true, // Enable audit logging for compliance
1112
- options: {
1113
- model: 'gpt-4o', // or 'claude-sonnet-4-5-20250929', 'gemini-3-flash'
1114
- instructions: 'You are a helpful customer support assistant. Be concise and professional.',
1115
- temperature: 0.7
1116
- }
1117
- });
1118
-
1119
- // Get agent details
1120
- const agentInfo = await myAgent.get();
1121
- console.log(agentInfo.id, agentInfo.description, agentInfo.options.model);
1122
-
1123
- // Update specific properties
1124
- await myAgent.updateInstructions('You are a technical support specialist.');
1125
- await myAgent.updateModel('claude-sonnet-4-5-20250929');
1126
- await myAgent.updateGuardrails(['no-harmful-content']);
1127
-
1128
- // Delete an agent
1129
- await myAgent.delete();
1130
-
1131
- // List all agents
1132
- const agents = await squid.ai().listAgents();
1133
- agents.forEach(agent => console.log(`${agent.id}: ${agent.description}`));
1134
-
1135
- // Manage agent API keys
1136
- const apiKey = await myAgent.regenerateApiKey();
1137
- console.log('New API key:', apiKey);
1138
-
1139
- const existingKey = await myAgent.getApiKey();
1140
- console.log('Current API key:', existingKey);
1141
-
1142
- // Update agent options at specific path
1143
- await myAgent.setAgentOptionInPath('temperature', 0.9);
1144
- await myAgent.setAgentOptionInPath('options.maxTokens', 2000);
1145
-
1146
- // Update connected agents
1147
- await myAgent.updateConnectedAgents([
1148
- { agentId: 'helper-agent', description: 'Helps with tasks' }
1149
- ]);
1150
-
1151
- // Update or delete custom guardrails
1152
- await myAgent.updateCustomGuardrails('Never reveal sensitive information');
1153
- await myAgent.deleteCustomGuardrail();
1154
- ```
1155
-
1156
- **Agent Lifecycle:**
1157
- 1. **Creation** - Use `upsert()` to create a new agent with an ID
1158
- 2. **Active** - Agent can process requests
1159
- 3. **Update** - Use `upsert()` or specific update methods
1160
- 4. **Deletion** - Use `delete()` to permanently remove the agent
1161
-
1162
- **Important Notes:**
1163
- - Agent IDs are permanent - once created, you cannot change the ID
1164
- - Deleting an agent does not delete associated chat history
1165
- - The built-in agent (accessed via `agent()` with no ID) cannot be deleted
1166
- - **Agent API Keys**: You can pass an options object with an `apiKey` when calling `agent()`:
1167
- - Allows calling agent methods without requiring an App API key
1168
- - Bypasses any `@secureAiAgent` security methods when calling agent ask functions
1169
- - Useful for direct agent access without app-level authentication
1170
-
1171
- **Connecting Resources to Agents:**
1172
-
1173
- Agents become powerful when connected to resources. You can connect agents to:
1174
- - **Other AI agents** (`connectedAgents`) - Agent collaboration
1175
- - **Backend functions** (`functions`) - Custom logic via `@aiFunction` decorators
1176
- - **Integrations** (`connectedIntegrations`) - Database/API queries
1177
- - **Knowledge bases** (`connectedKnowledgeBases`) - RAG (Retrieval Augmented Generation)
1178
-
1179
- **1. Connected Agents (`connectedAgents`):**
1180
-
1181
- Allow one agent to call another agent as a specialized sub-task handler.
1182
-
1183
- ```typescript
1184
- // Define connected agents in upsert or per-request
1185
- const response = await agent.ask('Analyze our sales data and send report to team', {
1186
- connectedAgents: [
1187
- {
1188
- agentId: 'data-analyst-agent',
1189
- description: 'Analyzes sales data and generates reports'
1190
- },
1191
- {
1192
- agentId: 'email-sender-agent',
1193
- description: 'Sends emails to team members'
1194
- }
1195
- ]
1196
- });
1197
-
1198
- // The main agent can now call these sub-agents when needed
1199
- // Agent orchestration happens automatically based on the prompt
1200
- ```
1201
-
1202
- **2. Functions (`functions`):**
1203
-
1204
- Connect backend functions decorated with `@aiFunction` that the agent can call.
1205
-
1206
- ```typescript
1207
- // Backend function
1208
- @aiFunction('Gets current inventory for a product', [
1209
- { name: 'productId', type: 'string', required: true, description: 'Product ID' }
1210
- ])
1211
- async getInventory({ productId }: { productId: string }): Promise<number> {
1212
- return await db.inventory.get(productId);
1213
- }
11
+ - **Client SDK** (`@squidcloud/client`) - TypeScript/JavaScript SDK for frontend. See [client.md](reference/client.md)
12
+ - **Backend SDK** (`@squidcloud/backend`) - TypeScript SDK with decorators for backend. See [backend.md](reference/backend.md)
13
+ - **CLI** (`@squidcloud/cli`) - Local development and deployment tools. See [backend.md](reference/backend.md)
14
+ - **Built-in integrations** - Databases, queues, storage, AI, APIs. See [databases.md](reference/databases.md), [ai.md](reference/ai.md), and [connectors.md](reference/connectors.md).
1214
15
 
1215
- // Client - Agent uses the function
1216
- const response = await agent.ask('How many units of product-123 do we have?', {
1217
- functions: ['getInventory'] // Function name or ID
1218
- });
1219
- // Agent automatically calls getInventory() and includes result in response
16
+ ## Feature-Specific Guidance
1220
17
 
1221
- // With predefined parameters (hidden from AI)
1222
- const response = await agent.ask('Send notification', {
1223
- functions: [
1224
- {
1225
- functionId: 'sendEmail',
1226
- context: { apiKey: 'secret-key' } // Passed to function but hidden from AI
1227
- }
1228
- ]
1229
- });
1230
- ```
18
+ - **[client.md](reference/client.md)** client SDK, initialization, setup, auth, login, tokens, OAuth, appId, region, environmentId, apiKey, authProvider, getToken, setAuthProvider, Squid client, frontend, collection, executeFunction, executeFunctionWithHeaders, getWebhookUrl, externalAuth, saveAuthCode, getAccessToken, storage, uploadFile, downloadUrl, queues, produce, consume, distributed locks, acquireLock, withLock, web, aiSearch, getUrlContent, createShortUrl, jobs, getJob, awaitJob, observability, metrics, reportMetric, queryMetrics, notifications, publishNotification, observeNotifications
19
+ - **[console.md](reference/console.md)** Squid Console, web UI, organizations, applications, AI Studio, knowledge bases, integrations, monitoring, logs, API keys, secrets, testing, debugging, profile settings, management API keys
20
+ - **[ai.md](reference/ai.md)** → AI agents, chat, ask, askWithAnnotations, askAsync, askWithVoiceResponse, transcribeAndChat, transcribeAndAsk, knowledge bases, RAG, embeddings, image generation, audio, transcription, text-to-speech, TTS, connectedAgents, connectedIntegrations, connectedKnowledgeBases, @aiFunction, @secureAiAgent, @secureAiQuery, memory, memoryOptions, voiceOptions, OpenAI, Anthropic, Gemini, Grok, DALL-E, Whisper, MCP, @mcpServer, @mcpTool, executeAiQuery, executeAiApiCall, extraction, createPdf, upsert agent, listAgents
21
+ - **[databases.md](reference/databases.md)** → collections, documents, queries, subscriptions, snapshots, insert, update, delete, CRUD, real-time, dereference, pagination, transactions, query operators, eq, neq, gt, gte, lt, lte, like, in, nin, arrayIncludesSome, arrayIncludesAll, sortBy, limit, join queries, OR queries, @trigger, native queries, SQL, MongoDB, Elasticsearch, incrementInPath, decrementInPath, watch changes, doc()
22
+ - **[backend.md](reference/backend.md)** → SquidService, @executable, @webhook, @trigger, TriggerRequest, @scheduler, @limits, rate limiting, quotas, decorators, backend functions, WebhookRequest, CronExpression, cron, file handling, SquidFile, getUserAuth, isAuthenticated, assertIsAuthenticated, createWebhookResponse, this.squid, this.secrets, @clientConnectionStateHandler, CLI, squid init, squid start, squid deploy, squid build, project structure
23
+ - **[security.md](reference/security.md)** security rules, @secureDatabase, @secureCollection, @secureTopic, @secureStorage, @secureApi, @secureNativeQuery, @secureAiQuery, @secureAiAgent, @secureDistributedLock, @secureGraphQL, QueryContext, MutationContext, isSubqueryOf, affectsPath, permissions, authorization, row-level security, role-based access
24
+ - **[admin.md](reference/admin.md)** → ManagementClient, management API keys, organizations, applications, programmatic management, CI/CD, automation, integrations admin, secrets admin, upsertIntegration, discoverDataConnectionSchema, testDataConnection, createOrganization, createApplication
25
+ - **[api.md](reference/api.md)** → API, REST API, HTTP endpoints, API reference, Agent API, AI Audio API, AI Image API, KnowledgeBase API, Matchmaking API, Web Utilities API, Database API, Extraction API
26
+ - **[openai.md](reference/openai.md)** → OpenAI, code interpreter, verbosity, reasoning models, o1, o3, gpt-5, DALL-E, Whisper, TTS, voice options, structured output, file upload
27
+ - **[connectors.md](reference/connectors.md)** → connectors, integrations, IntegrationType, postgres, mongo, auth0, s3, kafka, salesforce, essentials connector, built-in connector
28
+ - **React Hooks** → See `squid-react-development` skill
1231
29
 
1232
- **3. Connected Integrations (`connectedIntegrations`):**
30
+ ## Architecture
1233
31
 
1234
- Connect database or API integrations so the agent can query them directly.
32
+ ### Components
1235
33
 
1236
- ```typescript
1237
- const response = await agent.ask('Show me active users from last week', {
1238
- connectedIntegrations: [
1239
- {
1240
- integrationId: 'postgres-db',
1241
- integrationType: 'database',
1242
- description: 'Main application database with users, orders, and products',
1243
- instructions: 'Only query the users table unless explicitly asked otherwise',
1244
- options: {
1245
- // Integration-specific options (varies by integration type)
1246
- }
1247
- },
1248
- {
1249
- integrationId: 'stripe-api',
1250
- integrationType: 'api',
1251
- description: 'Stripe payment API for retrieving payment information'
1252
- }
1253
- ]
1254
- });
34
+ - **Client SDK** (`@squidcloud/client`) - Runs in browser or Node.js. Queries, subscriptions, AI, storage.
35
+ - **Squid Cloud** - Managed infrastructure that handles auth, routing, WebSockets, and deployment.
36
+ - **Your Backend** (`@squidcloud/backend`) - Optional custom code. SquidService classes with decorators.
37
+ - **Integrations** - External services (databases, AI providers, storage). Configured in Squid Console.
1255
38
 
1256
- // Agent can now query the database and API to answer questions
1257
- ```
39
+ **Flow:** Client SDK Squid Cloud [Your Backend] Integrations
1258
40
 
1259
- **4. Connected Knowledge Bases (`connectedKnowledgeBases`):**
41
+ Squid Cloud is always in the middle. Your Backend is optional - only needed for custom logic, security rules, triggers, etc.
1260
42
 
1261
- Connect knowledge bases for RAG (Retrieval Augmented Generation) - agent retrieves relevant context before answering.
43
+ ### Data Flow
1262
44
 
1263
- ```typescript
1264
- const response = await agent.ask('What is our return policy?', {
1265
- connectedKnowledgeBases: [
1266
- {
1267
- knowledgeBaseId: 'company-policies-kb',
1268
- description: 'Use this when asked about company policies, procedures, or guidelines'
1269
- },
1270
- {
1271
- knowledgeBaseId: 'product-docs-kb',
1272
- description: 'Use this when asked about product features, specifications, or usage'
1273
- }
1274
- ]
1275
- });
45
+ **Direct operations (without backend):**
46
+ - Collection queries
47
+ - AI agent chat
48
+ - Storage uploads
49
+ - Real-time subscriptions
1276
50
 
1277
- // Agent searches knowledge bases for relevant information before answering
1278
- ```
51
+ **Backend operations (requires backend code):**
1279
52
 
1280
- **Complete Agent Chat Options:**
53
+ Client Squid Cloud → Your Backend → Integration → Response
1281
54
 
1282
- ```typescript
1283
- const agent = squid.ai().agent('my-agent');
55
+ - Custom functions (`@executable`)
56
+ - Webhooks (`@webhook`)
57
+ - Triggers (`@trigger`)
58
+ - Scheduled jobs (`@scheduler`)
1284
59
 
1285
- // Chat (streaming) - Returns RxJS Observable
1286
- // IMPORTANT: Streaming behavior:
1287
- // - NO connected resources: streams token-by-token
1288
- // - HAS connected resources: emits ONCE with complete response
1289
- const chatObs = agent.chat('What is your return policy?', {
1290
- // Memory management
1291
- memoryOptions: {
1292
- memoryMode: 'read-write', // 'none' | 'read-only' | 'read-write'
1293
- memoryId: 'user-123', // Unique per user/session
1294
- expirationMinutes: 1440 // 24 hours
1295
- },
60
+ ### When Backend Code is Needed
1296
61
 
1297
- // Connected resources (can also be set on agent.upsert)
1298
- connectedAgents: [{ agentId: 'specialist-agent', description: 'Handles X' }],
1299
- functions: ['function1', 'function2'], // or with context: [{ functionId: 'fn', context: {...} }]
1300
- connectedIntegrations: [{ integrationId: 'db', integrationType: 'database', description: '...' }],
1301
- connectedKnowledgeBases: [{ knowledgeBaseId: 'kb1', description: 'When to use this KB' }],
62
+ | Use Case | Backend Required? | Why |
63
+ |----------|-------------------|-----|
64
+ | Query a database | No | Client SDK queries directly via Squid Cloud |
65
+ | Chat with AI agent | No | Client SDK calls AI directly |
66
+ | Upload files | No | Client SDK uploads directly |
67
+ | Real-time subscriptions | No | Client SDK uses WebSocket via Squid Cloud |
68
+ | Custom business logic | **Yes** | Use `@executable` functions |
69
+ | Security rules | **Yes** | Use `@secure*` decorators |
70
+ | Database triggers | **Yes** | Use `@trigger` decorator |
71
+ | Webhooks from external services | **Yes** | Use `@webhook` decorator |
72
+ | Scheduled jobs | **Yes** | Use `@scheduler` decorator |
73
+ | Server-side secrets | **Yes** | Access via `this.secrets` |
1302
74
 
1303
- // Model & generation
1304
- model: 'gpt-4o', // Override agent's default model
1305
- temperature: 0.7,
1306
- maxTokens: 4000,
1307
- maxOutputTokens: 2000,
1308
- instructions: 'Additional instructions for this request only',
1309
- responseFormat: 'json_object', // or 'text'
1310
- verbosity: 'medium', // 'low' | 'medium' | 'high' (OpenAI only)
1311
- reasoningEffort: 'high', // 'minimal' | 'low' | 'medium' | 'high' (for reasoning models)
75
+ ### What Squid Cloud Handles
1312
76
 
1313
- // Context & RAG
1314
- disableContext: false, // Disable all context
1315
- enablePromptRewriteForRag: false, // Rewrite prompt for better RAG results
1316
- includeReference: false, // Include source references in response
1317
- contextMetadataFilterForKnowledgeBase: {
1318
- 'kb-id': { category: 'documentation' } // Filter KB contexts by metadata
1319
- },
77
+ - **Authentication** - Validates tokens, manages sessions
78
+ - **Request routing** - Routes client requests to backend or integrations
79
+ - **WebSocket management** - Maintains connections for real-time subscriptions
80
+ - **Security enforcement** - Runs `@secure*` decorators before allowing operations
81
+ - **Deployment** - Runs your backend code in managed environment
82
+ - **Monitoring** - Logs, metrics, observability
1320
83
 
1321
- // Guardrails & quotas
1322
- guardrails: ['no-harmful-content', 'no-pii'], // Preset safety rules
1323
- quotas: {
1324
- maxAiCallStackSize: 5 // Max depth for nested agent calls
1325
- },
84
+ ### Development Workflow
1326
85
 
1327
- // Files & voice
1328
- fileUrls: [
1329
- { id: 'file1', type: 'image', purpose: 'context', url: 'https://...', description: 'Product image' }
1330
- ],
1331
- voiceOptions: {
1332
- modelName: 'tts-1',
1333
- voice: 'alloy', // alloy, ash, ballad, coral, echo, fable, onyx, nova, sage, shimmer, verse
1334
- speed: 1.0
1335
- },
86
+ 1. Create app in **Squid Console** → get `appId` and `apiKey`
87
+ 2. `squid init` → creates backend project
88
+ 3. `squid start` develop locally with hot-reload, reverse proxy to Squid Cloud
89
+ 4. `squid deploy` → deploy to Squid Cloud
90
+ 5. Build frontend with **Client SDK** → connects to your backend
1336
91
 
1337
- // Advanced
1338
- smoothTyping: true, // Smooth typing effect (default: true)
1339
- useCodeInterpreter: 'llm', // 'none' | 'llm' (OpenAI/Gemini only)
1340
- executionPlanOptions: {
1341
- enabled: true, // Agent plans which resources to use
1342
- model: 'gpt-4o',
1343
- reasoningEffort: 'high',
1344
- allowClarificationQuestions: false // Allow agent to ask follow-up questions (default: false)
1345
- },
1346
- agentContext: { userId: '123', role: 'admin' }, // Global context passed to all functions
1347
- includeMetadata: false // Include context metadata in response
1348
- });
1349
-
1350
- chatObs.subscribe(text => {
1351
- console.log(text);
1352
- });
1353
-
1354
- // Ask (complete response - same options as chat)
1355
- const response = await agent.ask('Question?', { /* same options */ });
1356
-
1357
- // Ask with annotations (includes file references, citations)
1358
- const result = await agent.askWithAnnotations('Question?', { /* same options as ask */ });
1359
- console.log(result.responseString);
1360
- console.log(result.annotations); // File references, sources, etc.
1361
-
1362
- // Ask asynchronously (doesn't wait for response, uses job system)
1363
- await agent.askAsync('Long running task', 'job-id-123', { /* same options */ });
1364
- // Check job status later with squid.job().getJob('job-id-123')
1365
-
1366
- // Voice responses
1367
- const voiceResult = await agent.askWithVoiceResponse('Hello', {
1368
- voiceOptions: { modelName: 'tts-1', voice: 'alloy', speed: 1.0 }
1369
- });
1370
- console.log(voiceResult.responseString);
1371
- console.log(voiceResult.voiceResponseFile);
1372
-
1373
- // Transcribe audio and chat
1374
- const transcribeResult = await agent.transcribeAndChat(audioFile);
1375
- console.log(transcribeResult.transcribedPrompt);
1376
- transcribeResult.responseStream.subscribe(text => console.log(text));
1377
-
1378
- // Transcribe audio and ask (non-streaming)
1379
- const askResult = await agent.transcribeAndAsk(audioFile, { /* ask options */ });
1380
- console.log(askResult.transcribedPrompt);
1381
- console.log(askResult.response);
1382
-
1383
- // Transcribe audio and get voice response
1384
- const voiceResponse = await agent.transcribeAndAskWithVoiceResponse(audioFile, {
1385
- voiceOptions: { modelName: 'tts-1', voice: 'alloy' }
1386
- });
1387
- console.log(voiceResponse.transcribedPrompt);
1388
- console.log(voiceResponse.responseString);
1389
- console.log(voiceResponse.voiceResponseFile);
1390
-
1391
- // Get chat history
1392
- const history = await agent.getChatHistory('memory-id-123');
1393
-
1394
- // Semantic search across agent's knowledge
1395
- const results = await agent.search({ prompt: 'product docs', limit: 10 });
1396
-
1397
- // Provide feedback on responses
1398
- await agent.provideFeedback('thumbs_up'); // or 'thumbs_down'
1399
-
1400
- // Observe status updates (for long-running operations)
1401
- agent.observeStatusUpdates().subscribe(status => {
1402
- console.log(status.title, status.tags);
1403
- });
1404
- ```
1405
-
1406
- **Agent Security (@secureAiAgent):**
1407
-
1408
- Secure agent access on the backend:
1409
-
1410
- **Backend:**
1411
- ```typescript
1412
- import { SquidService, secureAiAgent } from '@squidcloud/backend';
1413
-
1414
- export class MyService extends SquidService {
1415
- // Secure specific agent
1416
- @secureAiAgent('customer-support-agent')
1417
- allowCustomerAgent(): boolean {
1418
- return this.isAuthenticated();
1419
- }
1420
-
1421
- // Secure all agents
1422
- @secureAiAgent()
1423
- allowAllAgents(): boolean {
1424
- return this.isAuthenticated();
1425
- }
1426
- }
1427
- ```
1428
-
1429
- **Important Notes:**
1430
- - Connected resources can be set on agent creation/update OR per-request
1431
- - Per-request settings override agent-level settings
1432
- - Agent automatically decides when to use connected resources based on the prompt
1433
- - `executionPlanOptions` enables the agent to create a plan before using resources (improves accuracy)
1434
- - `chatId` and `profileId` are deprecated - use `memoryOptions` instead
1435
-
1436
- ### AI - Knowledge Bases
1437
-
1438
- ```typescript
1439
- const kb = squid.ai().knowledgeBase('product-docs');
1440
-
1441
- // Get knowledge base details
1442
- const kbDetails = await kb.getKnowledgeBase();
1443
- console.log(kbDetails);
1444
-
1445
- // Create or update knowledge base
1446
- await kb.upsertKnowledgeBase({
1447
- description: 'Product documentation knowledge base',
1448
- // ...other options
1449
- });
1450
-
1451
- // Delete knowledge base
1452
- await kb.delete();
1453
-
1454
- // List all knowledge bases
1455
- const allKBs = await squid.ai().listKnowledgeBases();
1456
-
1457
- // Upsert context (with or without file)
1458
- await kb.upsertContext({
1459
- contextId: 'doc-123',
1460
- text: 'Product documentation...',
1461
- metadata: { category: 'user-guide', version: '2.0' }
1462
- }, optionalFile);
1463
-
1464
- // Batch upsert
1465
- await kb.upsertContexts([
1466
- { contextId: 'doc-1', text: '...' },
1467
- { contextId: 'doc-2', text: '...' }
1468
- ], optionalFiles);
1469
-
1470
- // Search with prompt
1471
- const results = await kb.searchContextsWithPrompt({
1472
- prompt: 'How do I reset password?',
1473
- limit: 5,
1474
- metadata: { category: 'user-guide' }
1475
- });
1476
-
1477
- // Search by context ID
1478
- const related = await kb.searchContextsWithContextId({
1479
- contextId: 'doc-123',
1480
- limit: 10
1481
- });
1482
-
1483
- // Semantic search (returns chunks)
1484
- const searchResults = await kb.search({
1485
- prompt: 'authentication',
1486
- limit: 10
1487
- });
1488
-
1489
- // Get context
1490
- const context = await kb.getContext('doc-123');
1491
-
1492
- // List contexts
1493
- const allContexts = await kb.listContexts(1000); // truncateTextAfter
1494
- const contextIds = await kb.listContextIds();
1495
-
1496
- // Download context
1497
- const download = await kb.downloadContext('doc-123');
1498
-
1499
- // Delete contexts
1500
- await kb.deleteContext('doc-123');
1501
- await kb.deleteContexts(['doc-1', 'doc-2']);
1502
-
1503
- // List all knowledge bases
1504
- const allKBs = await squid.ai().listKnowledgeBases();
1505
- ```
1506
-
1507
- ### AI - Files
1508
-
1509
- Manage files stored with AI providers (OpenAI, etc.) for use with agents.
1510
-
1511
- ```typescript
1512
- const aiFiles = squid.ai().files('openai'); // provider: 'openai' | 'anthropic' | etc.
1513
-
1514
- // Upload file to AI provider
1515
- const fileId = await aiFiles.uploadFile({
1516
- file: myFile, // Browser File object
1517
- purpose: 'assistants' // Purpose for the file
1518
- });
1519
- console.log('Uploaded file ID:', fileId);
1520
-
1521
- // Delete file from AI provider
1522
- const deleted = await aiFiles.deleteFile(fileId);
1523
- console.log('File deleted:', deleted);
1524
- ```
1525
-
1526
- ### AI - Image & Audio
1527
-
1528
- ```typescript
1529
- // Image generation
1530
- const imageUrl = await squid.ai().image().generate(
1531
- 'A futuristic city',
1532
- { size: '1024x1024', quality: 'hd' }
1533
- );
1534
-
1535
- // Remove background
1536
- const noBgBase64 = await squid.ai().image().removeBackground(imageFile);
1537
-
1538
- // Transcribe audio
1539
- const text = await squid.ai().audio().transcribe(audioFile, {
1540
- language: 'en'
1541
- });
1542
-
1543
- // Text-to-speech
1544
- const audioFile = await squid.ai().audio().createSpeech(
1545
- 'Hello world',
1546
- { modelName: 'tts-1', voice: 'alloy', speed: 1.0 }
1547
- );
1548
- ```
1549
-
1550
- ### AI - Query & API Call
1551
-
1552
- AI Query allows you to query your database using natural language prompts. Squid's AI converts your prompt into database queries and returns the results.
1553
-
1554
- ```typescript
1555
- // Natural language database query (basic)
1556
- const result = await squid.ai().executeAiQuery(
1557
- 'built_in_db',
1558
- 'Show me all active users who registered last month'
1559
- );
1560
-
1561
- // With options
1562
- const result = await squid.ai().executeAiQuery(
1563
- 'built_in_db',
1564
- 'Show me all active users who registered last month',
1565
- {
1566
- instructions: 'Only query the users collection',
1567
- enableRawResults: true, // Include raw query results
1568
- selectCollectionsOptions: {
1569
- collectionsToUse: ['users'] // Limit to specific collections
1570
- },
1571
- generateQueryOptions: {
1572
- maxErrorCorrections: 3 // Number of retry attempts
1573
- },
1574
- analyzeResultsOptions: {
1575
- enableCodeInterpreter: true // Enable code execution for result analysis
1576
- }
1577
- }
1578
- );
1579
-
1580
- // AI-powered API call
1581
- const apiResult = await squid.ai().executeAiApiCall(
1582
- 'stripe-api',
1583
- 'Create a $50 charge for customer cus_123',
1584
- ['create-charge'], // allowed endpoints
1585
- true // provide explanation
1586
- );
1587
- ```
1588
-
1589
- ### AI - Document Extraction & PDF Creation
1590
-
1591
- Extract structured data from documents and create PDFs programmatically.
1592
-
1593
- ```typescript
1594
- const extraction = squid.extraction();
1595
-
1596
- // Extract data from document file
1597
- const extractedData = await extraction.extractDataFromDocumentFile(
1598
- myPdfFile, // File object
1599
- {
1600
- schema: {
1601
- fields: [
1602
- { name: 'invoiceNumber', type: 'string', description: 'Invoice number' },
1603
- { name: 'total', type: 'number', description: 'Total amount' },
1604
- { name: 'date', type: 'date', description: 'Invoice date' }
1605
- ]
1606
- }
1607
- }
1608
- );
1609
- console.log(extractedData);
1610
-
1611
- // Extract data from document URL
1612
- const urlData = await extraction.extractDataFromDocumentUrl(
1613
- 'https://example.com/document.pdf',
1614
- {
1615
- schema: {
1616
- fields: [
1617
- { name: 'title', type: 'string' },
1618
- { name: 'author', type: 'string' }
1619
- ]
1620
- }
1621
- }
1622
- );
1623
-
1624
- // Create PDF from HTML or markdown
1625
- const pdfResponse = await extraction.createPdf({
1626
- content: '<h1>Hello</h1><p>This is a PDF</p>',
1627
- contentType: 'html', // or 'markdown'
1628
- options: {
1629
- pageSize: 'A4',
1630
- margin: { top: '1cm', bottom: '1cm', left: '1cm', right: '1cm' }
1631
- }
1632
- });
1633
- console.log('PDF created:', pdfResponse.url);
1634
- ```
1635
-
1636
- **Use cases:**
1637
- - Invoice/receipt processing
1638
- - Form data extraction
1639
- - PDF report generation
1640
- - Document digitization
1641
- - Automated data entry
1642
-
1643
- ### AI - Application Settings
1644
-
1645
- Manage AI provider settings and API keys for your application.
1646
-
1647
- ```typescript
1648
- const aiClient = squid.ai();
1649
-
1650
- // Get application AI settings
1651
- const settings = await aiClient.getApplicationAiSettings();
1652
- console.log(settings);
1653
-
1654
- // Set application AI settings
1655
- await aiClient.setApplicationAiSettings({
1656
- defaultModel: 'gpt-4o',
1657
- // ... other settings
1658
- });
1659
-
1660
- // Set AI provider API key secret
1661
- await aiClient.setAiProviderApiKeySecret(
1662
- 'openai', // providerType: 'openai' | 'anthropic' | 'google' | etc.
1663
- 'OPENAI_API_KEY' // secret key name from secrets manager
1664
- );
1665
- ```
1666
-
1667
- ### Storage
1668
-
1669
- ```typescript
1670
- const storage = squid.storage(); // built_in_storage
1671
- const s3 = squid.storage('aws-s3-integration');
1672
-
1673
- // Upload file
1674
- await storage.uploadFile('/documents', file, 3600); // 1 hour expiration
1675
-
1676
- // Get download URL
1677
- const url = await storage.getDownloadUrl('/documents/report.pdf', 3600);
1678
- console.log(url.url);
1679
-
1680
- // Get file metadata
1681
- const metadata = await storage.getFileMetadata('/documents/report.pdf');
1682
- console.log(metadata.filename, metadata.size, metadata.lastModified);
1683
-
1684
- // List directory
1685
- const contents = await storage.listDirectoryContents('/documents');
1686
- console.log(contents.files, contents.directories);
1687
-
1688
- // Delete files
1689
- await storage.deleteFile('/documents/report.pdf');
1690
- await storage.deleteFiles(['/docs/file1.pdf', '/docs/file2.pdf']);
1691
- ```
1692
-
1693
- ### External OAuth Integration
1694
-
1695
- Manage OAuth tokens for external integrations. Squid automatically handles token refresh when tokens expire.
1696
-
1697
- ```typescript
1698
- const externalAuth = squid.externalAuth('google-oauth-integration');
1699
-
1700
- // Save authorization code (exchange for access token)
1701
- const tokenResponse = await externalAuth.saveAuthCode(
1702
- 'authorization-code-from-oauth-flow',
1703
- 'user-identifier' // Unique identifier for this user
1704
- );
1705
- console.log('Access token:', tokenResponse.accessToken);
1706
- console.log('Refresh token:', tokenResponse.refreshToken);
1707
- console.log('Expires at:', tokenResponse.expiresAt);
1708
-
1709
- // Get access token (auto-refreshes if expired)
1710
- const token = await externalAuth.getAccessToken('user-identifier');
1711
- console.log('Current access token:', token.accessToken);
1712
- ```
1713
-
1714
- **Use cases:**
1715
- - Google Drive/Calendar integration
1716
- - GitHub OAuth
1717
- - Slack OAuth
1718
- - Any external service requiring OAuth 2.0
1719
- - Squid handles token refresh automatically
1720
-
1721
- ### Queues
1722
-
1723
- ```typescript
1724
- const queue = squid.queue<Message>('notifications');
1725
- const kafkaQueue = squid.queue<Event>('events', 'kafka-integration');
1726
-
1727
- // Produce messages
1728
- await queue.produce([
1729
- { type: 'email', to: 'user@example.com' },
1730
- { type: 'sms', to: '+1234567890' }
1731
- ]);
1732
-
1733
- // Consume messages (observable)
1734
- const subscription = queue.consume<Message>().subscribe(message => {
1735
- console.log('Received:', message);
1736
- });
1737
-
1738
- // Unsubscribe
1739
- subscription.unsubscribe();
1740
- ```
1741
-
1742
- ### Distributed Locks
1743
-
1744
- ```typescript
1745
- // Acquire and release manually
1746
- const lock = await squid.acquireLock('payment-processing');
1747
- try {
1748
- await processPayment();
1749
- } finally {
1750
- lock.release(); // Note: release() is synchronous
1751
- }
1752
-
1753
- // Acquire with auto-release timeout (maxHoldTimeMillis)
1754
- const lockWithTimeout = await squid.acquireLock('my-mutex', {
1755
- maxHoldTimeMillis: 30000 // Auto-release after 30 seconds
1756
- });
1757
-
1758
- // Access lock properties
1759
- console.log(lock.resourceId); // The mutex name: 'payment-processing'
1760
- console.log(lock.lockId); // Unique ID for this lock instance
1761
- console.log(lock.isReleased()); // Check if released: true/false
1762
-
1763
- // Observe release (RxJS Observable)
1764
- const sub = lock.observeRelease().subscribe(() => {
1765
- console.log('Lock was released');
1766
- });
1767
- // Don't forget to unsubscribe when done
1768
- sub.unsubscribe();
1769
-
1770
- // Automatic release with withLock (recommended)
1771
- const result = await squid.withLock('payment-processing', async (lock) => {
1772
- // Critical section - only one client can execute at a time
1773
- await processPayment();
1774
- return 'success';
1775
- });
1776
-
1777
- // Lock is automatically released even if callback throws
1778
- ```
1779
-
1780
- **Important notes:**
1781
- - Lock is automatically released if WebSocket connection is lost
1782
- - If lock is already held by another client, `acquireLock()` will reject
1783
- - `maxHoldTimeMillis` option automatically releases lock after specified duration
1784
- - Use `@secureDistributedLock(mutexName?)` decorator to secure lock access
1785
- - Without API key, you must define security rules for locks
1786
-
1787
- ### API Integration
1788
-
1789
- ```typescript
1790
- const api = squid.api();
1791
-
1792
- // HTTP methods - All return HttpResponse<T> with status, headers, and body
1793
- const customerResponse = await api.get<Customer>('stripe-api', 'get-customer', {
1794
- pathParams: { customerId: 'cus_123' },
1795
- queryParams: { expand: 'subscriptions' }
1796
- });
1797
- // Access response details
1798
- console.log(customerResponse.status); // HTTP status code
1799
- console.log(customerResponse.headers); // Response headers
1800
- console.log(customerResponse.body); // Parsed response body
1801
-
1802
- const chargeResponse = await api.post<Charge, ChargeRequest>(
1803
- 'stripe-api',
1804
- 'create-charge',
1805
- { amount: 1000, currency: 'usd' },
1806
- { headers: { 'Idempotency-Key': 'unique' } }
1807
- );
1808
-
1809
- // Generic request method (for custom HTTP methods or complex requests)
1810
- const response = await api.request<ResponseType, RequestType>(
1811
- 'integration-id',
1812
- 'endpoint-id',
1813
- requestBody,
1814
- { headers: {}, queryParams: {} },
1815
- 'POST' // or 'GET', 'PUT', 'PATCH', 'DELETE'
1816
- );
1817
-
1818
- // Other methods: put, patch, delete
1819
- await api.put(integrationId, endpointId, body, options);
1820
- await api.patch(integrationId, endpointId, body, options);
1821
- await api.delete(integrationId, endpointId, body, options);
1822
- ```
1823
-
1824
- ### Web
1825
-
1826
- ```typescript
1827
- const web = squid.web();
1828
-
1829
- // AI-powered web search
1830
- const results = await web.aiSearch('latest AI developments');
1831
- console.log(results.markdownContent);
1832
- console.log(results.citations);
1833
-
1834
- // Get URL content (as markdown)
1835
- const content = await web.getUrlContent('https://example.com/article');
1836
-
1837
- // URL shortening
1838
- const shortUrl = await web.createShortUrl('https://very-long-url.com', 86400);
1839
- console.log(shortUrl.url, shortUrl.id);
1840
-
1841
- const shortUrls = await web.createShortUrls(['url1', 'url2'], 86400);
1842
-
1843
- await web.deleteShortUrl(shortUrlId);
1844
- await web.deleteShortUrls([id1, id2]);
1845
- ```
1846
-
1847
- ### Schedulers
1848
-
1849
- ```typescript
1850
- const schedulers = squid.schedulers;
1851
-
1852
- // List all
1853
- const all = await schedulers.list();
1854
-
1855
- // Enable/disable
1856
- await schedulers.enable('daily-cleanup');
1857
- await schedulers.enable(['scheduler-1', 'scheduler-2']);
1858
-
1859
- await schedulers.disable('hourly-sync');
1860
- await schedulers.disable(['scheduler-1', 'scheduler-2']);
1861
- ```
1862
-
1863
- ### Observability & Metrics
1864
-
1865
- ```typescript
1866
- const obs = squid.observability;
1867
-
1868
- // Report metric
1869
- await obs.reportMetric({
1870
- name: 'api_request_count',
1871
- value: 1,
1872
- tags: { endpoint: '/users', method: 'GET' },
1873
- timestamp: Date.now()
1874
- });
1875
-
1876
- // Query metrics
1877
- const metrics = await obs.queryMetrics({
1878
- metricNames: ['api_request_count'],
1879
- startTime: startTimestamp,
1880
- endTime: endTimestamp,
1881
- groupBy: ['endpoint'],
1882
- filters: { method: 'GET' },
1883
- aggregation: 'sum'
1884
- });
1885
-
1886
- // Flush pending
1887
- await obs.flush();
1888
- ```
1889
-
1890
- ### Custom Notifications
1891
-
1892
- Send custom messages between clients for real-time coordination.
1893
-
1894
- ```typescript
1895
- const notifications = squid.getNotificationClient();
1896
-
1897
- // Observe incoming notifications
1898
- const subscription = notifications.observeNotifications().subscribe(payload => {
1899
- console.log('Received notification:', payload);
1900
- // payload can be any serializable data
1901
- });
1902
-
1903
- // Publish notification to specific clients - can be done only if Squid was initialized with API key.
1904
- await notifications.publishNotification(
1905
- { type: 'update', message: 'Data changed' }, // payload
1906
- ['client-id-1', 'client-id-2'] // target client IDs
1907
- );
1908
-
1909
- // Unsubscribe when done
1910
- subscription.unsubscribe();
1911
- ```
1912
-
1913
- **Use cases:**
1914
- - Real-time coordination between users
1915
- - Custom event notifications
1916
- - Client-to-client messaging
1917
- - Broadcast updates to specific users
1918
-
1919
- ### Jobs
1920
-
1921
- Jobs allow you to track the status of long-running asynchronous operations. Each job has a unique ID that can be used to query its status or wait for completion.
1922
-
1923
- **Important:** Job IDs must be globally unique and kept private. Anyone who knows a job ID can query its status or result.
1924
-
1925
- ```typescript
1926
- const jobClient = squid.job();
1927
-
1928
- // Get job status (returns AsyncJob with status: 'pending' | 'completed' | 'failed')
1929
- const job = await jobClient.getJob<Result>('job-123');
1930
- if (job) {
1931
- console.log('Status:', job.status);
1932
- if (job.status === 'completed') {
1933
- console.log('Result:', job.result);
1934
- } else if (job.status === 'failed') {
1935
- console.log('Error:', job.error);
1936
- }
1937
- }
1938
-
1939
- // Wait for completion (resolves when job finishes or throws if job fails)
1940
- const result = await jobClient.awaitJob<Result>('job-123');
1941
- console.log('Job completed with result:', result);
1942
- ```
1943
-
1944
- ### Admin - Integrations
1945
-
1946
- **Important:** Admin methods require initialization with an API key. They cannot be used with user authentication.
1947
-
1948
- ```typescript
1949
- // Squid must be initialized with apiKey
1950
- const squid = new Squid({ appId: 'app-id', region: 'us-east-1.aws', apiKey: 'your-api-key' });
1951
-
1952
- const integrations = squid.admin().integrations();
1953
-
1954
- // List all or by type
1955
- const all = await integrations.list();
1956
- const databases = await integrations.list('database');
1957
-
1958
- // Get one
1959
- const integration = await integrations.get('postgres-db');
1960
-
1961
- // Create/update
1962
- await integrations.upsertIntegration({
1963
- integrationId: 'my-postgres',
1964
- type: 'postgres',
1965
- connectionString: 'postgresql://...'
1966
- });
1967
-
1968
- // Delete
1969
- await integrations.delete('old-integration');
1970
- await integrations.deleteMany(['int-1', 'int-2']);
1971
-
1972
- // Get integration schema
1973
- const schema = await integrations.getIntegrationSchema<MySchemaType>('postgres-db');
1974
- console.log(schema);
1975
-
1976
- // Set integration schema
1977
- await integrations.setIntegrationSchema('postgres-db', {
1978
- collections: [
1979
- {
1980
- name: 'users',
1981
- fields: [
1982
- { name: 'id', type: 'string', primaryKey: true },
1983
- { name: 'email', type: 'string' },
1984
- { name: 'name', type: 'string' }
1985
- ]
1986
- }
1987
- ]
1988
- });
1989
-
1990
- // Generate AI descriptions for data schema (collections and fields)
1991
- const schemaResult = await integrations.generateAiDescriptionsForDataSchema('postgres-db', {
1992
- schema: currentSchema, // The current schema of the integration
1993
- collections: ['users', 'orders'], // Optional: specific collections to generate for
1994
- instructions: 'Use business terminology' // Optional: guide the AI
1995
- });
1996
- console.log(schemaResult.schema); // Schema with AI-generated descriptions
1997
-
1998
- // Generate AI descriptions for associations (relationships between entities)
1999
- const assocResult = await integrations.generateAiDescriptionsForAssociations('postgres-db', {
2000
- schema: currentSchema,
2001
- associations: ['User_Order'], // Optional: specific associations to generate for
2002
- instructions: 'Explain the business relationship'
2003
- });
2004
-
2005
- // Generate AI descriptions for stored procedures
2006
- const spResult = await integrations.generateAiDescriptionsForStoredProcedures('postgres-db', {
2007
- schema: currentSchema,
2008
- storedProcedures: ['calculate_totals'], // Optional: specific procedures to generate for
2009
- instructions: 'Describe what the procedure does'
2010
- });
2011
-
2012
- // Generate AI descriptions for API endpoints
2013
- const apiResult = await integrations.generateAiDescriptionsForEndpoints('my-api', {
2014
- schema: apiSchema,
2015
- endpoints: ['GET /users', 'POST /orders'], // Optional: specific endpoints to generate for
2016
- instructions: 'Use REST API terminology'
2017
- });
2018
-
2019
- // Discover schema from a database connection
2020
- const discovered = await integrations.discoverDataConnectionSchema('postgres-db');
2021
- console.log(discovered.schema); // Discovered tables, columns, relationships
2022
- console.log(discovered.collectionReadiness); // Permission and replication status
2023
-
2024
- // Test a database connection to verify it is working (before or after saving)
2025
- const testResult = await integrations.testDataConnection({
2026
- id: 'my-postgres',
2027
- type: 'postgres',
2028
- configuration: {
2029
- connectionOptions: {
2030
- host: 'localhost',
2031
- port: 5432,
2032
- database: 'mydb',
2033
- user: 'user',
2034
- secrets: { password: 'pass' }
2035
- }
2036
- }
2037
- });
2038
- if (testResult.success) {
2039
- console.log('Connection successful!');
2040
- } else {
2041
- console.log('Connection failed:', testResult.errorMessage);
2042
- }
2043
-
2044
- // Discover schema from a GraphQL endpoint
2045
- const graphqlSchema = await integrations.discoverGraphQLConnectionSchema('my-graphql', {
2046
- connectionOptions: {
2047
- url: 'https://api.example.com/graphql',
2048
- headers: { 'Authorization': 'Bearer token' }
2049
- }
2050
- });
2051
-
2052
- // Discover schema from an OpenAPI spec URL
2053
- const openApiSchema = await integrations.discoverOpenApiSchema('my-api', {
2054
- discoveryOptions: {
2055
- openApiSpecUrl: 'https://api.example.com/openapi.json',
2056
- headers: { 'Authorization': 'Bearer token' }
2057
- }
2058
- });
2059
-
2060
- // Discover schema from an uploaded OpenAPI spec file
2061
- const fileSchema = await integrations.discoverOpenApiSchemaFromFile('my-api');
2062
- ```
2063
-
2064
- ### Admin - Secrets
2065
-
2066
- ```typescript
2067
- const secrets = squid.admin().secrets();
2068
-
2069
- // Get secret
2070
- const value = await secrets.get('STRIPE_KEY');
2071
-
2072
- // Get all
2073
- const all = await secrets.getAll();
2074
-
2075
- // Create/update
2076
- await secrets.upsert('API_KEY', 'secret-value');
2077
- await secrets.upsertMany([
2078
- { key: 'KEY1', value: 'val1' },
2079
- { key: 'KEY2', value: 'val2' }
2080
- ]);
2081
-
2082
- // Delete
2083
- await secrets.delete('OLD_KEY');
2084
- await secrets.deleteMany(['KEY1', 'KEY2']);
2085
-
2086
- // API Keys
2087
- const apiKeys = secrets.apiKeys;
2088
- await apiKeys.upsert('my-key');
2089
- const key = await apiKeys.get('my-key');
2090
- const allApiKeys = await apiKeys.getAll(); // Get all API keys
2091
- await apiKeys.delete('my-key');
2092
- ```
2093
-
2094
- ## Backend SDK
2095
-
2096
- ### SquidService Base Class
2097
-
2098
- All backend services extend `SquidService`:
2099
-
2100
- ```typescript
2101
- import { SquidService } from '@squidcloud/backend';
2102
-
2103
- export class MyService extends SquidService {
2104
- // Decorated methods
2105
- }
2106
- ```
2107
-
2108
- **Important:** In backend code running on Squid's infrastructure, the Squid client is **already initialized and available** via `this.squid`. You don't need to manually create a new instance or provide configuration parameters - simply use the pre-configured client.
2109
-
2110
- **Available properties:**
2111
-
2112
- ```typescript
2113
- this.region // 'local' during dev, region in production
2114
- this.backendBaseUrl
2115
- this.secrets // From Squid Console
2116
- this.apiKeys
2117
- this.context // RunContext (appId, clientId, sourceIp, headers, openApiContext)
2118
- this.squid // Pre-initialized Squid client instance (ready to use)
2119
- this.assetsDirectory // Path to public/ folder
2120
- ```
2121
-
2122
- **Auth methods:**
2123
-
2124
- ```typescript
2125
- // Get user authentication (JWT token)
2126
- this.getUserAuth() // AuthWithBearer | undefined
2127
- // Returns: { type: 'Bearer', userId: string, expiration: number, attributes: Record<string, any>, jwt?: string }
2128
-
2129
- // Get API key authentication
2130
- this.getApiKeyAuth() // AuthWithApiKey | undefined
2131
- // Returns: { type: 'ApiKey', apiKey: string }
2132
-
2133
- this.isAuthenticated() // boolean - true if user token OR API key
2134
- this.assertIsAuthenticated() // throws if not authenticated
2135
- this.assertApiKeyCall() // throws if not API key auth
2136
- ```
2137
-
2138
- **Helper methods:**
2139
-
2140
- ```typescript
2141
- // Create webhook response (for @webhook decorated functions)
2142
- this.createWebhookResponse(body?, statusCode?, headers?)
2143
- // Throws webhook response immediately (interrupts execution)
2144
- this.throwWebhookResponse({ body?, statusCode?, headers? })
2145
-
2146
- // Create OpenAPI response (for OpenAPI/tsoa decorated functions)
2147
- this.createOpenApiResponse(body?, statusCode?, headers?)
2148
- // Throws OpenAPI response immediately (interrupts execution)
2149
- this.throwOpenApiResponse({ body?, statusCode?, headers? })
2150
-
2151
- // Convert browser File to SquidFile for OpenAPI file returns
2152
- await this.convertToSquidFile(file: File): Promise<SquidFile>
2153
-
2154
- // Publish AI status update to specific client (used in @aiFunction)
2155
- await this.publishAiStatusUpdate(update: AiStatusMessage, clientId: ClientId)
2156
- ```
2157
-
2158
- ### Backend Decorators
2159
-
2160
- This section contains backend decorators that don't have corresponding client methods (triggers, schedulers, security rules, etc.).
2161
-
2162
- #### @trigger(options)
2163
- Responds to database collection changes.
2164
-
2165
- ```typescript
2166
- @trigger({ id: 'user-created', collection: 'users', mutationTypes: ['insert'] })
2167
- async onUserCreated(request: TriggerRequest<User>): Promise<void> {
2168
- console.log(request.mutationType); // 'insert' | 'update' | 'delete'
2169
- console.log(request.docBefore);
2170
- console.log(request.docAfter);
2171
- }
2172
- ```
2173
-
2174
- #### @scheduler(options)
2175
- Schedules periodic execution using cron expressions.
2176
-
2177
- ```typescript
2178
- import { CronExpression } from '@squidcloud/backend';
2179
-
2180
- // Using cron string directly
2181
- @scheduler({ id: 'daily-cleanup', cron: '0 0 * * *' }) // Daily at midnight UTC
2182
- async cleanup(): Promise<void> {
2183
- console.log('Running cleanup');
2184
- }
2185
-
2186
- // Using predefined CronExpression enum
2187
- @scheduler({ id: 'hourly-sync', cron: CronExpression.EVERY_HOUR })
2188
- async hourlySync(): Promise<void> {
2189
- console.log('Running hourly sync');
2190
- }
2191
-
2192
- @scheduler({ id: 'weekday-morning', cron: CronExpression.MONDAY_TO_FRIDAY_AT_9AM })
2193
- async weekdayTask(): Promise<void> {
2194
- console.log('Running weekday morning task');
2195
- }
2196
-
2197
- @scheduler({ id: 'frequent', cron: CronExpression.EVERY_5_MINUTES, exclusive: false })
2198
- async frequentTask(): Promise<void> {
2199
- // exclusive: false allows concurrent runs
2200
- }
2201
- ```
2202
-
2203
- **Common CronExpression values:**
2204
- - `EVERY_SECOND`, `EVERY_5_SECONDS`, `EVERY_10_SECONDS`, `EVERY_30_SECONDS`
2205
- - `EVERY_MINUTE`, `EVERY_5_MINUTES`, `EVERY_10_MINUTES`, `EVERY_30_MINUTES`
2206
- - `EVERY_HOUR`, `EVERY_2_HOURS`, `EVERY_3_HOURS`, etc.
2207
- - `EVERY_DAY_AT_MIDNIGHT`, `EVERY_DAY_AT_NOON`, `EVERY_DAY_AT_1AM`, etc.
2208
- - `EVERY_WEEKDAY`, `EVERY_WEEKEND`, `EVERY_WEEK`
2209
- - `MONDAY_TO_FRIDAY_AT_9AM`, `MONDAY_TO_FRIDAY_AT_5PM`, etc.
2210
- - `EVERY_1ST_DAY_OF_MONTH_AT_MIDNIGHT`, `EVERY_QUARTER`, `EVERY_YEAR`
2211
-
2212
- #### @secureTopic(topicName, type, integrationId?)
2213
- Secures queue topics.
2214
-
2215
- ```typescript
2216
- @secureTopic('notifications', 'read')
2217
- allowRead(): boolean {
2218
- return this.isAuthenticated();
2219
- }
2220
-
2221
- @secureTopic('notifications', 'write')
2222
- allowWrite(context: TopicWriteContext<any>): boolean {
2223
- return context.messages.length <= 100;
2224
- }
2225
- ```
2226
-
2227
- Types: `'read'`, `'write'`, `'all'`
2228
-
2229
- #### @secureStorage(type, integrationId?)
2230
- Secures storage operations.
2231
-
2232
- ```typescript
2233
- @secureStorage('write')
2234
- allowWrite(context: StorageContext): boolean {
2235
- return !context.pathsInBucket.some(p => p.startsWith('/admin'));
2236
- }
2237
- ```
2238
-
2239
- Types: `'read'`, `'write'`, `'update'`, `'insert'`, `'delete'`, `'all'`
2240
-
2241
- #### @secureApi(integrationId, endpointId?)
2242
- Secures API integrations.
2243
-
2244
- ```typescript
2245
- @secureApi('stripe-api', 'create-charge')
2246
- allowCharge(context: ApiCallContext): boolean {
2247
- const amount = (context.body as any)?.amount;
2248
- return amount < 10000;
2249
- }
2250
-
2251
- @secureApi('external-api') // Secures entire API
2252
- allowApi(): boolean {
2253
- return this.isAuthenticated();
2254
- }
2255
- ```
2256
-
2257
- #### @secureNativeQuery(integrationId)
2258
- Secures native database queries.
2259
-
2260
- ```typescript
2261
- @secureNativeQuery('postgres-db')
2262
- allowQuery(context: NativeQueryContext): boolean {
2263
- if (context.type === 'relational') {
2264
- return !context.query.toUpperCase().includes('DROP');
2265
- }
2266
- return true;
2267
- }
2268
- ```
2269
-
2270
- #### @secureAiQuery(integrationId?)
2271
- Secures AI query execution.
2272
-
2273
- ```typescript
2274
- @secureAiQuery()
2275
- allowAiQuery(context: AiQueryContext): boolean {
2276
- return this.isAuthenticated() && context.prompt.length < 1000;
2277
- }
2278
- ```
2279
-
2280
- #### @secureAiAgent(agentId?)
2281
- Secures AI agent access.
2282
-
2283
- ```typescript
2284
- @secureAiAgent('customer-bot')
2285
- allowAgent(context: SecureAiAgentContext): boolean {
2286
- return !context.prompt?.includes('admin');
2287
- }
2288
-
2289
- @secureAiAgent() // All agents
2290
- allowAllAgents(): boolean {
2291
- return this.isAuthenticated();
2292
- }
2293
- ```
2294
-
2295
- #### @secureDistributedLock(mutexName?)
2296
- Secures distributed lock access.
2297
-
2298
- ```typescript
2299
- // Secure specific mutex
2300
- @secureDistributedLock('payment-processing')
2301
- allowPaymentLock(context: DistributedLockContext): boolean {
2302
- // context.mutex contains the mutex name
2303
- return this.isAuthenticated() && context.mutex === 'payment-processing';
2304
- }
2305
-
2306
- // Secure all mutexes
2307
- @secureDistributedLock()
2308
- allowAllLocks(context: DistributedLockContext): boolean {
2309
- return this.isAuthenticated();
2310
- }
2311
- ```
2312
-
2313
- **Important:** Without API key, you must define `@secureDistributedLock` decorators to allow lock acquisition.
2314
-
2315
- #### @aiFunction(options)
2316
- Exposes function to AI agents. Agents automatically call these functions based on user prompts.
2317
-
2318
- ```typescript
2319
- // Simple form: description and params array
2320
- @aiFunction('Returns the names of the pirates crew on a given ship name', [
2321
- { name: 'shipName', type: 'string', required: true, description: 'The name of the ship' }
2322
- ])
2323
- async getShipCrew({ shipName }: { shipName: string }): Promise<string[]> {
2324
- const crew = await getCrew(shipName);
2325
- return crew.map(m => m.name);
2326
- }
2327
-
2328
- // Options object form with more control
2329
- @aiFunction({
2330
- id: 'custom-function-id', // Optional custom ID
2331
- description: 'Get user profile',
2332
- params: [
2333
- { name: 'userId', type: 'string', required: true, description: 'User ID' }
2334
- ],
2335
- attributes: {
2336
- integrationType: ['salesforce'] // Only available when specific integrations connected
2337
- }
2338
- })
2339
- async getUserProfile(
2340
- params: { userId: string },
2341
- context: AiFunctionCallContext
2342
- ): Promise<User> {
2343
- console.log('Agent ID:', context.agentId);
2344
- console.log('Integration ID:', context.integrationId);
2345
- return { userId: params.userId, name: 'John' };
2346
- }
2347
-
2348
- // Parameter types: 'string' | 'number' | 'boolean' | 'date' | 'files'
2349
- @aiFunction('Books a hotel room', [
2350
- { name: 'hotelName', type: 'string', required: true, description: 'Name of hotel' },
2351
- { name: 'checkInDate', type: 'date', required: true, description: 'Check-in date' },
2352
- { name: 'numberOfGuests', type: 'number', required: true, description: 'Number of guests' },
2353
- { name: 'breakfast', type: 'boolean', required: false, description: 'Include breakfast' },
2354
- { name: 'roomType', type: 'string', required: true, description: 'Type of room',
2355
- enum: ['single', 'double', 'suite'] }
2356
- ])
2357
- async bookHotel(args: BookingArgs): Promise<string> {
2358
- return `Booked ${args.roomType} room at ${args.hotelName}`;
2359
- }
2360
-
2361
- // Using predefined parameters (hidden from AI)
2362
- // Call from client with: agent.ask(prompt, {
2363
- // functions: ['sendEmail'],
2364
- // predefinedParams: { sendEmail: { apiKey: 'secret' } }
2365
- // })
2366
- @aiFunction('Sends an email', [
2367
- { name: 'recipient', type: 'string', required: true, description: 'Email recipient' },
2368
- { name: 'subject', type: 'string', required: true, description: 'Email subject' },
2369
- { name: 'body', type: 'string', required: true, description: 'Email body' }
2370
- ])
2371
- async sendEmail(args: { recipient: string; subject: string; body: string; apiKey?: string }): Promise<string> {
2372
- await emailService.send(args.apiKey, args.recipient, args.subject, args.body);
2373
- return 'Email sent successfully';
2374
- }
2375
- ```
2376
-
2377
- **Using functions from client:**
2378
- ```typescript
2379
- const response = await agent.ask('What is the crew of the Black Pearl?', {
2380
- functions: ['getShipCrew', 'getShipDetails'] // Function names or custom IDs
2381
- });
2382
- ```
2383
-
2384
- #### @limits(options)
2385
- Rate/quota limiting.
2386
-
2387
- ```typescript
2388
- // Simple rate limit (5 QPS globally)
2389
- @limits({ rateLimit: 5 })
2390
- async limited(): Promise<void> {}
2391
-
2392
- // Quota (100 calls/month per user)
2393
- @limits({ quotaLimit: { value: 100, scope: 'user', renewPeriod: 'monthly' } })
2394
- async quotaLimited(): Promise<void> {}
2395
-
2396
- // Multiple limits
2397
- @limits({
2398
- rateLimit: [
2399
- { value: 100, scope: 'global' },
2400
- { value: 10, scope: 'user' }
2401
- ],
2402
- quotaLimit: [
2403
- { value: 10000, scope: 'global', renewPeriod: 'monthly' },
2404
- { value: 500, scope: 'user', renewPeriod: 'weekly' }
2405
- ]
2406
- })
2407
- async multiLimited(): Promise<void> {}
2408
- ```
2409
-
2410
- Scopes: `'global'`, `'user'`, `'ip'`
2411
- Periods: `'hourly'`, `'daily'`, `'weekly'`, `'monthly'`, `'quarterly'`, `'annually'`
2412
-
2413
- ## Common Patterns
2414
-
2415
- ### Authentication
2416
-
2417
- Client:
2418
- ```typescript
2419
- const squid = new Squid({
2420
- appId: 'YOUR_APP_ID',
2421
- region: 'us-east-1.aws',
2422
- authProvider: {
2423
- integrationId: 'auth0',
2424
- getToken: async () => await auth0.getAccessTokenSilently()
2425
- }
2426
- });
2427
- ```
2428
-
2429
- Backend:
2430
-
2431
- ```typescript
2432
- @executable()
2433
- async getUserData(): Promise<UserData> {
2434
- this.assertIsAuthenticated();
2435
- const userId = this.getUserAuth()?.userId;
2436
- if (!userId) throw new Error('User not authenticated');
2437
- return await fetchUserData(userId);
2438
- }
2439
-
2440
- @secureCollection('users', 'read')
2441
- allowRead(context: QueryContext): boolean {
2442
- const userId = this.getUserAuth()?.userId;
2443
- if (!userId) return false;
2444
- return context.isSubqueryOf('id', '==', userId);
2445
- }
2446
- ```
2447
-
2448
- ### File Upload
2449
-
2450
- Client:
2451
- ```typescript
2452
- const file: File = ...; // from input
2453
- await squid.storage().uploadFile('/uploads', file);
2454
- // OR
2455
- await squid.executeFunction('processFile', file);
2456
- ```
2457
-
2458
- Backend:
2459
- ```typescript
2460
- @executable()
2461
- async processFile(file: SquidFile): Promise<Result> {
2462
- console.log(file.originalName, file.size, file.mimetype);
2463
- const content = new TextDecoder().decode(file.data);
2464
- return { processed: true };
2465
- }
2466
- ```
2467
-
2468
- ### Using Squid Client in Backend
2469
-
2470
- ```typescript
2471
- export class MyService extends SquidService {
2472
- @executable()
2473
- async aggregateStats(userId: string): Promise<Stats> {
2474
- const orders = await this.squid.collection('orders')
2475
- .query()
2476
- .eq('userId', userId)
2477
- .snapshot();
2478
-
2479
- return { totalOrders: orders.data.length };
2480
- }
2481
- }
2482
- ```
2483
-
2484
- ## Best Practices
2485
-
2486
- 1. **Always secure collections/topics/storage** - Default deny
2487
- 2. **Validate input in executables** - Check auth and params
2488
- 3. **Use transactions for multi-doc updates** - Atomic operations
2489
- 4. **Limit query results** - Default 1000, max 20000
2490
- 5. **Use snapshots for one-time data** - Use subscriptions for realtime
2491
- 6. **Batch operations when possible** - insertMany, deleteMany
2492
- 7. **Use memoryOptions for AI conversations** - Not deprecated chatId
2493
- 8. **Test in dev before prod deployment** - `squid deploy --environmentId dev`
2494
-
2495
- ## Documentation
92
+ ## Links
2496
93
 
2497
94
  - Docs: https://docs.getsquid.ai/
2498
95
  - Console: https://console.getsquid.ai/
2499
96
  - Samples: https://github.com/squid-cloud-samples
2500
-
2501
- ---
2502
-
2503
- **This skill is verified against the actual Squid source code and contains only accurate, tested APIs.**