Your First API

Let's build a complete API from scratch. This tutorial will guide you through creating a production-ready API with validation, error handling, and best practices.

Step 1: Create the Basic Server

Start by creating a simple server with a health check endpoint:

src/server.ts

typescript

1import { createApp } from '@morojs/moro';
2
3const app = createApp();
4
5// Health check endpoint
6app.get('/health', () => {
7  return {
8    status: 'healthy',
9    timestamp: new Date().toISOString(),
10    uptime: process.uptime()
11  };
12});
13
14// Start the server
15const port = process.env.PORT || 3000;
16app.listen(port);
17
18console.log(`🚀 API server running on http://localhost:${port}`);

Test it out!

Run npm run dev and visithttp://localhost:3000/health

Step 2: Add Data Validation

Let's add a user management endpoint with proper validation:

src/server.ts

typescript

1import { createApp } from '@morojs/moro';
2import { z } from 'zod';
3
4const app = createApp();
5
6// In-memory storage for demo
7const users: Array<{ id: string; name: string; email: string; createdAt: string }> = [];
8
9// Validation schemas
10const CreateUserSchema = z.object({
11  name: z.string().min(1, "Name is required").max(100, "Name too long"),
12  email: z.string().email("Invalid email format")
13});
14
15const UserParamsSchema = z.object({
16  id: z.string().uuid("Invalid user ID")
17});
18
19// Health check
20app.get('/health', () => ({
21  status: 'healthy',
22  timestamp: new Date().toISOString(),
23  users: users.length
24}));
25
26// Create user
27app.post('/users', {
28  body: CreateUserSchema,
29  handler: ({ body }) => {
30    const user = {
31      id: crypto.randomUUID(),
32      ...body,
33      createdAt: new Date().toISOString()
34    };
35    
36    users.push(user);
37    
38    return {
39      message: 'User created successfully',
40      user
41    };
42  }
43});
44
45// Get all users
46app.get('/users', () => ({
47  users,
48  total: users.length
49}));
50
51// Get user by ID
52app.get('/users/:id', {
53  params: UserParamsSchema,
54  handler: ({ params }) => {
55    const user = users.find(u => u.id === params.id);
56    
57    if (!user) {
58      return {
59        status: 404,
60        body: {
61          error: 'USER_NOT_FOUND',
62          message: 'User not found'
63        }
64      };
65    }
66    
67    return { user };
68  }
69});
70
71app.listen(3000);

Step 3: Add Middleware

Add logging and error handling middleware:

src/server.ts - With Middleware

typescript

1// Add after imports
2app.use(async ({ request }, next) => {
3  console.log(`${new Date().toISOString()} ${request.method} ${request.url}`);
4  await next();
5});
6
7// Global error handler
8app.use(async (context, next) => {
9  try {
10    await next();
11  } catch (error) {
12    console.error('Request error:', error);
13    
14    return {
15      status: 500,
16      body: {
17        error: 'INTERNAL_SERVER_ERROR',
18        message: 'Something went wrong'
19      }
20    };
21  }
22});

Step 4: Test Your API

Test your API endpoints with these example requests:

Create a User

POST /users

typescript

1curl -X POST http://localhost:3000/users \
2  -H "Content-Type: application/json" \
3  -d '{
4    "name": "John Doe",
5    "email": "john@example.com"
6  }'

Get All Users

GET /users

typescript

1curl http://localhost:3000/users

What You've Learned

Core Concepts

  • Creating a MoroJS application
  • Defining routes with handlers
  • Request and response handling
  • Server startup and configuration

Advanced Features

  • Schema-based validation with Zod
  • Type-safe parameter handling
  • Global middleware setup
  • Error handling patterns

Next Steps