Skip to content

Controllers

Controllers are functions that handle HTTP questions and send responses. This guide covers everything you need to know about building controllers in StratusTS.

A controller is a function that:

  1. Receives an HTTP question
  2. Processes the data (business logic)
  3. Sends back an HTTP response

Think of controllers as the bridge between your routes and your application logic.

Every controller follows this pattern:

app/controllers.ts
import { type ControllerType, ok } from 'stratus-ts';
const myController: ControllerType = (question, reply) => {
// 1. Get data from question
// 2. Process/validate data
// 3. Send response
reply.status(ok()).json({
success: true,
message: 'Hello World!',
});
};
export { myController };

ControllerType - TypeScript type for type safety

question - The incoming HTTP question object containing:

  • question.body() - POST/PUT data
  • question.headers() - HTTP headers
  • question.method() - HTTP method (GET, POST, etc.)

reply - The response object for sending data back:

  • reply.status() - Set HTTP status code
  • reply.json() - Send JSON response
  • reply.send() - Send plain text/HTML
  • reply.header() - Set response headers

Open src/users/controllers.ts:

src/users/controllers.ts
import { type ControllerType, ok } from 'stratus-ts';
const users = [
{ id: 1, name: 'Alice', email: 'alice@example.com' },
{ id: 2, name: 'Bob', email: 'bob@example.com' },
];
const listUsers: ControllerType = (question, reply) => {
reply.status(ok()).json({
success: true,
data: users,
});
};
export { listUsers };

Open src/users/routes.ts:

src/users/routes.ts
import { Router } from 'stratus-ts';
import { listUsers } from './controllers';
const { routes, route } = Router();
route.get('/', listUsers);
export default routes;

Visit http://localhost:2000/users to see your data!

For POST/PUT requests, data comes in question.body:

src/app/controllers.ts
const createUser: ControllerType = (question, reply) => {
const { body: {
name, email, password
} } = await question.body();
// Validate data
if (!name || !email || !password) {
return reply.status(400).json({
success: false,
error: 'Missing required fields',
});
}
// Create user (mock example)
const newUser = {
id: Date.now(),
name,
email,
};
reply.status(201).json({
success: true,
data: newUser,
});
};

Test with curl:

terminal
curl -X POST http://localhost:2000/users/create -H "Content-Type: application/json" -d '{"name":"Charlie","email":"charlie@example.com","password":"secret"}'

Access dynamic URL segments:

terminal
// Route: route.get('/users/<id:str>', getUser)
const getUser: ControllerType = (question, reply) => {
const { id } = question.params(); // Get <id:str> from URL
// Mock data
const users = [
{ id: '1', name: 'Alice', email: 'alice@example.com' },
{ id: '2', name: 'Bob', email: 'bob@example.com' },
];
const user = users.find(u => u.id === id);
if (!user) {
return reply.status(404).json({
success: false,
error:{
message:'User not found',
name:'not found'
}
});
}
reply.status(200).json({
success: true,
data: user,
});
};

Test: http://localhost:2000/users/1

Access query strings (?page=1&limit=10):

Access HTTP headers:

app/controllers.ts
const protectedController: ControllerType = (question, reply) => {
const authHeader = question.get('authorization'); // question.header('authorization')
const contentType = question.get('content-type');
if (!authHeader) {
throw new Error('Authorization header required')
}
// Extract token
const token = authHeader.replace('Bearer ', '');
// Verify token (mock)
if (token !== 'valid-token') {
throw new Error('Invalid token')
}
reply.status(200).json({
success: true,
data:{}
});
};

Test with curl:

terminal
curl http://localhost:2000/users/protected -H "Authorization: Bearer valid-token"

StratusTS provides helpers for common status codes:

app/controllers.ts
import {
ok, // 200
created, // 201
badRequest, // 400
unauthorized, // 401
forbidden, // 403
notFound, // 404
serverError, // 500
} from 'stratus-ts';

200 OK - Success

app/controllers.ts
reply.status(ok()).json({
success: true,
data: users,
});

201 Created - Resource Created

app/controllers.ts
reply.status(created()).json({
success: true,
data: newUser,
});

400 Bad Request - Invalid Input

app/controllers.ts
reply.status(badRequest()).json({
success: false,
error: {
message: 'Invalid email format',
name: 'invalid'
}
});

401 Unauthorized - Authentication Required

app/controllers.ts
reply.status(unauthorized()).json({
success: false,
error: {
message: 'Please log in to continue',
name: 'invalid'
}
});

403 Forbidden - No Permission

app/controllers.ts
reply.status(forbidden()).json({
success: false,
error: {
message:'You do not have permission to perform this action',
name: 'invalid'
}
});

404 Not Found - Resource Missing

app/controllers.ts
reply.status(notFound()).json({
success: false,
error: {
message: 'User not found',
name: 'not found',
}
});

500 Internal Server Error - Something Broke

app/controllers.ts
reply.status(serverError()).json({
success: false,
error: {
message: 'An unexpected error occurred',
name: 'server error',
}
});

The most common response format:

app/controllers.ts
const controller: ControllerType = (question, reply) => {
reply.status(ok()).json({
success: true,
data: {
id: 1,
name: 'Alice',
email: 'alice@example.com',
}
});
};

Now that you understand controllers: