Getting Started

Quick Start

One command scaffolds a typed, validated MoroJS API. You'll have a running server in under a minute.

What You'll Learn

  • Scaffold a MoroJS project with the CLI
  • Run the dev server and hit your first endpoints
  • Add a typed, validated route

Scaffold Your Project

Run the CLI. You'll be prompted for runtime, validation library, and optional features. Press enter to accept the defaults for a typed REST API.

Scaffold
1npx @morojs/cli init my-api

What this does: Creates my-api/ with TypeScript, ESM, validation, and a working src/index.ts wired up. The CLI auto-detects your package manager and installs dependencies.

Want a full project, not just a basic API? The Interactive Project Builder lets you pick a database, auth, runtime, validation, and features, then copies the exact init command to scaffold it all.

Open the builder

Run It

Start the dev server with hot reload:

Start dev server
1cd my-api
2npm run dev

You'll see the server come up on port 3000. Hit the welcome and health endpoints already scaffolded for you:

  • GET http://localhost:3000/: welcome endpoint
  • GET http://localhost:3000/health: health check

Add a Typed, Validated Route

Open src/index.ts and add a parameterized GET, a validated POST and a literal health route. Save, and the dev server reloads automatically.

src/index.ts
1import { param } from '@morojs/moro';
2import { z } from 'zod';
3
4// Parameterized route — req.params.id is typed as string
5app.get('/users/:id').handler((req) => {
6  return {
7    userId: req.params.id,
8    message: `User ${req.params.id} retrieved`,
9  };
10});
11
12// POST route with body validation (chainable pattern)
13const CreateUserSchema = z.object({
14  name: z.string().min(1),
15  email: z.string().email(),
16  age: z.number().min(18).max(120),
17});
18
19app.post('/users')
20  .validate({ body: CreateUserSchema })
21  .handler((req) => {
22    // req.body is fully typed and validated
23    return {
24      message: 'User created',
25      user: {
26        id: crypto.randomUUID(),
27        ...req.body,
28        createdAt: new Date().toISOString(),
29      },
30    };
31  });
32
33// A literal body is the whole handler: on Moro's native engine it is
34// answered without entering JS at all - and so is param('id'), which
35// echoes that path parameter as the body
36app.get('/health').handler('ok');
37app.get('/user/:id').handler(param('id'));

Result: TypeScript knows the shape of req.body from the Zod schema. Invalid requests are rejected automatically before your handler runs.

Prefer to set up MoroJS by hand instead of using the CLI?

Manual installation guide

Next Steps