UNPKG

@mastra/core

Version:

Mastra is a framework for building AI-powered applications and agents with a modern TypeScript stack.

218 lines (155 loc) 6.68 kB
# Cloudflare D1 storage The Cloudflare D1 storage implementation provides a serverless SQL database solution using Cloudflare D1, supporting relational operations and transactional consistency. > **Observability Not Supported:** Cloudflare D1 storage **doesn't support the observability domain**. Traces from the `MastraStorageExporter` can't be persisted to D1, and [Studio's](https://mastra.ai/docs/studio/overview) observability features won't work with D1 as your only storage provider. To enable observability, use [composite storage](https://mastra.ai/reference/storage/composite) to route observability data to a supported provider like ClickHouse. > **Row Size Limit:** Cloudflare D1 enforces a **1 MiB maximum row size**. This limit can be exceeded when storing messages with base64-encoded attachments such as images. See [Handling large attachments](https://mastra.ai/docs/memory/storage) for workarounds including uploading attachments to external storage. ## Installation **npm**: ```bash npm install @mastra/cloudflare-d1@latest ``` **pnpm**: ```bash pnpm add @mastra/cloudflare-d1@latest ``` **Yarn**: ```bash yarn add @mastra/cloudflare-d1@latest ``` **Bun**: ```bash bun add @mastra/cloudflare-d1@latest ``` ## Usage ### Using with Mastra CloudflareDeployer The standard way to use D1Store with Mastra on Cloudflare is with `CloudflareDeployer`. Import `env` from `cloudflare:workers` and initialize `D1Store` inline inside `new Mastra({...})`. ```typescript import { env } from 'cloudflare:workers' import { D1Store } from '@mastra/cloudflare-d1' import { Mastra } from '@mastra/core' import { CloudflareDeployer } from '@mastra/deployer-cloudflare' export const mastra = new Mastra({ storage: new D1Store({ binding: env.DB }), deployer: new CloudflareDeployer({ name: 'my-worker', d1_databases: [ { binding: 'DB', database_name: 'your-database-name', database_id: 'your-database-id', }, ], }), }) ``` > **Note:** When using `import { env } from 'cloudflare:workers'`, `D1Store` must be initialized inline inside `new Mastra({...})` — not extracted to a module-level variable. Alternatively, initialize `D1Store` inside the `fetch` handler after `env` is available. See [CloudflareDeployer reference](https://mastra.ai/reference/deployer/cloudflare) for details. ### Using in a Cloudflare Worker without HTTP routes If you want to call Mastra directly in a Workerfor example, to run an agent or trigger a workflow — without serving HTTP routes, you don't need `CloudflareDeployer`. Access the D1 binding from the worker's `env` parameter and call Mastra programmatically. ```typescript import { D1Store } from '@mastra/cloudflare-d1' import { Mastra } from '@mastra/core' type Env = { DB: D1Database } export default { async fetch(request: Request, env: Env, ctx: ExecutionContext) { const mastra = new Mastra({ storage: new D1Store({ binding: env.DB }), }) const agent = mastra.getAgent('my-agent') const result = await agent.generate('Hello') return Response.json({ text: result.text }) }, } ``` ### Using with REST API For non-Workers environments (Node.js, serverless functions, etc.), use the REST API approach: ```typescript import { D1Store } from '@mastra/cloudflare-d1' const storage = new D1Store({ accountId: process.env.CLOUDFLARE_ACCOUNT_ID!, // Cloudflare Account ID databaseId: process.env.CLOUDFLARE_D1_DATABASE_ID!, // D1 Database ID apiToken: process.env.CLOUDFLARE_API_TOKEN!, // Cloudflare API Token tablePrefix: 'dev_', // Optional: isolate tables per environment }) ``` ### Wrangler Configuration Add the D1 database binding to your `wrangler.toml`: ```toml [[d1_databases]] binding = "DB" database_name = "your-database-name" database_id = "your-database-id" ``` Or in `wrangler.jsonc`: ```jsonc { "d1_databases": [ { "binding": "DB", "database_name": "your-database-name", "database_id": "your-database-id", }, ], } ``` ## Parameters **binding** (`D1Database`): Cloudflare D1 Workers binding (for Workers runtime) **accountId** (`string`): Cloudflare Account ID (for REST API) **databaseId** (`string`): Cloudflare D1 Database ID (for REST API) **apiToken** (`string`): Cloudflare API Token (for REST API) **tablePrefix** (`string`): Optional prefix for all table names (useful for environment isolation) ## Additional notes ### Schema Management The storage implementation handles schema creation and updates automatically. It creates the following tables: - `threads`: Stores conversation threads - `messages`: Stores individual messages - `metadata`: Stores additional metadata for threads and messages ### Initialization When you pass storage to the Mastra class, `init()` is called automatically before any storage operation: ```typescript import { Mastra } from '@mastra/core' import { D1Store } from '@mastra/cloudflare-d1' type Env = { DB: D1Database } // In a Cloudflare Worker export default { async fetch(request: Request, env: Env, ctx: ExecutionContext) { const storage = new D1Store({ binding: env.DB, }) const mastra = new Mastra({ storage, // init() is called automatically }) // Your handler logic here return new Response('Success') }, } ``` If you're using storage directly without Mastra, you must call `init()` explicitly to create the tables: ```typescript import { D1Store } from '@mastra/cloudflare-d1' type Env = { DB: D1Database } // In a Cloudflare Worker export default { async fetch(request: Request, env: Env, ctx: ExecutionContext) { const storage = new D1Store({ id: 'd1-storage', binding: env.DB, }) // Required when using storage directly await storage.init() // Access domain-specific stores via getStore() const memoryStore = await storage.getStore('memory') const thread = await memoryStore?.getThreadById({ threadId: '...' }) return new Response('Success') }, } ``` > **Warning:** If `init()` isn't called, tables won't be created and storage operations will fail silently or throw errors. ### Transactions & Consistency Cloudflare D1 provides transactional guarantees for single-row operations. This means that multiple operations can be executed as a single, all-or-nothing unit of work. ### Table Creation & Migrations Tables are created automatically when storage is initialized (and can be isolated per environment using the `tablePrefix` option), but advanced schema changes—such as adding columns, changing data types, or modifying indexes—require manual migration and careful planning to avoid data loss.