# Composite Storage `MastraCompositeStore` can compose storage domains from different providers. Use it when you need different databases for different purposes. For example, use LibSQL for memory and PostgreSQL for workflows. ## Installation `MastraCompositeStore` is included in `@mastra/core`: **npm**: ```bash npm install @mastra/core@latest ``` **pnpm**: ```bash pnpm add @mastra/core@latest ``` **Yarn**: ```bash yarn add @mastra/core@latest ``` **Bun**: ```bash bun add @mastra/core@latest ``` You'll also need to install the storage providers you want to compose: **npm**: ```bash npm install @mastra/pg@latest @mastra/libsql@latest ``` **pnpm**: ```bash pnpm add @mastra/pg@latest @mastra/libsql@latest ``` **Yarn**: ```bash yarn add @mastra/pg@latest @mastra/libsql@latest ``` **Bun**: ```bash bun add @mastra/pg@latest @mastra/libsql@latest ``` ## Storage domains Mastra organizes storage into five specialized domains, each handling a specific type of data. Each domain can be backed by a different storage adapter, and domain classes are exported from each storage package. | Domain | Description | | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `memory` | Conversation persistence for agents. Stores threads (conversation sessions), messages, resources (user identities), and working memory (persistent context across conversations). | | `workflows` | Workflow execution state. When workflows suspend for human input, external events, or scheduled resumption, their state is persisted here to enable resumption after server restarts. | | `scores` | Evaluation results from Mastra's evals system. Scores and metrics are persisted here for analysis and comparison over time. | | `observability` | Telemetry data including traces and spans. Agent interactions, tool calls, and LLM requests generate spans collected into traces for debugging and performance analysis. | | `agents` | Agent configurations for stored agents. Enables agents to be defined and updated at runtime without code deployments. | ## Usage ### Basic composition Import domain classes directly from each store package and compose them: ```typescript import { MastraCompositeStore } from "@mastra/core/storage"; import { WorkflowsPG, ScoresPG } from "@mastra/pg"; import { MemoryLibSQL } from "@mastra/libsql"; import { Mastra } from "@mastra/core"; export const mastra = new Mastra({ storage: new MastraCompositeStore({ id: "composite", domains: { memory: new MemoryLibSQL({ url: "file:./local.db" }), workflows: new WorkflowsPG({ connectionString: process.env.DATABASE_URL }), scores: new ScoresPG({ connectionString: process.env.DATABASE_URL }), }, }), }); ``` ### With a default storage Use `default` to specify a fallback storage, then override specific domains: ```typescript import { MastraCompositeStore } from "@mastra/core/storage"; import { PostgresStore } from "@mastra/pg"; import { MemoryLibSQL } from "@mastra/libsql"; import { Mastra } from "@mastra/core"; const pgStore = new PostgresStore({ id: "pg", connectionString: process.env.DATABASE_URL, }); export const mastra = new Mastra({ storage: new MastraCompositeStore({ id: "composite", default: pgStore, domains: { memory: new MemoryLibSQL({ url: "file:./local.db" }), }, }), }); ``` ## Options **id:** (`string`): Unique identifier for this storage instance. **default?:** (`MastraCompositeStore`): Default storage adapter. Domains not explicitly specified in \`domains\` will use this storage's domains as fallbacks. **domains?:** (`object`): Individual domain overrides. Each domain can come from a different storage adapter. These take precedence over the default storage. **domains.memory?:** (`MemoryStorage`): Storage for threads, messages, and resources. **domains.workflows?:** (`WorkflowsStorage`): Storage for workflow snapshots. **domains.scores?:** (`ScoresStorage`): Storage for evaluation scores. **domains.observability?:** (`ObservabilityStorage`): Storage for traces and spans. **domains.agents?:** (`AgentsStorage`): Storage for stored agent configurations. **disableInit?:** (`boolean`): When true, automatic initialization is disabled. You must call init() explicitly. ## Initialization `MastraCompositeStore` initializes each configured domain independently. When passed to the Mastra class, `init()` is called automatically: ```typescript import { MastraCompositeStore } from "@mastra/core/storage"; import { MemoryPG, WorkflowsPG, ScoresPG } from "@mastra/pg"; import { Mastra } from "@mastra/core"; const storage = new MastraCompositeStore({ id: "composite", domains: { memory: new MemoryPG({ connectionString: process.env.DATABASE_URL }), workflows: new WorkflowsPG({ connectionString: process.env.DATABASE_URL }), scores: new ScoresPG({ connectionString: process.env.DATABASE_URL }), }, }); export const mastra = new Mastra({ storage, // init() called automatically }); ``` If using storage directly, call `init()` explicitly: ```typescript import { MastraCompositeStore } from "@mastra/core/storage"; import { MemoryPG } from "@mastra/pg"; const storage = new MastraCompositeStore({ id: "composite", domains: { memory: new MemoryPG({ connectionString: process.env.DATABASE_URL }), }, }); await storage.init(); // Access domain-specific stores via getStore() const memoryStore = await storage.getStore("memory"); const thread = await memoryStore?.getThreadById({ threadId: "..." }); ``` ## Use cases ### Separate databases for different workloads Use a local database for development while keeping production data in a managed service: ```typescript import { MastraCompositeStore } from "@mastra/core/storage"; import { MemoryPG, WorkflowsPG, ScoresPG } from "@mastra/pg"; import { MemoryLibSQL } from "@mastra/libsql"; const storage = new MastraCompositeStore({ id: "composite", domains: { // Use local SQLite for development, PostgreSQL for production memory: process.env.NODE_ENV === "development" ? new MemoryLibSQL({ url: "file:./dev.db" }) : new MemoryPG({ connectionString: process.env.DATABASE_URL }), workflows: new WorkflowsPG({ connectionString: process.env.DATABASE_URL }), scores: new ScoresPG({ connectionString: process.env.DATABASE_URL }), }, }); ``` ### Specialized storage for observability Observability data can quickly overwhelm general-purpose databases in production. A single agent interaction can generate hundreds of spans, and high-traffic applications can produce thousands of traces per day. **ClickHouse** is recommended for production observability because it's optimized for high-volume, write-heavy analytics workloads. Use composite storage to route observability to ClickHouse while keeping other data in your primary database: ```typescript import { MastraCompositeStore } from "@mastra/core/storage"; import { MemoryPG, WorkflowsPG, ScoresPG } from "@mastra/pg"; import { ObservabilityStorageClickhouse } from "@mastra/clickhouse"; const storage = new MastraCompositeStore({ id: "composite", domains: { memory: new MemoryPG({ connectionString: process.env.DATABASE_URL }), workflows: new WorkflowsPG({ connectionString: process.env.DATABASE_URL }), scores: new ScoresPG({ connectionString: process.env.DATABASE_URL }), observability: new ObservabilityStorageClickhouse({ url: process.env.CLICKHOUSE_URL, username: process.env.CLICKHOUSE_USERNAME, password: process.env.CLICKHOUSE_PASSWORD, }), }, }); ``` > **Info:** This approach is also required when using storage providers that don't support observability (like Convex, DynamoDB, or Cloudflare). See the [DefaultExporter documentation](https://mastra.ai/docs/observability/tracing/exporters/default) for the full list of supported providers.