Introduction

What is JustScale and why should you use it?

JustScale is a modern TypeScript framework for building scalable backend applications with compile-time type safety. Unlike traditional frameworks that rely on runtime reflection or decorators, JustScale leverages TypeScript's type system to validate dependencies, routes, and middleware at compile time.

The Core Concept

Controllers are application entry points.

Whether triggered by an HTTP request, a CLI command, or an internal event - each invocation is a fresh entry into your application with:

  • Full dependency injection (scoped per request)
  • Middleware chain execution
  • Guard validation
  • Observability (logging, tracing)

The transport is just the trigger source. Your business logic stays the same whether it's handling an HTTP POST, a CLI command, or a background job. This is what we mean by transport-agnostic.

users-controller.tsTypeScript
import { createController } from '@justscale/core';
import { Get, Post } from '@justscale/http';
import { Cli } from '@justscale/cli';
import { body } from '@justscale/http/builder';
import { UserService } from './user-service';
import { CreateUserSchema } from './schemas';

const UsersController = createController('/users', {
  inject: { users: UserService },
  routes: (services) => ({
    // HTTP: GET /users
    list: Get('/').handle(({ res }) => {
      res.json(services.users.findAll());
    }),

    // HTTP: POST /users
    create: Post('/')
      .apply(body(CreateUserSchema))
      .handle(({ body, res }) => {
        res.json(services.users.create(body));
      }),

    // CLI: users list
    listCli: Cli('list').handle(({ io }) => {
      io.table(services.users.findAll());
    }),
  }),
});

Same controller. Same services. Same type safety. Different entry points.

What JustScale Provides

  • Dependency Injection - Type-safe service management with compile-time validation. Missing dependencies are caught before your code runs.
  • Controllers - Transport-agnostic entry points with automatic type inference and route composition.
  • Middleware & Guards - Composable request processing with type-safe context accumulation.
  • Features - Modular, reusable application components that encapsulate services, controllers, and configuration.
  • Transports - HTTP, CLI, and more. Each transport is a separate package that plugs into the same core.

Key Features

Compile-Time Type Safety

All dependencies are resolved and validated at compile time. If you forget a service or have a circular dependency, TypeScript tells you before you run your code:

app.tsTypeScript
import { createClusterBuilder } from '@justscale/core';
import { UserService } from './user-service';
import { UsersController } from './users-controller';

const app = createClusterBuilder()
  .add(UserService) // Missing Database dependency!
  .add(UsersController)
  .build();
// TypeScript Error: __dependencies_missing__: Database

Middleware with Context Accumulation

Middleware in JustScale accumulates type-safe context. Each .use() call adds properties to the handler context:

users-controller.tsTypeScript
import { createController } from '@justscale/core';
import { Post } from '@justscale/http';
import { UserService } from './user-service';
import { authenticate } from './middleware/authenticate';
import { parseBody } from './middleware/parse-body';
import { isOwner } from './guards/is-owner';

const UsersController = createController('/users', {
  inject: { users: UserService },
  routes: (services) => ({
    update: Post('/:id')
      .use(authenticate)   // Adds { user: User }
      .use(parseBody)      // Adds { body: UpdateUserDto }
      .guard(isOwner)      // Can access user and params
      .handle(({ user, body, params }) => {
        services.users.update(params.id, body);
        // All properties are typed
      }),
  }),
});

Zero Runtime Magic

No decorators, no reflection, no metadata. JustScale uses plain TypeScript types and functions. What you write is what runs:

user-service.tsTypeScript
import { createService } from '@justscale/core';
import { Database } from './database';

export const UserService = createService({
  inject: { db: Database },
  factory: ({ db }) => ({
    findAll: () => db.query('SELECT * FROM users'),
    create: (data) => db.insert('users', data),
  }),
});

Feature-Based Architecture

Build reusable features that encapsulate everything needed for a capability. Features compose together and dependencies resolve automatically:

app.tsTypeScript
import { createClusterBuilder, wrapWithCluster } from '@justscale/cluster';
import { LoggingFeature } from './features/logging';
import { UsersController } from './controllers/users';
import { UserService } from './services/user';

const built = createClusterBuilder()
  .add(LoggingFeature)
  .add(UserService)
  .add(UsersController)
  .build();

const cluster = wrapWithCluster(built.compile());
await cluster.serve({ http: 3000 });

Who is JustScale For?

JustScale is ideal for:

  • TypeScript developers who want maximum type safety without sacrificing developer experience
  • Teams building maintainable, modular applications that need to scale
  • Projects that need transport flexibility - expose your logic via HTTP today, add CLI tomorrow, events next week
  • Developers who prefer explicit code over magical frameworks