Skip to content

Named inject map

Inline HTTP handlers declare inject as a named map and read services from scope.name.

AI generated, pending review Updated 10 days ago · Flare 0.3

Class controllers declare dependencies with static deps and this.inject(Token). Inline route handlers do not have a class, so they declare services in the route options with a named inject map: inject: { name: Token }, then read scope.name in the handler.

config and input are reserved scope keys (input holds parsed route, query, and body fields from the route descriptor). Do not use those names in your inject map.

import { FlareHost, FlareResponse, FlareService } from "@flare-ts/core";
import { node } from "@flare-ts/core/node";
class DbService extends FlareService {
public static override deps = [];
query() {
return { ok: true };
}
}
const host = new FlareHost(node);
host.scoped(DbService);
host.http.get(
"/db",
{ inject: { db: DbService } },
(_ctx, scope) => new FlareResponse(200, scope.db.query()),
);
const app = host.build();
app.run();

The map key (db) is the property name on scope. The value is the service token. Every token in the map must be registered with host.scoped() or host.singleton() before build(). Unregistered tokens produce CONTROLLER_UNREGISTERED_DEP at build time because inline routes are validated like controller routes.

import { FlareHost, FlareResponse, FlareService } from "@flare-ts/core";
import { node } from "@flare-ts/core/node";
class DbService extends FlareService {
public static override deps = [];
all() {
return [];
}
}
class CacheService extends FlareService {
public static override deps = [];
hit(_key: string) {
return 1;
}
}
const host = new FlareHost(node);
host.scoped(DbService);
host.scoped(CacheService);
host.http.get(
"/report",
{ inject: { db: DbService, cache: CacheService } },
(_ctx, scope) => {
const rows = scope.db.all();
const hit = scope.cache.hit("report");
return new FlareResponse(200, { rows, hit });
},
);
const app = host.build();
app.run();

Pick names that read clearly in the handler. They are local to the route, not global API.

Inline handlers often need both services and parsed request input:

import { FlareHost, FlareResponse, FlareService } from "@flare-ts/core";
import { node } from "@flare-ts/core/node";
import { int } from "@flare-ts/lib/schema";
class UserService extends FlareService {
public static override deps = [];
findById(id: number) {
return { id };
}
}
const host = new FlareHost(node);
host.scoped(UserService);
host.http.get(
"/users/:id",
{ route: { id: int }, inject: { users: UserService } },
(_ctx, scope) => {
const row = scope.users.findById(scope.input.route.id);
return new FlareResponse(200, row);
},
);
const app = host.build();
app.run();

scope.input carries validated route, query, and body fields from the descriptor. scope.users is the injected service. See HTTP contracts and Inline route handlers.

Controllers, middleware, services, and error handlers use static deps and this.inject(Token). The named map applies only to function-form route handlers (and the same pattern exists on WebSocket inline handlers). Migrating from an earlier release? See Release notes.