Use Mastra to build workflows, then use this component to run them on Convex as durable functions.
- Run workflows asynchronously. Fire and forget from a serverless function (mutation or action).
- Track the status of the workflow. Reactive queries and run-to-completion utilities. Or just write to the database from your steps and use normal Convex reactivity.
- Resume a workflow from where it left off, after suspending it for user input.
- Define retry configurations, which will use exponential backoff and jitter, and not use server resources in between retries. Each step will run in its own serverless function, providing scaling and isolation.
- Define parallelism limits, to avoid workflows competing for resources with live requests.
- Full support for Mastra's step forking, joining, triggering, and more.
- Runtime validation of input, output, and trigger schemas.
export const workflowAction = registry.define(workflow);
// ...
const { start } = await runner.create( ctx, internal.file.workflowAction);
const result = await start({...});
- Agentic workflows, such as taking user input, calling multiple LLMs, calling third parties, etc.
- Durable functions including multiple steps that may fail due to third-party API failures.
- When you want to use the Workpool for multiple asynchronous steps.
Found a bug? Feature request? File it here.
- Add Convex as a Vector provider for Mastra.
- Provide Storage and Vector integrations for using Convex from Mastra servers.
- Enables running from both
mastra dev
andconvex dev
for fast iterations. - Enables using Convex for Agent Memory.
- Enables running from both
- Support exposing the same
hono
HTTP API as Mastra servers. - Provide helpers to export functions so browsers can call them safely.
- Add a custom mutation step, for a transactional step that will always terminate without needing a retry configuration (built-in for Convex).
- Support better Logging and Tracing.
You'll need an existing Convex project to use the component. Convex is a hosted backend platform, including a database, serverless functions, and a ton more you can learn about here.
Run npm create convex
or follow any of the quickstarts to set one up.
Install the component package:
npm install @convex-dev/mastra
NOTE: You also need to:
- Directly install
@libsql/client
- Mark it as an external package
- Export it from a file in your /convex folder due to current issues.
You can do all of this by running the following commands from the project root:
npm install -D @libsql/client
echo '{"node":{"externalPackages":["@libsql/client"]}}' > convex.json
printf '"use node";\nexport * as _ from "@libsql/client";' > convex/_libsql_workaround.ts
Create a convex.config.ts
file in your app's convex/
folder and install the component by calling use
:
// convex/convex.config.ts
import { defineApp } from "convex/server";
import mastra from "@convex-dev/mastra/convex.config";
const app = defineApp();
app.use(mastra);
export default app;
"use node";
import { components } from "./_generated/api";
import { WorkflowRegistry } from "@convex-dev/mastra/registry";
import { createStep, Workflow } from "@mastra/core";
const MyStep = createStep({
id: "MyStep",
execute: async ({ context, suspend }) => {
//...
},
});
const myWorkflow = new Workflow({
name: "myWorkflow",
})
.step(MyStep)
//...more steps
.commit();
const registry = new WorkflowRegistry(components.workflow);
export const myWorkflowAction = registry.define(workflow);
You can run this from any server function (mutation or action, node or otherwise)
const runner = new WorkflowRunner(components.mastra);
const { runId, startAsync, start, resume } = await runner.create(
ctx,
internal.myNodeFile.myWorkflowAction // the one you registered
);
const result = await start({...});
// OR
await startAsync({...});
// OR
await resume({...});
// Can call any of these from anywhere.
await runner.waitForCompletion(ctx, runId);
const status = await runner.getStatus(ctx, runId);
See more example usage in example.ts.
- You can't currently register workflows dynamically. The action you register needs to exist at import time and the implementation needs a reference to the in-memory workflow you create.
- For local development, you need to run
mastra dev
in Node 20, butconvex dev
in Node 18. If you see issues about syscalls at import time, try using the cloud dev environment instead.
- [ ] Support variable mapping for step inputs ("variables" config for steps)
- [ ] Support conditional evaluation ("when" config for steps)
- [ ] Return the same output format as Mastra
start
/resume
. - [ ] Should we add
resumeAsync
? It's currently async only. - [ ] Validate the trigger data later to surface a better error message.
- [ ] Validate the Storage and Vector implementations (from Convex).
- [ ] Detect infinite loops at runtime. They don't yet do cycle detection at runtime or initialization time, and for dynamic "when" it's not possible.
- [ ] Performance optimizations
- [ ] Re-use the workflowd config document if it hasn't changed.
- [ ] Assume the workflow config is valid and fetch configuration lazily o.w.
- [ ] Support using Storage and Vector from
mastra dev
. - [ ] Support events - triggering and resuming with events.
- [ ] Share state between Mastra workflows and the Component.
- [ ] Configurable vacuuming of workflow state.
- [ ] Support the playground hitting Convex.
- [ ] Support running workflows in the default runtime.
- Better logging and tracing.
- Provide a Mutation Step to avoid the v8 action and is executed exactly once.
- Workflows currently only run in Node Actions. You can create/start/resume them from anywhere, but each step will be executed in the node runtime. This is a bit slower and more expensive than running in the default runtime.
- Using the
ConvexStorage
from Mastra doesn't share state with workflows made via the Component. They're currently stored in separate tables with different schemas.
✘ [ERROR] Could not resolve "assert"
node_modules/sonic-boom/index.js:8:23:
8 │ const assert = require('assert')
╵ ~~~~~~~~
The package "assert" wasn't found on the file system but is built into node. Are you trying to
bundle for node? You can use "platform: 'node'" to do that, which will remove this error.
✖ It looks like you are using Node APIs from a file without the "use node" directive.
This is because you're using a Node API in a file that doesn't have the "use node"
directive.
Or you're importing a file in your project that imports from a node dependency that doesn't have the "use node"
directive.
To fix this, add the "use node"
directive to the file. Note: these files can
only have actions, since mutations and queries only run in the default runtime.