import { workbenchXPath, TutorialStep } from '@motiadev/workbench' export const steps: TutorialStep[] = [ { title: 'Welcome to Motia', image: { height: 200, src: 'https://github.com/MotiaDev/motia/raw/main/packages/docs/public/github-readme-banner.png', }, description: () => (
Motia is an all-in-one framework for modern backend systems. Out of the box support for API endpoints,
background jobs, scheduled tasks and agentic workflow orchestration through a unified runtime. Thanks to its
event driven architecture you can run tasks in parallel, stream data to clients, or allow for seamless
orchestration of flows.
Let's start with Workbench, it is a development tool provided by Motia's ecosystem, from here you'll be
able to visualize your flows and observe their behavior.
💡 If you are already familiar with Motia, you can skip this tutorial.
Let's evaluate the Step that will allow you to receive traffic from external applications, API Steps will allow you to expose an HTTP endpoint for external traffic.
), before: [ { type: 'click', selector: workbenchXPath.links.flows }, { type: 'click', selector: workbenchXPath.flows.dropdownFlow('basic-tutorial') }, ], }, { elementXpath: workbenchXPath.flows.previewButton('apitrigger'), title: 'Code Preview', description: () =>Clicking on this icon will allow you to visualize the source code for a given Step.
, before: [ { type: 'click', selector: workbenchXPath.closePanelButton, optional: true, }, ], }, { elementXpath: workbenchXPath.sidebarContainer, title: 'Step Config', description: () => (
All Steps are defined by two main components, the configuration and the handler.
Let's start with the configuration, the common config attributes are
type, name, description, and flows.
There are specific configuration attributes for an API Step. Let's start with the method attribute. This
will declare the type of HTTP method used to talk to your API Step.
Through the path attribute you'll declare the url path used to trigger your API Step
The bodySchema attribute will define the shape of the request body.
💡 Both the request body and response payload are defined by zod schemas
Through the responseSchema attribute you can declare the different type of http responses based on the
http status code.
💡 Both the request body and response payload are defined by zod schemas
Motia allows you to interact between Steps or flows through an event driven architecture.
In order to connect your Steps during runtime you will use the emits and subscribes attributes.
Through the emits, you can specify a list of topics that your Step emits for others to subscribe.
Now that we've covered how to declare a Step, let's dive into the Step Handler.
Handlers are essential for the execution of your Step. For API Steps, the handler will receive the request
object as the first argument, followed by a second argument that provides access to the logger,{' '}
event emitter, state manager, and trace id.
💡 We will cover these in depth further down the tutorial.
We recommend using the provided logger util in order to guarantee observability through Motia's
ecosystem.
You can use logger similar to console.log for js or print for python, but with enhanced utilities,
such as being able to provide additional context.
Motia will take care of the rest to provide the best experience to visualize your logs and tie them through
tracing.
Now let's wrap our API Step and return a response.
You simply need to return an object that complies with one of the responseSchema definitions
declared in your Step configuration.
Now that we have an entry point in our flow, let's focus on subscribing to a topic and performing a
specific task.
For this we will look at the Event Step.
Event Steps are essential for Motia's event driven architecture. Let's dive deeper into the
anatomy of an Event Step by taking a look at the code visualization tool.
💡 Event Steps can only be triggered internally, through topic subscriptions.
Now that we have an entry point in our flow, let's focus on subscribing to a topic and performing a
specific task.
For this we will look at the Event Step.
Event Steps are essential for Motia's event driven architecture. Let's dive deeper into the
anatomy of an Event Step by taking a look at the code visualization tool.
💡 Event Steps can only be triggered internally, through topic subscriptions.
Event Steps, like other Step types, are composed of a configuration and a handler.
Event Steps have a specific attribute from their config, the input attribute, which declares the
data structure provided by the topic it is subscribed to.
The input attributes is defined as a zod schema, think of the input attributes as a contract for
other Steps that emit the topics that your Step subscribes to.
💡 Multiple Steps can subscribe to the same topic, but their input schema must be the same.
Let's take a look at the Event Step Handler.
The handler will seem familiar to other Step handlers, but notice that the first argument holds the data
provided for the topic or topics your Step subscribes to.
💡 The first argument will match the structure of your input schema, defined in the Event Step config.
Let's take a closer look at storing data in state.
In this example we are persisting the result of a third party HTTP request in State, scoping it to a
group id named "orders".
Let's do a recap of what you've learned. Thus far, you've become familiar with two Step types: API{' '}
and Event Steps.
You've also started to learn how to navigate around Workbench. Let's wrap up Motia's Step types with the last
one: the CRON Step. Let's take a deeper look at its definition.
CRON Steps are similar to the other Step types, they are composed by a configuration and a handler.
The CRON Step config has a distinct attribute, the cron attribute, through this attribute you will
define the cron schedule for your Step.
For instance, in this example the cron schedule is configured to execute the Step handler every 5 minutes. Let's
take a look at the handler definition.
The CRON Step handler only receives one argument, which is the Motia context, if you recall the Motia
context gives you access to utilities to emit topics, log, manage state, and it provides
the trace id associated to your Step's execution.
In this CRON Step example we are evaluating orders persisted in state, and emitting warnings through a topic for
each order that hasn't been processed and has a shipping date in the past.
Now that we've looked at Motia primitives, let's trigger the API Step from the endpoints section in
Workbench.
💡 All of your API Steps declare HTTP endpoints that can be reviewed and tested from the Endpoints{' '}
section in Workbench.
This section will display all of the endpoints declared in your API Steps. It will list the HTTP method, the URL path, and the description declared in the Step configuration.
), before: [{ type: 'click', selector: workbenchXPath.links.endpoints }], }, { elementXpath: workbenchXPath.endpoints.endpoint('POST', '/basic-tutorial'), title: 'Endpoints Tool', description: () => (Clicking on an endpoint from the list will open the endpoint overview which provides documentation on how to use the endpoint and a tool to test the endpoint.
), }, { elementXpath: workbenchXPath.endpoints.callPanel, title: 'API Endpoint Playground', description: () => (
Once you click on an endpoint from the list, you will be able to test it by providing a request payload and
clicking on the Play button.
This section will provide an overview of your API endpoint.
It will display documentation on how to use the endpoint in the Details Tab, and a form to test the
endpoint in the Call Tab.
Clicking on this button will open the specification of your API endpoint. Like response and request schemas.
), }, { elementXpath: workbenchXPath.sidebarContainer, title: 'API Endpoint Specification', description: () => (This is what you see when clicking on the Details button.
), before: [{ type: 'click', selector: workbenchXPath.endpoints.specButton }], }, { elementXpath: workbenchXPath.endpoints.callPanel, title: 'API Endpoint Test', description: () => (
This form will allow you to validate your API Step by executing an HTTP request against your API endpoint.
You can also test your API endpoints using your terminal through the curl command.
💡 Thanks to the bodySchema attribute from the API Step config, you are automatically provided with a
sample request payload.
curl -X POST http://localhost:3000/basic-tutorial \
{' '}-H "Content-Type: application/json" \
{' '}-d '
{JSON.stringify({
pet: { name: 'Jack', photoUrl: 'https://images.dog.ceo/breeds/pug/n02110958_13560.jpg' },
foodOrder: { id: 'food-order-1', quantity: 0 },
})}
'
),
before: [
{ type: 'click', selector: workbenchXPath.closePanelButton },
{ type: 'click', selector: workbenchXPath.endpoints.bodyTab },
],
},
{
elementXpath: workbenchXPath.endpoints.playButton,
title: 'API Endpoint Test',
description: () => (
Once you've filled the request payload, you can click on the Play button to trigger an HTTP request against your API endpoint.
), before: [ { type: 'fill-editor', content: { pet: { name: 'Jack', photoUrl: 'https://images.dog.ceo/breeds/pug/n02110958_13560.jpg' }, foodOrder: { id: 'food-order-1', quantity: 0 }, }, }, ], }, { elementXpath: workbenchXPath.endpoints.response, title: 'Test Result', description: () =>Once your request has been resolved, you will see the response from here.
, before: [{ type: 'click', selector: workbenchXPath.endpoints.playButton }], }, // Tracing { elementXpath: workbenchXPath.bottomPanel, title: 'Tracing', description: () => (
Great! You have triggered your first flow, now let's take a look at our example flow behavior using Workbench's
observability tools.
Let's start with tracing, in this section you will be able to see all of your flow executions grouped by{' '}
trace id.
Trace IDs are auto generated and injected throughout the execution of all Steps in your flow.
Clicking on a trace item from this list will allow you to dive deeper into your flow behavior.
This section will show all Step executions associated to the selected trace, you will see a list of executed Steps and their sequencing over a timeline.
), }, { elementXpath: workbenchXPath.tracing.timeline(1), title: 'Trace Timeline Segment', description: () => (Each timeline segment will show you the time it took to execute each Step, you can click on any segment and dive even deeper into that specific Step execution logs.
), }, { elementXpath: workbenchXPath.sidebarContainer, title: 'Trace Details', description: () => (
This is the Trace Details View, this will allow you to look deeper into the logs raised during the
execution of a Step.
💡 This is a simplified version of the logs, if you wish to look further into a log you will need to use the{' '}
Logs Tool.
Let's take a look at your execution logs, click on this tab will take you to the Logs Tool.
), before: [{ type: 'click', selector: workbenchXPath.links.logs }], }, { elementXpath: workbenchXPath.logs.traceColumn(1), title: 'Filtering by Trace ID', description: () => (
Your log results will show their associated Trace ID in the third column, the Trace ID values are
linked to update your search.
💡 Clicking a Trace ID will narrow down your search to only show logs from that trace.
By clicking the Trace ID, your search is updated to match results associated with that Trace ID.
), before: [{ type: 'click', selector: workbenchXPath.logs.traceColumn(1) }], }, { elementXpath: workbenchXPath.sidebarContainer, title: 'Logs', description: () => (
When you click on a log row, it will open the Log Details View.
In here you will be able to look at your log details (Log Level, Timestamp, Step Name,{' '}
Flow Name, and Trace ID), along with any additional context you've provided in your log call.
Ok now that we've seen the observability tools, let's take a look at the State Management Tool.
), before: [{ type: 'click', selector: workbenchXPath.links.states }], }, { elementXpath: workbenchXPath.states.container, title: 'State Management Tool', description: () => (This is your State Management Tool, from here you will be able to see all of your persisted state key/value pairs.
), before: [{ type: 'click', selector: workbenchXPath.states.row(1) }], }, { elementXpath: workbenchXPath.sidebarContainer, title: 'State Details', description: () => (This section presents the details for a given state key, from here you will be able to manage the value assigned to the selected state key.
), before: [{ type: 'click', selector: workbenchXPath.links.states }], }, // End of Tutorial { title: 'Congratulations 🎉', link: 'https://www.motia.dev/docs', description: () => (
You've completed our Motia basics tutorial!
You've learned about Motia's Step types, how to navigate around Workbench, and how to use core features from the
Motia Framework (State Management, Logging, and Tracing).
We recommend you give our{' '}
core concepts
{' '}
a read if you wish to learn further about Motia's fundamentals.
Don't forget to join our{' '}
Discord community
{' '}
or tag us in socials to show us what you've built with Motia.
We are an open source project, so feel free to raise your{' '}
issues
{' '}
or{' '}
suggestions
{' '}
in our{' '}
Github repo
.
Thank you for going this far in our tutorial!