Everything you need.Only when you need it.
Most Node frameworks give you routing and leave the rest up to you. Moro comes with auth, validation, WebSockets, gRPC, and intelligent routing already built in, so you use what your app needs and nothing else. The same code runs on Node, Edge, Lambda, and Workers.
One framework.Everything included.
Routing, validation, auth, real-time, multi-runtime. The pieces you'd normally wire up yourself are already in the framework.
Schema-first routing in 6 lines
Validate, type, document, and handle a route in one chain. No middleware files, no DTOs, no decorators.
- Type inference
req.bodyandreq.queryare fully typed from your schema. - Auto-validation400 with a structured error before your handler ever runs.
- Universal validatorsSwap Zod for Joi, Yup, or Class Validator without rewriting.
import { createApp, z } from '@morojs/moro'
const app = await createApp()
app.post('/users')
.body(z.object({
name: z.string().min(1),
email: z.string().email(),
age: z.number().int().min(18),
}))
.handler((req) => {
// req.body is fully typed and already validated
return { id: crypto.randomUUID(), ...req.body }
})
app.listen(3000)Middleware thatorders itself.
Add cors, auth, rate-limit, and validation in whatever order you like. MoroJS works out the dependencies and runs them in the right order, so a refactor can't quietly break your pipeline.
Same code.Every runtime.
Your business logic doesn't care where it runs. Move from Node to Edge to Lambda by swapping the entry file. The routes never change.
// app.ts — your application code, identical in every runtime
import { createApp, z } from '@morojs/moro'
export const app = await createApp()
app.get('/users/:id')
.params(z.object({ id: z.string().uuid() }))
.handler((req) => {
return { id: req.params.id, name: `User ${req.params.id}` }
})
app.post('/users')
.body(z.object({ name: z.string(), email: z.string().email() }))
.handler((req) => ({ id: crypto.randomUUID(), ...req.body }))// server.ts
import { app } from './app'
app.listen(3000, () => {
console.log('Ready on :3000 — node runtime')
})Long-running server, full uWebSockets.js perf available.
Versioned modules.Built-in scheduler.
Drop in a folder and get a versioned API surface. Cron jobs, mail, and worker threads all run from the same app instance, with no extra services to stand up.
// modules/users/index.ts — versioned, isolated, testable
import { defineModule } from '@morojs/moro'
import * as actions from './actions'
import * as schemas from './schemas'
export const usersModule = defineModule({
name: 'users',
version: '1.0.0',
routes: [
{
method: 'POST',
path: '/users',
handler: actions.createUser,
validation: { body: schemas.CreateUserSchema },
rateLimit: { requests: 10, window: 60_000 },
description: 'Create a new user',
},
],
})
// Mounted at /api/v1.0.0/users — versioning is automatic.// background work, in the same app
import { app, createApp } from '@morojs/moro'
// Cron expression
app.job('cleanup', '0 2 * * *', async () => {
await db.sessions.deleteExpired()
})
// Macro
app.job('daily-report', '@daily', async (ctx) => {
await mail.send({ subject: 'Daily report', html: report() })
})
// Interval string — '5m', '1h', '30s'
app.job('health-check', '5m', async (ctx) => {
ctx.logger.debug('alive', ctx.executionId)
})Auto-mounted at /api/v{version}/{module}.
Cron expressions, @daily macros, "5m" strings.
SES, SendGrid, Resend, Nodemailer — same API.
PostgreSQL, MySQL, MongoDB, SQLite, Redis, Drizzle.
Everything you need.Built in. Type-safe.
22 first-class features, all built in. No plugin ecosystem to learn, no "what do I use for X" rabbit holes. It's already in the box.
No install.Just run it.
A real MoroJS endpoint, executing right here. Change the input, hit run, see the response.
import { createApp, z } from '@morojs/moro'
const app = await createApp()
app.get('/hello')
.query(z.object({
name: z.string().optional().default('World'),
}))
.handler((req) => ({
message: `Hello, ${req.query.name}!`,
timestamp: new Date().toISOString(),
}))
app.listen(3000)Build it inthe next 60 seconds.
One command. A typed, validated, production-ready API. No framework decisions left to make.
MIT licensed · No telemetry · Open governance