Request & Response

Learn how to handle HTTP requests and responses in MoroJS with type safety, automatic parsing, and flexible response formatting.

Request Handling

Request Context

typescript

1app.get('/example', ({ request, headers, params, query, body, context }) => {
2  // request: Full Request object
3  console.log(request.method); // GET
4  console.log(request.url);    // /example?foo=bar
5  
6  // headers: Typed headers object
7  console.log(headers['content-type']);
8  console.log(headers.authorization);
9  
10  // params: Route parameters (typed)
11  console.log(params.id); // From /users/:id
12  
13  // query: Query string parameters
14  console.log(query.search); // From ?search=value
15  
16  // body: Parsed request body (validated if schema provided)
17  console.log(body.name);
18  
19  // context: Middleware-populated context
20  console.log(context.user); // From auth middleware
21  
22  return { success: true };
23});

Request Body Parsing

typescript

1// JSON body parsing (automatic)
2app.post('/users', {
3  body: z.object({
4    name: z.string(),
5    email: z.string().email()
6  }),
7  handler: ({ body }) => {
8    // body is automatically parsed and validated
9    return { user: body };
10  }
11});
12
13// Form data parsing
14app.post('/upload', {
15  body: z.object({
16    file: z.instanceof(File),
17    description: z.string().optional()
18  }),
19  handler: ({ body }) => {
20    // Handle file upload
21    return { filename: body.file.name };
22  }
23});
24
25// Raw body access
26app.post('/webhook', ({ request }) => {
27  const rawBody = await request.text();
28  const signature = request.headers.get('x-signature');
29  
30  // Verify webhook signature
31  if (!verifySignature(rawBody, signature)) {
32    throw new Error('Invalid signature');
33  }
34  
35  return { received: true };
36});

Response Handling

Response Formats

typescript

1// Simple JSON response
2app.get('/users', () => {
3  return { users: [] }; // Automatically serialized to JSON
4});
5
6// Custom status code
7app.post('/users', ({ body }) => {
8  const user = createUser(body);
9  return {
10    status: 201,
11    body: { user }
12  };
13});
14
15// Custom headers
16app.get('/download/:file', ({ params }) => {
17  const file = getFile(params.file);
18  return {
19    status: 200,
20    headers: {
21      'Content-Type': 'application/octet-stream',
22      'Content-Disposition': `attachment; filename="${file.name}"`
23    },
24    body: file.content
25  };
26});
27
28// Redirect response
29app.get('/old-endpoint', () => {
30  return {
31    status: 301,
32    headers: {
33      'Location': '/new-endpoint'
34    }
35  };
36});

Streaming Responses

typescript

1// Server-sent events
2app.get('/events', () => {
3  return new Response(
4    new ReadableStream({
5      start(controller) {
6        const interval = setInterval(() => {
7          const data = `data: ${JSON.stringify({ timestamp: Date.now() })}\n\n`;
8          controller.enqueue(new TextEncoder().encode(data));
9        }, 1000);
10        
11        // Cleanup after 30 seconds
12        setTimeout(() => {
13          clearInterval(interval);
14          controller.close();
15        }, 30000);
16      }
17    }),
18    {
19      headers: {
20        'Content-Type': 'text/event-stream',
21        'Cache-Control': 'no-cache',
22        'Connection': 'keep-alive'
23      }
24    }
25  );
26});
27
28// File streaming
29app.get('/stream/:file', ({ params }) => {
30  const fileStream = createReadStream(params.file);
31  return new Response(fileStream, {
32    headers: {
33      'Content-Type': 'application/octet-stream'
34    }
35  });
36});

Error Response Patterns

Standard Error Responses

typescript

1app.get('/users/:id', {
2  params: z.object({ id: z.string().uuid() }),
3  handler: async ({ params }) => {
4    const user = await getUserById(params.id);
5    
6    if (!user) {
7      return {
8        status: 404,
9        body: {
10          error: 'USER_NOT_FOUND',
11          message: 'User not found',
12          code: 'E001'
13        }
14      };
15    }
16    
17    return { user };
18  }
19});
20
21// Global error handling
22app.use(async (context, next) => {
23  try {
24    await next();
25  } catch (error) {
26    if (error.name === 'ValidationError') {
27      return {
28        status: 400,
29        body: {
30          error: 'VALIDATION_ERROR',
31          message: error.message,
32          details: error.errors
33        }
34      };
35    }
36    
37    if (error.name === 'UnauthorizedError') {
38      return {
39        status: 401,
40        body: {
41          error: 'UNAUTHORIZED',
42          message: 'Authentication required'
43        }
44      };
45    }
46    
47    // Unknown error
48    return {
49      status: 500,
50      body: {
51        error: 'INTERNAL_SERVER_ERROR',
52        message: 'Something went wrong'
53      }
54    };
55  }
56});

Next Steps