Skip to content

Creating Apps

Apps are the building blocks of StratusTS applications. This guide explains what apps are, how to create them, and best practices for organizing your code.

An app in StratusTS is a self-contained module that handles a specific domain or feature of your application. Think of apps as reusable packages of functionality.

Good app examples:

  • users - User authentication and management
  • blog - Blog posts and comments
  • api - External API endpoints
  • admin - Admin panel functionality
  • payments - Payment processing
  • notifications - Email and push notifications

Each app focuses on one responsibility, making your code easier to understand, test, and maintain.

Apps keep related code together:

  • Directoryusers/
    • controllers.ts # User-related request handlers
    • routes.ts # User-related URLs
    • models.ts # User database models
    • services.ts # Services related to User models

Everything about users lives in one place.

Apps can be moved between projects:

terminal
# Copy entire app to another project
cp -r users/ ../other-project/src/users/

Share common functionality across projects without code duplication.

Add features without touching existing code:

src/settings.ts
// Before
const APPS = ['users', 'blog'];
// After - just add new app
const APPS = ['users', 'blog', 'payments', 'notifications'];

Each app is independent—adding one doesn’t break others.

Different developers can work on different apps without conflicts:

  • Alice works on users app
  • Bob works on blog app
  • Charlie works on api app

No stepping on each other’s toes!

Test apps independently:

tests/controllers.ts
import { test } from 'node:test';
import { listUsers } from '../controllers';
// Test user functionality in isolation
test('listUsers returns all users', () => {
// ...
});

The easiest way to create an app is with the CLI:

terminal
st create app users

or the full command:

terminal
stratus-ts create app users

This creates a new directory with the standard structure.

Run this command from your project root directory (where src/ folder exists):

terminal
my-first-app/
├── src/ # You should be here
└── settings.ts
└── package.json
# Run from here:
$ pwd
/path/to/my-first-app
$ st create app users

The CLI generates this structure:

  • Directorysrc/
    • Directoryusers/
      • controllers.ts # Request handlers
      • routes.ts # URL routing
      • models.ts # Database models (optional)
      • services.ts # If models.ts is presend then required

controllers.ts - Empty file ready for your controllers:

app/controllers.ts
import { type ControllerType } from 'stratus-ts';
// Add your controllers here

routes.ts - Basic router setup:

app/routes.ts
import { Router } from 'stratus-ts';
const { routes, route } = Router();
// Define your routes here
// Example: route.get('/', yourController);
export default routes;

models.ts - Empty file for database models:

app/models.ts
// Define your database models here

Creating an app isn’t enough—you must register it in settings.

Edit src/settings.ts:

src/settings.ts
import type { DatabaseType, SettingsType } from 'stratus-ts';
const PORT = 2000;
const APPS = []; // ← Currently empty
const DATABASE: DatabaseType = false;
const TEMPLATE_DIR = 'templates';
export default {
PORT,
APPS,
DATABASE,
TEMPLATE_DIR,
} satisfies SettingsType;

Add the app name to the APPS array:

src/settings.ts
const APPS = ['users']; // ← Added!

Full example:

src/settings.ts
import type { DatabaseType, SettingsType } from 'stratus-ts';
const PORT = 2000;
const APPS = ['users']; // Your app is now registered
const DATABASE: DatabaseType = false;
const TEMPLATE_DIR = 'templates';
export default {
PORT,
APPS,
DATABASE,
TEMPLATE_DIR,
} satisfies SettingsType;

If your server is running and doesn’t load new app, restart it to load the new app:

terminal
# Stop the server (Ctrl+C)
# Start it again
npm run dev

Your app’s routes are now active!

StratusTs automatically registers routes from all apps in the APPS array. Here’s how:

When your server starts, StratusTS:

  1. Reads the APPS array from settings
  2. Looks for each app directory in src/
  3. Imports routes.ts from each app
  4. Registers all routes automatically

Combine all the routes in one place:

// App name: users
// Route defined as: route.get('/', controller)
// Actual URL: /
// App name: blog
// Route defined as: route.get('/posts', controller)
// Actual URL: /posts
src/apps/users/routes.ts & src/apps/blog/routes.ts
// users/routes.ts
import { route } from 'stratus-ts';
route.get('/', listUsers); // → /
route.get('/<id:str>', getUser); // → /:id
route.post('/create', createUser); // → /create
// blog/routes.ts
route.get('/blog', listPosts); // → /blog/
route.get('/blog/<slug:str>', getPost); // → /blog/:slug

You can create as many apps as needed:

terminal
st create app users
st create app blog
st create app api
st create app admin

Register all of them in settings:

src/settings.ts
const APPS = [
'users',
'blog',
'api',
'admin',
];

Lowercase, no special characters:

terminal
st create app users
st create app blog
st create app api

Use hyphens for multi-word apps:

terminal
st create app user-management
st create app blog-posts
st create app payment-gateway

Uppercase letters:

terminal
st create app Users # Don't do this
st create app BlogPosts # Don't do this

Spaces or special characters:

terminal
st create app "user management" # Don't do this
st create app blog_posts # Use hyphens instead

Each app should have one clear purpose.

Good:

src/settings.ts
const APPS = [
'users', // User management only
'blog', // Blog functionality only
'payments', // Payment processing only
];

Bad:

src/settings.ts
const APPS = [
'everything', // Users, blog, payments all mixed together
];

Apps should work without depending on each other’s internals.

Good:

users/controllers.ts
import { User } from './models'; // Import from own app
// blog/controllers.ts
import { Post } from './models'; // Import from own app

Acceptable:

blog/controllers.ts
// If you need user data, import the public API
import { getUserById } from '../users/services';

Bad:

blog/controllers.ts
import { User } from '../users/models'; // Tight coupling

App names should instantly convey their purpose.

Good:

  • users - Obvious
  • blog - Clear
  • notifications - Self-explanatory

Less Good:

  • stuff - Too vague
  • misc - What does it do?
  • utils - Utility functions don’t need an app

If features are closely related, keep them in one app.

Good:

  • Directoryblog/
    • Directorycontrollers.ts
      • listPosts()
      • createPost()
      • listComments()
      • createComment()
    • Directorymodels.ts
      • Post
      • Comment

Posts and comments are related—they belong together.

Debatable:

  • Directoryblog-posts/ # Separate app for posts…
  • Directoryblog-comments/ # …and comments

Only separate if they’re truly independent features.

Start simple. One app is fine for small projects.

For small projects:

src/settings.ts
const APPS = ['api']; // Everything in one app is OK

As you grow:

src/settings.ts
const APPS = ['users', 'blog', 'api'];

Split apps when complexity demands it, not before.

Organize by feature/domain:

src/settings.ts
const APPS = [
'users', // User authentication
'blog', // Blog posts
'shop', // E-commerce
'admin', // Admin panel
];

When to use: Most applications benefit from this approach.

Separate apps for API versions:

src/settings.ts
const APPS = [
'api-v1', // Version 1 endpoints
'api-v2', // Version 2 endpoints
];

When to use: When maintaining multiple API versions.

Different apps for different clients:

src/settings.ts
const APPS = [
'web', // Web application routes
'mobile-api', // Mobile app API
'admin', // Admin panel
];

When to use: When different clients need different functionality.

Let’s walk through creating a complete blog app.

terminal
st create app blog
src/settings.ts
const APPS = ['blog'];
src/blog/controllers.ts
import { type ControllerType, ok, created, notFound } from 'stratus-ts';
const posts = [
{ id: 1, title: 'First Post', author: 'Alice' },
{ id: 2, title: 'Second Post', author: 'Bob' },
];
// List all posts
export const listPosts: ControllerType = (question, reply) => {
reply.status(ok()).json({ success: true, data: posts });
};
// Get single post
export const getPost: ControllerType = (question, reply) => {
const { id } = question.params;
const post = posts.find(p => p.id === parseInt(id));
if (!post) {
return reply.status(notFound()).json({
success: false,
error: 'Post not found',
});
}
reply.status(ok()).json({ success: true, data: post });
};
// Create post
export const createPost: ControllerType = (question, reply) => {
const { title, content, author } = question.body;
const newPost = {
id: Date.now(),
title,
content,
author,
createdAt: new Date(),
};
posts.push(newPost);
reply.status(created()).json({ success: true, data: newPost });
};
src/blog/routes.ts
import { Router } from 'stratus-ts';
import { listPosts, getPost, createPost } from './controllers';
const { routes, route } = Router();
route.get('/', listPosts);
route.get('/<id:int>', getPost);
route.post('/create', createPost);
export default routes;
terminal
# Start server
npm run dev
# Test endpoints
curl http://localhost:2000/
curl http://localhost:2000/1
curl -X POST http://localhost:2000/create \
-H 'Content-Type: application/json' \
-d '{"title":"New Post","content":"Content","author":"Charlie"}'

Your blog app is complete!

Problem: Routes return 404.

Solution: Check that:

  1. App is added to APPS array in settings
  2. App name matches directory name exactly
  3. routes.ts exports default
  4. Server was restarted after adding app

Problem: One route overrides another.

Solution: Check app order in APPS array. The first app wins conflicts.

Problem: Cannot import from app.

Solution: Use correct relative paths:

users/{file name}.ts
import { controller } from './controllers'; // Same directory
import { User } from './models'; // Same directory

Now that you understand apps, explore: