Project Structure
Understanding how to organize your Motia project is crucial for building maintainable and scalable workflow applications. This guide covers the directory structure, file naming conventions, and Motia's automatic step discovery system.
Basic Project Structure
Here's what a typical Motia project looks like:
File Descriptions
| File | Purpose | Type | Auto-Generated |
|---|---|---|---|
01-api-gateway.step.ts | TypeScript API endpoint | User Code | - |
02-data-processor.step.py | Python data processing | User Code | - |
03-send-notification.step.js | JavaScript automation | User Code | - |
custom-ui.step.tsx | Optional UI component | User Code | - |
package.json | Node.js dependencies (if using JS/TS) | Config | - |
requirements.txt | Python dependencies (if using Python) | Config | - |
tsconfig.json | TypeScript config (if using TypeScript) | Config | - |
types.d.ts | Type definitions for your project | Generated | ✅ By TypeScript |
motia-workbench.json | 🤖 Visual workflow positioning | Generated | ✅ By Motia |
config.yml | Optional Motia configuration | Config | - |
- Both the
steps/andsrc/directories are supported at the project root (e.g.,my-motia-project/stepsormy-motia-project/src). - You can freely nest steps in subfolders under either directory (e.g.,
steps/aaa/a1.step.ts,src/bbb/ccc/c1.step.py). - Discovery is recursive inside both directories, so deeper folder structures for large apps are supported.
- You can use either
steps/,src/, or both directories in your project.
Automatic Step Discovery
Motia will automatically discover and register any file that follows the .step. naming pattern as a workflow step. You don't need to manually register steps - just create a file with the right naming pattern and Motia will find it.
Discovery Rules
Motia scans your steps/ and src/ directories and automatically registers files as steps based on these rules:
- File must contain
.step.or_step.in the filename (e.g.,my-task.step.ts,my_task_step.py) - File must export a
configobject defining the step configuration - File must export a
handlerfunction containing the step logic - File extension determines the runtime (
.ts= TypeScript,.py= Python,.js= JavaScript)
When you run motia dev, Motia will:
- Scan both the
steps/andsrc/directories recursively - Find all files matching
*.step.*in both directories - Parse their
configexports to understand step types and connections - Register them in the workflow engine
- Make them available in the Workbench
File Naming Convention
Motia uses this specific pattern for automatic step discovery:
[prefix-]descriptive-name.step.[extension]
Supported Languages & Extensions
| Language | Extension | Example Step File | Runtime |
|---|---|---|---|
| TypeScript | .ts | user-registration.step.ts | Node.js with TypeScript |
| Python | .py | data-analysis.step.py | Python interpreter |
| JavaScript | .js | send-notification.step.js | Node.js |
Naming Examples by Step Type
| Step Type | TypeScript | Python | JavaScript |
|---|---|---|---|
| API Endpoint | 01-auth-api.step.ts | 01-auth-api.step.py or auth_api_step.py | 01-auth-api.step.js |
| Event Handler | process-order.step.ts | process-order.step.py or process_order_step.py | process-order.step.js |
| Cron Job | daily-report.step.ts | daily-report.step.py or daily_report_step.py | daily-report.step.js |
| Data Processing | transform-data.step.ts | ml-analysis.step.py or ml_analysis_step.py | data-cleanup.step.js |
Step Organization Patterns
<Tabs items={["Sequential", "Feature-Based", "Language-Specific"]}>
Sequential Flow Organization
Perfect for linear workflows where order matters:
| Step | Language | Purpose |
|---|---|---|
01-api-start.step.ts | TypeScript | API endpoint |
02-validate-data.step.py | Python | Data validation |
03-process-payment.step.js | JavaScript | Payment processing |
04-send-confirmation.step.ts | TypeScript | Email service |
05-cleanup.step.py | Python | Cleanup tasks |
Feature-Based Organization
Organize by business domains for complex applications:
Benefits:
- Logical grouping by business domain
- Easy to locate related functionality
- Team ownership by feature area
- Independent scaling and deployment
Language-Specific Organization
Group by programming language for team specialization:
Benefits:
- Team specialization by language
- Consistent tooling and patterns
- Easy onboarding for language experts
- Shared libraries and utilities
Language-Specific Configuration
TypeScript/JavaScript Projects
For Node.js-based steps, you'll need:
{
"name": "my-motia-app",
"version": "1.0.0",
"scripts": {
"dev": "motia dev",
"build": "motia build",
"start": "motia start"
},
"dependencies": {
"motia": "^0.5.12-beta.121",
"zod": "^3.24.4"
},
"devDependencies": {
"typescript": "^5.7.3",
"@types/node": "^20.0.0"
}
}
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "Node",
"esModuleInterop": true,
"strict": true,
"skipLibCheck": true
},
"include": ["**/*.ts", "**/*.tsx"],
"exclude": ["node_modules", "dist"]
}
Python Projects
For Python-based steps:
# Core Motia dependency
motia>=0.5.12
# Common dependencies
requests>=2.28.0
pydantic>=1.10.0
# Data processing (if needed)
pandas>=1.5.0
numpy>=1.21.0
Step Discovery Examples
Let's see how Motia discovers different step types:
Example 1: TypeScript API Step
import { ApiRouteConfig, Handlers } from 'motia'
import { z } from 'zod'
// Motia discovers this file because:
// 1. Filename contains '.step.'
// 2. Exports 'config' object
// 3. Has .ts extension -> uses TypeScript runtime
export const config: ApiRouteConfig = {
type: 'api',
name: 'user-api',
path: '/users',
method: 'GET',
emits: ['users.fetched'],
flows: ['user-management']
}
export const handler: Handlers['user-api'] = async (req, { emit }) => {
await emit({
topic: 'users.fetched',
data: { users: [] }
})
return {
status: 200,
body: { message: 'Users retrieved' }
}
}
Example 2: Python Event Step
# Motia discovers this file because:
# 1. Filename contains '.step.'
# 2. Exports 'config' dict
# 3. Has .py extension -> uses Python runtime
config = {
"type": "event",
"name": "data-processor",
"description": "Process incoming data with Python",
"subscribes": ["users.fetched"],
"emits": ["data.processed"],
"flows": ["user-management"]
}
async def handler(input_data, ctx):
"""Process the data"""
processed_data = {
"original": input_data,
"processed_at": ctx.utils.dates.now().isoformat(),
"count": len(input_data.get("users", []))
}
await ctx.emit({
"topic": "data.processed",
"data": processed_data
})
Example 3: JavaScript Automation Step
// Motia discovers this file because:
// 1. Filename contains '.step.'
// 2. Exports 'config' object
// 3. Has .js extension -> uses Node.js runtime
export const config = {
type: 'event',
name: 'send-notifications',
description: 'Send notifications via multiple channels',
subscribes: ['data.processed'],
emits: ['notifications.sent'],
flows: ['user-management']
}
export const handler = async (input, { emit, logger }) => {
logger.info('Sending notifications', { data: input })
// Send email, SMS, push notifications, etc.
const results = await Promise.all([
sendEmail(input),
sendSMS(input),
sendPush(input)
])
await emit({
topic: 'notifications.sent',
data: {
results,
sent_at: new Date().toISOString()
}
})
}
async function sendEmail(data) { /* implementation */ }
async function sendSMS(data) { /* implementation */ }
async function sendPush(data) { /* implementation */ }
Auto-Generated Files
Some files in your Motia project are automatically generated:
types.d.ts- TypeScript generates this for type definitionsmotia-workbench.json- Motia manages visual node positions in the Workbench
Multi-Language Project Example
Here's a real-world example showing how the three languages work together:
Architecture Breakdown
| Layer | Language | Purpose | Examples |
|---|---|---|---|
| API Layer | TypeScript | Fast API responses, type safety | Product catalog, user auth, order management |
| Processing Layer | Python | Data processing, ML, analytics | Inventory sync, recommendations, fraud detection |
| Automation Layer | JavaScript | Business automation, workflows | Email campaigns, fulfillment, customer support |
| Integration Layer | Multi-language | External system connections | Payment webhooks, ERP sync, social media |
Language Strengths & When to Use
| Language | Best For | Common Step Types | Example Use Cases |
|---|---|---|---|
| TypeScript | API endpoints, type safety, web integrations | API, Event, UI | REST APIs, webhooks, data validation |
| Python | Data science, ML, automation, integrations | Event, Cron | Data analysis, AI models, file processing |
| JavaScript | Automation, integrations, general scripting | Event, Cron | Email automation, webhooks, social media |
Discovery Troubleshooting
If Motia isn't discovering your steps:
Common Issues
<Tabs items={["Filename Issues", "Export Issues", "Location Issues"]}>
Missing .step. in filename
Missing config export
// No config export
export const handler = async () => {
console.log('This won't be found by Motia')
}
// Proper exports
export const config = {
type: 'event',
name: 'my-step',
subscribes: ['my-topic'],
emits: ['my-output'],
flows: ['my-flow']
}
export const handler = async (input, ctx) => {
// Motia will discover and register this step
}
File outside steps/ or src/ directory
Note: Both steps/ and src/ directories are supported. Files must be in one of these directories to be discovered.
Discovery Verification
Check if your steps are discovered:
# Run Motia in development mode
motia dev
# Look for discovery logs:
# ✅ Discovered step: user-api (TypeScript)
# ✅ Discovered step: data-processor (Python)
# ✅ Discovered step: send-notifications (JavaScript)
Next Steps
Now that you understand how Motia discovers and organizes steps:
- Learn about Core Concepts to understand how steps work together
- Explore Defining Steps for detailed step creation
- Check out API Steps for creating HTTP endpoints
- Dive into Event Steps for workflow orchestration