Skip to main content

Services

Services are the primary building block of Webda application logic. Every service extends the Service<P> base class and participates in the lifecycle managed by Core.

Service base class​

import { Service, ServiceParameters } from "@webda/core";

// 1. Define typed configuration
interface MyServiceParameters extends ServiceParameters {
apiUrl: string;
maxRetries?: number;
}

// 2. Extend Service<P>
export class MyService extends Service<MyServiceParameters> {
async init(): Promise<this> {
await super.init();
this.log.info("Initialized", { url: this.parameters.apiUrl });
return this;
}
}

ServiceParameters base fields:

FieldTypeDescription
typestringService type (set by the framework)
openapiobjectOpenAPI metadata override

The @Bean decorator​

@Bean registers a class as an auto-discovered singleton service. The framework instantiates it automatically when the application starts.

import { Bean } from "@webda/core";

@Bean
export class NotificationService extends Service {
async sendAlert(message: string): Promise<void> {
// ...
}
}

A @Bean service is always registered in the application's service map. It does not need to be declared in webda.config.json.

The @Inject decorator​

@Inject performs dependency injection — it sets the decorated field to the named service instance after all services are constructed, during the resolve() phase.

import { Service, Inject, Store } from "@webda/core";
import { Bean } from "@webda/core";

@Bean
export class OrderService extends Service {
// Inject by service name
@Inject("orderStore")
store: Store<Order>;

// Inject from configuration parameter value
@Inject("params:notificationServiceName", "NotificationService")
notifier: NotificationService;

// Optional injection (does not throw if service is missing)
@Inject("CacheService", undefined, true)
cache?: CacheService;
}

@Inject signature: @Inject(name, defaultName?, optional?)

ArgumentDescription
nameService name OR "params:key" to read from this.parameters[key]
defaultNameFallback service name if name resolves to undefined
optionalIf true, silently set to undefined when not found

The @Route decorator​

@Route registers a service method as an HTTP endpoint:

import { Service, Route, WebContext } from "@webda/core";
import { Bean } from "@webda/core";

@Bean
export class StatusService extends Service {
@Route("/status", ["GET"])
async getStatus(context: WebContext): Promise<void> {
context.write({ status: "ok", uptime: process.uptime() });
}

@Route("/admin/reset", ["POST"])
async reset(context: WebContext): Promise<void> {
await this.resetAllData();
context.write({ done: true });
}
}

@Route signature: @Route(url, methods, openapi?)

ServiceParameters typing​

Define your service's configuration shape by extending ServiceParameters:

import { ServiceParameters } from "@webda/core";

export class PublisherParameters extends ServiceParameters {
platforms: ("linkedin" | "twitter")[];
maxPostLength?: number;

load(params: any = {}): this {
super.load(params);
this.platforms ??= ["twitter"];
this.maxPostLength ??= 280;
return this;
}
}

The load(params) method is called during service construction. Use ??= to set defaults.

Accessing services at runtime​

From anywhere in a request handler or service method:

import { useService, useDynamicService } from "@webda/core";

// Typed access
const crypto = useService("CryptoService");

// Dynamic name from config
const storeName = this.parameters.storeName;
const store = useDynamicService<Store>(storeName);

Configuration in webda.config.json​

Services (other than @Bean) must be declared in config:

{
"services": {
"orderStore": {
"type": "Webda/MemoryStore",
"model": "MyApp/Order"
},
"notificationService": {
"type": "MyApp/NotificationService",
"notificationServiceName": "notificationService"
}
}
}

Logging in services​

Use useLog from @webda/workout:

import { useLog } from "@webda/workout";

@Bean
export class MyService extends Service {
protected log = useLog("MyService");

async doWork(): Promise<void> {
this.log.info("Starting work", { key: "value" });
try {
await this.run();
this.log.debug("Work complete");
} catch (err) {
this.log.error("Work failed", { error: err });
throw err;
}
}
}

Verify​

cd packages/core
pnpm test
✓ packages/core — service tests pass

See also​