Skip to content

Routing

Routes connect URLs to your controllers. This guide covers everything you need to know about routing in StratusTS.

Routing maps HTTP requests to specific controllers based on:

  • URL path: /users, /blog/posts
  • HTTP method: GET, POST, PATCH, DELETE, etc.

When someone visits your URL, routing decides which code runs.

Each app has its own routes.ts file that defines its routes.

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

What’s happening:

  1. Import Router from StratusTS
  2. Create a router with Router()
  3. Define routes using route.get(), route.post(), etc.
  4. Export routes as default

StratusTS supports all standard HTTP methods.

src/users/routes.ts
route.get('/', listUsers);
route.get('/<id:int>', getUser);

When to use: Fetching data, displaying pages, reading resources

src/users/routes.ts
route.post('/create', createUser);
route.post('/login', loginUser);

When to use: Creating new resources, submitting forms, uploading data

src/users/routes.ts
route.put('/<id:int>', updateUser);

When to use: Replacing entire resources

src/users/routes.ts
route.patch('/<id:int>', patchUser);

When to use: Updating specific fields

src/users/routes.ts
route.delete('/<id:int>', deleteUser);

When to use: Removing resources

src/users/routes.ts
route.all('/webhook', handleWebhook); // doesn't support yet trying hard

When to use: Endpoints that accept any HTTP method

src/users/routes.ts
route.get('/', controller);
src/users/routes.ts
route.get('/profile', getProfile);
route.get('/settings', getSettings);

Full URLs:

  • /profile
  • /settings
src/users/routes.ts
route.get('/posts/recent', getRecentPosts);
route.get('/admin/dashboard', getDashboard);
src/users/routes.ts
route.get('/<id:int>', getUser);

Access in controller:

src/users/routes.ts
const getUserController: ControllerType = (question, reply) => {
const { id } = question.params();
console.log(`User ID: ${id}`);
};
src/users/routes.ts
route.get('/<userId:int>/posts/<postId:int>', getPost);

Access in controller:

src/users/routes.ts
const { userId, postId } = question.params();

Query parameters appear after ? in URLs: /users?page=2&limit=10

You can customize the base URL:

src/users/routes.ts
const { routes, route } = Router('/api/v1');
route.get('/users', handler); // → /api/v1/users

Here’s a complete CRUD (Create, Read, Update, Delete) setup:

src/users/routes.ts
import { Router } from 'stratus-ts';
import {
listUsers,
getUser,
createUser,
updateUser,
deleteUser,
} from './controllers';
const { routes, route } = Router();
// List all users
route.get('/', listUsers);
// Get single user
route.get('/<id:int>', getUser);
// Create new user
route.post('/create', createUser);
// Update user
route.put('/<id:int>', updateUser);
// Delete user
route.delete('/<id:int>', deleteUser);
export default routes;
Users Route Summary
GET / listUsers
GET /:id getUser
POST /create createUser
PUT /:id updateUser
DELETE /:id deleteUser

Follow REST conventions for clean, predictable APIs.

src/users/routes.ts
// Collection routes
route.get('/', listResources); // GET /users
route.post('/', createResource); // POST /users
// Member routes
route.get('/<id:int>', getResource); // GET /users/:id
route.put('/<id:int>', updateResource); // PUT /users/:id
route.patch('/<id:int>', patchResource); // PATCH /users/:id
route.delete('/<id:int>', deleteResource); // DELETE /users/:id
src/users/routes.ts
// Posts belong to users
route.get('/<userId:int>/posts', getUserPosts);
route.post('/<userId:int>/posts', createUserPost);
route.get('/<userId:int>/posts/<postId:int>', getUserPost);

URLs become:

  • /1/posts - Get all posts by user 1
  • /1/posts/5 - Get post 5 by user 1

Group related functionality:

src/users/routes.ts
// User routes
route.get('/', listUsers);
route.get('/<id:int>', getUser);
route.post('/create', createUser);
// Authentication routes
route.post('/login', loginUser);
route.post('/logout', logoutUser);
route.post('/register', registerUser);
// Profile routes
route.get('/profile', getProfile);
route.patch('/profile', updateProfile);

Routes are matched in the order they’re defined.

users/routes.ts
// ✅ Correct order
route.get('/profile', getMyProfile); // Matches /profile
route.get('/<id:int>', getUser); // Matches /123
// ❌ Wrong order
route.get('/<id:int>', getUser); // Matches /profile too!
route.get('/profile', getMyProfile); // Never reached

Rule: Define specific routes before dynamic routes.

app/routes.ts
// Good
route.get('/search', searchUsers);
route.get('/new', newUserForm);
route.get('/<id:str>', getUser);
// Bad
route.get('/<id:str>', getUser); // Catches '/search' and '/new'
route.get('/search', searchUsers); // Never reached
route.get('/new', newUserForm); // Never reached
terminal
# GET request
curl http://localhost:2000/users
# POST request with JSON data
curl -X POST http://localhost:2000/users/create \
-H 'Content-Type: application/json' \
-d '{"name":"Alice","email":"alice@example.com"}'
# GET with parameters
curl http://localhost:2000/users/1
# DELETE request
curl -X DELETE http://localhost:2000/users/1

Visit in your browser for GET requests:

endpoints
http://localhost:2000/users
http://localhost:2000/users/1
http://localhost:2000/users?page=2
  1. Set HTTP method (GET, POST, etc.)
  2. Enter URL: http://localhost:2000/users
  3. Add body for POST/PUT requests
  4. Click Send
src/api/routes.ts
const { routes, route } = Router('/api');
route.get('/users', listUsers);
route.get('/posts', listPosts);
route.get('/comments', listComments);
// URLs: /api/users, /api/posts, /api/comments
src/{api,api-v2}/routes.ts
// api/routes.ts
const { routes, route } = Router('/api/v1');
route.get('/users', listUsers);
// api-v2/routes.ts
const { routes, route } = Router('/api/v2');
route.get('/users', listUsers);
src/admin/routes.ts
// In admin app
const { routes, route } = Router('/admin');
route.get('/dashboard', adminDashboard);
route.get('/users', adminListUsers);
route.delete('/users/<id:int>', adminDeleteUser);
// URLs: /admin/dashboard, /admin/users, etc.

Add a debug route to see all registered routes:

src/admin/routes.ts
// In admin app
const { routes, route } = Router();
route.get('/dashboard', dashboard);
route.get('/users', users);
route.delete('/users/<id:int>', user);
console.log(routes);

Add logging to understand route matching:

Follow standard REST patterns for predictable APIs:

src/users/routes.ts
// ✅ Good - RESTful
route.get('/', listUsers);
route.post('/', createUser);
route.get('/<id:int>', getUser);
route.put('/<id:int>', updateUser);
route.delete('/<id:int>', deleteUser);
// ❌ Bad - non-standard
route.get('/getAllUsers', listUsers);
route.get('/createNewUser', createUser);
src/blog/routes.ts
// ✅ Good
route.get('/posts', listPosts);
route.get('/posts/<id:int>', getPost);
// ❌ Bad
route.get('/get-all-posts-from-database', listPosts);
route.get('/retrieve-single-post-by-id/<id:int>', getPost);
src/users/routes.ts
// ✅ Good - nouns
route.get('/users', listUsers);
route.post('/users', createUser);
// ❌ Bad - verbs
route.get('/getUsers', listUsers);
route.post('/createUser', createUser);
src/routes-consistency.ts
// ✅ Good - consistent
route.get('/users', listUsers);
route.get('/posts', listPosts);
route.get('/comments', listComments);
// ❌ Bad - inconsistent
route.get('/users', listUsers);
route.get('/all-posts', listPosts);
route.get('/comment-list', listComments);

For public APIs, use versioning:

src/routes-consistency.ts
const { routes, route } = Router('/api/v1');

This allows changes without breaking existing clients.

Check:

  1. App is in APPS array in settings
  2. Route is defined in routes.ts
  3. Server was restarted after changes
  4. URL matches exactly (check spelling, case)

Check:

  1. Route order (specific routes before generic)
  2. Multiple apps don’t define same route
  3. App order in APPS array

Check:

  1. Using : prefix: /<id:int> not /id
  2. Accessing with question.params().id
  3. Dynamic routing is enabled (see Dynamic Routes)

Now that you understand routing: