Motia Icon
Core Concepts

Steps

One primitive to build any backend. Simple, composable, and multi-language.

One Primitive for Any Backend

A Step is the core primitive in Motia. Instead of juggling separate frameworks for APIs, background jobs, queues, or workflows, you define everything in one place: how it runs, when it runs, where it runs, and what it does.

Every Step file contains two parts:

  • Config → defines when and how the Step runs, and gives it a unique name
  • Handler → the function that executes your business logic

Motia automatically discovers any file ending in .step.ts, .step.js, or _step.py.
The filename tells Motia to load it, and the name in the config uniquely identifies the Step inside your system.


The Simplest Example

steps/hello.step.ts
import { ApiRouteConfig, Handlers } from 'motia';
 
export const config: ApiRouteConfig = {
  name: 'HelloStep',
  type: 'api',
  path: '/hello',
  method: 'GET'
};
 
export const handler: Handlers['HelloStep'] = async (req, { logger }) => {
  logger.info('Hello endpoint called');
  return { status: 200, body: { message: 'Hello world!' } };
};

👉 That’s all you need to make a running API endpoint.
Motia will auto-discover this file and wire it into your backend.


Steps Work Together: Emit + Subscribe

Steps aren’t isolated. They communicate by emitting and subscribing to events.
This is the core of how you build backends with Motia.

Example Flow: API Step → Event Step

steps/send-message.step.ts
import { ApiRouteConfig, Handlers } from 'motia';
 
export const config: ApiRouteConfig = {
  name: 'SendMessage',
  type: 'api',
  path: '/messages',
  method: 'POST',
  emits: ['message.sent']
};
 
export const handler: Handlers['SendMessage'] = async (req, { emit }) => {
  await emit({
    topic: 'message.sent',
    data: { text: req.body.text }
  });
  return { status: 200, body: { ok: true } };
};
steps/process-message.step.ts
import { EventConfig, Handlers } from 'motia';
 
export const config: EventConfig = {
  name: 'ProcessMessage',
  type: 'event',
  subscribes: ['message.sent']
};
 
export const handler: Handlers['ProcessMessage'] = async (input, { logger }) => {
  logger.info('Processing message', input);
};

👉 With just two files, you have an API endpoint that triggers an event-driven workflow.


Triggers

Every Step has a type that defines how it triggers:

TypeWhen it runsUse case
apiHTTP requestREST APIs, webhooks
eventEvent emittedBackground jobs, workflows
cronScheduleCleanup, reports, reminders

API Trigger

Runs when an HTTP request hits the path.

Example:

import { ApiRouteConfig, Handlers } from 'motia'
 
export const config: ApiRouteConfig = {
  name: 'GetUser',
  type: 'api',
  path: '/users/:id',
  method: 'GET'
}
 
export const handler: Handlers['GetUser'] = async (req, { logger }) => {
  const userId = req.pathParams.id
  logger.info('Getting user', { userId })
  return { status: 200, body: { id: userId, name: 'John' } }
}

Config:

PropertyDescription
nameUnique identifier
typeSet to 'api'
pathURL path (supports :params)
methodGET, POST, PUT, DELETE
bodySchemaValidate request body

Handler: handler(req, ctx)

  • req - Request with body, headers, pathParams, queryParams
  • ctx - Context with logger, emit, state, streams, traceId
  • Returns { status, body, headers? }

Context Object

Every handler receives a ctx object with these tools:

PropertyDescription
loggerStructured logging (info, warn, error)
emitTrigger other Steps by emitting events
statePersistent key-value storage
streamsReal-time data channels for clients
traceIdUnique ID for tracing requests & workflows

Core Functionality

State – Persistent Data

Key-value storage shared across Steps and workflows.

await state.set(traceId, 'preferences', { theme: 'dark' });
const prefs = await state.get(traceId, 'preferences');

Logging – Structured & Contextual

For debugging, monitoring, and observability.

logger.info('Processing user', { userId: '123' });

Streams – Real-Time Data

Push updates directly to connected clients.

await streams.chat.set('room-123', 'msg-456', { text: 'Hello!' });

Remember

  • Steps are just files. Export a config and handler.
  • Motia auto-discovers and connects them.
  • Combine Steps with emit + subscribe to build APIs, workflows, background jobs, or entire systems.

What’s Next?

👉 Build your first app →

Need help? See our Community Resources for questions, examples, and discussions.