Posted on :: 1697 Words :: Tags: , , , , , ,

Introduction

Modern backend development often requires juggling multiple frameworks: Express for APIs, BullMQ for queues, Temporal for workflows, and various tools for observability. Motia changes this paradigm by unifying all these concerns around a single core primitive called the Step.

In this comprehensive guide, we'll explore how Motia revolutionizes backend development, making it as simple as React made frontend development.

Sample Repository

A complete working example with all features discussed in this article is available on GitHub:

https://github.com/khuongdo/motia-backend-sample

Clone and run:

git clone https://github.com/khuongdo/motia-backend-sample.git
cd motia-backend-sample
npm install
cp .env.example .env
docker-compose up -d
npm run dev

The repository includes:

  • Full TypeScript + Python implementation
  • i18n with 3 languages (English, Vietnamese, Japanese)
  • Authentication & authorization
  • Error handling & validation
  • Database integration
  • Rate limiting
  • Scheduled jobs
  • AI integration examples
  • Docker deployment setup

What is Motia?

Motia is a multi-language backend framework that unifies:

  • REST APIs
  • Background jobs and queues
  • Durable workflows
  • Real-time streams
  • AI agents and agentic workflows
  • Built-in observability and state management

All of this is accomplished through one fundamental building block: the Step.

The Core Primitive: Steps

Just as React introduced components to simplify frontend development, Motia introduces Steps to unify backend concerns. A Step is simply a file containing:

  1. Config: Defines execution timing, naming, and triggers
  2. Handler: Contains the business logic
// message.step.ts
export const config = {
  name: 'SendMessage',
  type: 'api',
  path: '/messages',
  method: 'POST',
  emits: ['message.sent']
};

export const handler = async (req, { emit, logger }) => {
  logger.info('Sending message', { text: req.body.text });

  await emit({
    topic: 'message.sent',
    data: { text: req.body.text, timestamp: Date.now() }
  });

  return { status: 200, body: { ok: true } };
};

Key Features

1. Multi-Language Support

Motia supports polyglot development, allowing you to write different Steps in different languages within the same project:

  • TypeScript/JavaScript (Stable)
  • Python (Stable)
  • Ruby (Beta)
  • Go (Coming Soon)

This is particularly valuable for AI workflows where cutting-edge tools are often Python-only:

# sentiment_analysis_step.py
config = {
    "name": "AnalyzeSentiment",
    "type": "event",
    "subscribes": ["message.sent"]
}

def handler(input, context):
    # Use Python AI libraries
    from transformers import pipeline

    sentiment = pipeline("sentiment-analysis")
    result = sentiment(input["data"]["text"])

    context.logger.info(f"Sentiment: {result}")
    return result

2. Event-Driven Architecture

Steps communicate through topics, decoupling producers from consumers:

// processor.step.ts
export const config = {
  name: 'ProcessMessage',
  type: 'event',
  subscribes: ['message.sent']
};

export const handler = async (input, { state, logger }) => {
  logger.info('Processing message', input);

  // Store in persistent state
  await state.set(`message:${input.data.timestamp}`, input.data);

  // Process and emit further events
  // Built-in retry mechanisms handle failures
};

3. Built-in State Management

Persistent key-value storage spans across all Steps, eliminating the need for external databases for simple state needs:

export const handler = async (req, { state }) => {
  // Atomic operations
  const count = await state.get('message_count') || 0;
  await state.set('message_count', count + 1);

  // State is shared across all Steps
  const history = await state.get('message_history') || [];
  await state.set('message_history', [...history, req.body]);
};

4. Scheduled Jobs (Cron)

Define recurring tasks with simple configuration:

// cleanup.step.ts
export const config = {
  name: 'CleanupOldMessages',
  type: 'cron',
  schedule: '0 0 * * *' // Daily at midnight
};

export const handler = async (_, { state, logger }) => {
  logger.info('Running cleanup job');

  const cutoff = Date.now() - (7 * 24 * 60 * 60 * 1000); // 7 days
  // Cleanup logic here
};

5. Real-Time Streaming

Stream data to connected clients without complex infrastructure:

export const handler = async (req, { streams }) => {
  // Define stream structure
  const stream = streams.create({
    id: req.params.chatId,
    schema: { messages: [] }
  });

  // Any changes stream to all subscribed clients automatically
  stream.update({ messages: [...stream.data.messages, newMessage] });
};

6. Built-in Observability

The Motia Workbench provides:

  • Real-time logs with automatic trace IDs
  • Request flow visualization
  • State inspection
  • Dependency diagrams between Steps
  • Performance metrics

Essential Backend Features

Error Handling

Motia provides robust error handling with built-in retry mechanisms for event-based Steps:

// error-handling.step.ts
export const config = {
  name: 'ProcessPayment',
  type: 'event',
  subscribes: ['payment.requested'],
  retry: {
    maxAttempts: 3,
    backoff: 'exponential',
    timeout: 30000
  }
};

export const handler = async (input, { logger, emit }) => {
  try {
    // Process payment
    const result = await processPayment(input.data);

    await emit({
      topic: 'payment.succeeded',
      data: result
    });

    return { success: true };
  } catch (error) {
    logger.error('Payment processing failed', {
      error: error.message,
      input
    });

    // Motia automatically retries based on config
    // If max attempts reached, emit failure event
    if (error.retryCount >= 3) {
      await emit({
        topic: 'payment.failed',
        data: { error: error.message, input }
      });
    }

    throw error; // Trigger retry
  }
};

Input Validation

Use Zod or other validation libraries for type-safe APIs:

// validated-api.step.ts
import { z } from 'zod';

const CreateUserSchema = z.object({
  email: z.string().email(),
  name: z.string().min(2).max(100),
  age: z.number().int().min(18)
});

export const config = {
  name: 'CreateUser',
  type: 'api',
  path: '/users',
  method: 'POST'
};

export const handler = async (req, { logger }) => {
  try {
    // Validate input
    const data = CreateUserSchema.parse(req.body);

    // Process valid data
    logger.info('Creating user', data);

    return {
      status: 201,
      body: { id: generateId(), ...data }
    };
  } catch (error) {
    if (error instanceof z.ZodError) {
      return {
        status: 400,
        body: {
          error: 'Validation failed',
          details: error.errors
        }
      };
    }

    throw error;
  }
};

Internationalization (i18n)

Implement i18n using popular libraries like i18next:

// i18n-config.ts
import i18next from 'i18next';

i18next.init({
  lng: 'en',
  resources: {
    en: {
      translation: {
        'welcome': 'Welcome',
        'error.notFound': 'Resource not found'
      }
    },
    vi: {
      translation: {
        'welcome': 'Chào mừng',
        'error.notFound': 'Không tìm thấy tài nguyên'
      }
    },
    ja: {
      translation: {
        'welcome': 'ようこそ',
        'error.notFound': 'リソースが見つかりません'
      }
    }
  }
});

export default i18next;
// localized-api.step.ts
import i18n from './i18n-config';

export const config = {
  name: 'GetLocalizedContent',
  type: 'api',
  path: '/content',
  method: 'GET'
};

export const handler = async (req) => {
  const lang = req.headers['accept-language']?.split(',')[0] || 'en';

  return {
    status: 200,
    body: {
      message: i18n.t('welcome', { lng: lang }),
      lang
    }
  };
};

Middleware & Authentication

Configure custom middleware in motia.config.ts:

// motia.config.ts
import { defineConfig } from 'motia';
import jwt from 'jsonwebtoken';

export default defineConfig({
  middleware: {
    pre: [
      // CORS middleware
      (req, res, next) => {
        res.setHeader('Access-Control-Allow-Origin', '*');
        res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
        next();
      },
      // Authentication middleware
      (req, res, next) => {
        const publicPaths = ['/auth/login', '/auth/register'];
        if (publicPaths.includes(req.path)) {
          return next();
        }

        const token = req.headers.authorization?.replace('Bearer ', '');
        if (!token) {
          return res.status(401).json({ error: 'Unauthorized' });
        }

        try {
          req.user = jwt.verify(token, process.env.JWT_SECRET);
          next();
        } catch (error) {
          res.status(401).json({ error: 'Invalid token' });
        }
      }
    ]
  }
});

Environment Configuration

Manage environment-specific settings:

// config.step.ts
export const config = {
  name: 'GetConfig',
  type: 'api',
  path: '/config',
  method: 'GET'
};

export const handler = async () => {
  return {
    status: 200,
    body: {
      environment: process.env.NODE_ENV,
      apiUrl: process.env.API_URL,
      version: process.env.APP_VERSION
    }
  };
};

Database Integration

Integrate with any database (PostgreSQL, MongoDB, etc.):

// db-integration.step.ts
import { Pool } from 'pg';

const pool = new Pool({
  connectionString: process.env.DATABASE_URL
});

export const config = {
  name: 'GetUsers',
  type: 'api',
  path: '/users',
  method: 'GET'
};

export const handler = async (req, { logger }) => {
  try {
    const result = await pool.query('SELECT * FROM users LIMIT 10');

    return {
      status: 200,
      body: result.rows
    };
  } catch (error) {
    logger.error('Database query failed', error);
    return {
      status: 500,
      body: { error: 'Internal server error' }
    };
  }
};

Rate Limiting

Implement rate limiting for API protection:

// rate-limiter.step.ts
export const config = {
  name: 'RateLimitedEndpoint',
  type: 'api',
  path: '/api/limited',
  method: 'GET'
};

const rateLimits = new Map();

export const handler = async (req, { state }) => {
  const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
  const key = `ratelimit:${ip}`;

  const requests = await state.get(key) || [];
  const now = Date.now();
  const windowMs = 60000; // 1 minute

  // Filter requests within window
  const recentRequests = requests.filter(t => now - t < windowMs);

  if (recentRequests.length >= 100) {
    return {
      status: 429,
      body: { error: 'Too many requests' }
    };
  }

  await state.set(key, [...recentRequests, now]);

  return { status: 200, body: { ok: true } };
};

Getting Started

Installation

npx motia@latest create my-backend
cd my-backend
npm run dev

This launches the Motia Workbench on http://localhost:3000 with zero configuration.

Project Structure

my-backend/
├── src/
│   ├── api/
│   │   ├── users.step.ts
│   │   └── auth.step.ts
│   ├── events/
│   │   ├── email-notification.step.ts
│   │   └── analytics.step.ts
│   ├── cron/
│   │   └── cleanup.step.ts
│   ├── ai/
│   │   └── sentiment_analysis_step.py
│   └── config/
│       ├── i18n.ts
│       └── database.ts
├── motia.config.ts
├── package.json
└── .env

File Naming Convention

Motia auto-discovers files following these patterns:

  • .step.ts / .step.js (TypeScript/JavaScript)
  • _step.py (Python)
  • .step.rb (Ruby)

Deployment Options

npm run deploy

Features:

  • Atomic blue/green deployments
  • One-click rollbacks
  • Automatic scaling
  • Built-in monitoring

2. Self-Hosted

docker run -p 3000:3000 \
  -e DATABASE_URL=$DATABASE_URL \
  -v ./src:/app/src \
  motia/motia-runtime

3. Platform Partners

Deploy to Railway, Fly.io, or any Node.js hosting with:

npm run build
npm start

Real-World Use Cases

AI Agent Workflows

// ai-agent.step.ts
export const config = {
  name: 'AIContentModerator',
  type: 'event',
  subscribes: ['content.submitted']
};

export const handler = async (input, { emit, logger }) => {
  // Call AI model (OpenAI, Anthropic, etc.)
  const moderation = await checkContent(input.data.text);

  if (moderation.flagged) {
    await emit({
      topic: 'content.flagged',
      data: { ...input.data, reason: moderation.reason }
    });
  } else {
    await emit({
      topic: 'content.approved',
      data: input.data
    });
  }

  logger.info('Content moderated', { flagged: moderation.flagged });
};

Multi-Step Workflows

// Order processing workflow
// 1. Create order (API)
// 2. Process payment (Event)
// 3. Update inventory (Event)
// 4. Send confirmation email (Event)
// 5. Schedule delivery (Event)

// Each step emits events for the next, creating durable workflows

Comparison with Other Frameworks

FeatureMotiaExpress + BullMQ + Temporal
Setup Complexity⭐ Simple⭐⭐⭐⭐ Complex
Multi-Language✅ Built-in❌ Separate services
State Management✅ Built-in❌ External DB required
Observability✅ Built-in Workbench❌ Setup required
Event Streaming✅ Native❌ Additional infrastructure
Learning Curve⭐ Easy⭐⭐⭐⭐ Steep

Best Practices

1. Organize by Feature

src/
├── users/
│   ├── create-user.step.ts
│   ├── get-user.step.ts
│   └── update-user.step.ts
├── orders/
│   ├── create-order.step.ts
│   └── process-payment.step.ts

2. Use Environment Variables

Never hardcode sensitive data. Use .env files:

DATABASE_URL=postgresql://...
JWT_SECRET=your-secret-key
API_KEY=your-api-key

3. Leverage Type Safety

Use TypeScript and validation libraries for robust APIs.

4. Monitor with Workbench

Use the built-in Workbench during development to trace requests and debug issues.

5. Test Incrementally

Test each Step independently before integrating into workflows.

Conclusion

Motia represents a paradigm shift in backend development, offering the same simplicity that React brought to frontend development. By unifying APIs, background jobs, workflows, and AI agents around a single primitive, Motia eliminates the complexity of managing multiple frameworks while providing production-ready features like observability, state management, and multi-language support.

Whether you're building a simple REST API or a complex AI-powered workflow system, Motia provides the tools and developer experience to ship faster without sacrificing scalability or maintainability.

Get Started Today

npx motia@latest create

Resources


Have you tried Motia? Share your experience in the comments below!

References