Plugins
Extend JustScale with custom transports and plugins
JustScale's plugin system allows you to extend the framework with custom route factories, transports, and integrations. The core framework is transport-agnostic - HTTP, gRPC, WebSockets, and other protocols are implemented as plugins.
Understanding the Plugin System
JustScale uses TypeScript's module augmentation and a registry-based approach to extend functionality. Plugins can:
- Add new route factory methods (like Get, Post, etc.)
- Extend the route context with transport-specific properties
- Register as cluster transports for protocol-specific serving
- Hook into the app lifecycle for initialization
Creating Custom Route Factories
Route factories provide the DSL for defining routes. The HTTP plugin provides Get, Post, etc. You can create your own.
import type { RouteHandler, RouteDef } from "@justscale/core/plugin";
import { registerRouteFactory } from "@justscale/core/plugin";
import type { HttpMethod } from "@justscale/core/plugin";
// Step 1: Augment the RouteFactories interface
declare module "@justscale/core/plugin" {
interface RouteFactories<TDeps> {
// Add your custom route factory method
SSE(path: string): SseRouteBuilder<TDeps>;
}
}
// Step 2: Create the factory implementation
function createSseFactory() {
return function SSE<TDeps, TPath extends string>(
path: TPath
): SseRouteBuilder<TDeps, TPath> {
return {
handle(handler) {
return {
method: "GET",
path,
middlewares: [],
guards: [],
handler: async (ctx) => {
// Set up SSE headers
ctx.res.setHeader("Content-Type", "text/event-stream");
ctx.res.setHeader("Cache-Control", "no-cache");
ctx.res.setHeader("Connection", "keep-alive");
// Create event stream
const stream = new EventStream(ctx.res);
await handler({ ...ctx, stream });
},
};
},
};
};
}
// Register the factory
registerRouteFactory("SSE", createSseFactory());Extending Route Context
Plugins can add transport-specific properties to route handlers by augmenting the RouteContext interface.
import type { ServerResponse, IncomingMessage } from "http";
declare module "@justscale/core/plugin" {
interface RouteContext<TDeps, TParams> {
// Add HTTP-specific properties
res: ServerResponse;
req: IncomingMessage;
// These are now available in all route handlers
}
}The HTTP plugin uses this to add res and req. Your handlers can then access these properties with full type safety.
Creating Cluster Transports
Cluster transports integrate with createCluster() to handle protocol-specific serving. The HTTP transport is a good example.
import { registerClusterTransport } from "@justscale/cluster";
import type { ClusterTransportPlugin } from "./types";
const GrpcTransport: ClusterTransportPlugin = {
name: "grpc",
beforeControllerResolution(container, controllers) {
// Register gRPC-specific services
// e.g., reflection service, interceptors
},
onAppCreated(app) {
// Optional: post-creation setup
},
async serve(app, config) {
const server = new GrpcServer();
// Register routes from app.controllers
for (const controller of app.controllers) {
for (const route of controller.routes) {
server.register(route);
}
}
await server.listen(config.port);
return {
stop: async () => {
await server.close();
},
};
},
};
// Register the transport
registerClusterTransport("grpc", GrpcTransport);Real-World Example: HTTP Plugin
The @justscale/http package is a complete plugin implementation. Here's how it's structured:
import { registerRouteFactory } from "@justscale/core/plugin";
import { registerClusterTransport } from "@justscale/cluster";
import { Get, Post, Put, Delete, Patch } from "./factory";
import { HttpTransport } from "./transport";
// 1. Augment route factories
declare module "@justscale/core/plugin" {
interface RouteFactories<TDeps> {
Get: HttpFactory<TDeps>;
Post: HttpFactory<TDeps>;
Put: HttpFactory<TDeps>;
Delete: HttpFactory<TDeps>;
Patch: HttpFactory<TDeps>;
}
}
// 2. Augment route context
declare module "@justscale/core/plugin" {
interface RouteContext<TDeps, TParams> {
res: JsonResponse;
req: IncomingMessage;
}
}
// 3. Register factories
registerRouteFactory("Get", Get);
registerRouteFactory("Post", Post);
registerRouteFactory("Put", Put);
registerRouteFactory("Delete", Delete);
registerRouteFactory("Patch", Patch);
// 4. Register as cluster transport
registerClusterTransport("http", HttpTransport);Plugin Best Practices
- Use module augmentation for type safety - Always augment interfaces rather than using
any - Register early - Import and register plugins before creating your app
- Namespace your additions - Prefix custom context properties to avoid conflicts
- Document your plugin - Provide clear examples and type definitions
- Test in isolation - Use
createStandaloneAppfor plugin testing
Info
Pro Tip: Study the source code of @justscale/http and @justscale/datastar for complete plugin examples. They demonstrate route factories, context augmentation, and cluster transport integration.