Core Concepts
App API
Core APIs for accessing your app instance, grouping routes under shared prefixes, and auto-loading route files from the filesystem.
What You'll Learn
- How to access the app singleton from any file
- Grouping routes under shared prefixes with nesting
- Auto-loading route files from a directory
- File conventions for route auto-loading
getApp(): App Singleton Accessor
Route files no longer need the app instance passed in. Any file can register routes directly using getApp(). This returns the singleton app instance created by createApp().
Using getApp()typescript
1import { getApp } from '@morojs/moro';
2const app = getApp();
3
4app.get('/health', (req, res) => {
5 res.json({ status: 'healthy' });
6});Why use getApp()?
- No need to thread the app instance through imports
- Route files become self-contained and portable
- Works anywhere after
createApp()has been called - Pairs naturally with
app.loadRoutes()for auto-loading
app.group(): Route Grouping
Group routes under a shared prefix. Supports the full chainable API and nesting for clean, organized route definitions.
Basic Route Groupingtypescript
1app.group('/auth', (auth) => {
2 auth.post('/login').body(LoginSchema).handler(actions.login);
3 auth.post('/logout').handler(actions.logout);
4 auth.get('/me', requireAuth, actions.me);
5});Nested Groupstypescript
1// Nested groups for API versioning
2app.group('/api', (api) => {
3 api.group('/v1', (v1) => {
4 v1.get('/users').handler(listUsers);
5 v1.get('/users/:id').handler(getUser);
6 v1.post('/users').body(CreateUserSchema).handler(createUser);
7 });
8
9 api.group('/v2', (v2) => {
10 v2.get('/users').handler(listUsersV2);
11 });
12});Group Features
- Shared prefix applied to all child routes
- Full chainable API inside the group callback
- Unlimited nesting depth
- Middleware can be applied per-group or per-route
app.loadRoutes(): Auto-Load Route Files
Automatically imports route files from a directory. Top-level files are loaded directly. Subdirectories load only index.ts — other files like actions.ts or schemas.ts are ignored by the loader and pulled in by the index.
Usagetypescript
1await app.loadRoutes('./src/routes');Directory Conventiontext
1routes/
2 health.ts -> loaded
3 auth/
4 index.ts -> loaded (uses app.group('/auth'))
5 actions.ts -> ignored (imported by index.ts)
6 schemas.ts -> ignored (imported by index.ts)
7 stats/
8 index.ts -> loaded
9 actions.ts -> ignoredExample Route Filetypescript
1// routes/health.ts
2import { getApp } from '@morojs/moro';
3const app = getApp();
4
5app.get('/health', (req, res) => {
6 res.json({ status: 'healthy', uptime: process.uptime() });
7});Example Route Directorytypescript
1// routes/auth/index.ts
2import { getApp } from '@morojs/moro';
3import * as actions from './actions';
4import { LoginSchema } from './schemas';
5
6const app = getApp();
7
8app.group('/auth', (auth) => {
9 auth.post('/login').body(LoginSchema).handler(actions.login);
10 auth.post('/logout').handler(actions.logout);
11 auth.get('/me', requireAuth, actions.me);
12});Loading Rules
- Top-level
.tsfiles are loaded directly - Subdirectories only load
index.ts - Supporting files (
actions.ts,schemas.ts) are imported by the index, not the loader - Keeps route directories self-contained and organized