or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

application.mdcomponents.mdcontext-api.mdextensions.mdindex.mdlifecycle.mdservices.md

services.mddocs/

0

# Service Layer

1

2

The Service Layer in LoopBack Core provides a robust dependency injection system for registering and consuming services throughout the application. It supports both service classes and provider patterns with type-safe service interfaces and automatic discovery.

3

4

## Capabilities

5

6

### Service Injection Decorator

7

8

Main decorator for injecting services by interface, with automatic type inference and service discovery.

9

10

```typescript { .api }

11

/**

12

* @service injects a service instance that matches the class or interface.

13

*/

14

function service(

15

serviceInterface?: ServiceInterface,

16

metadata?: InjectionMetadata

17

): PropertyDecorator & ParameterDecorator;

18

```

19

20

**Usage Examples:**

21

22

```typescript

23

import { service, inject } from "@loopback/core";

24

25

// Service class

26

class EmailService {

27

async sendEmail(to: string, subject: string, body: string): Promise<void> {

28

// Email implementation

29

}

30

}

31

32

// Service interface (using symbol)

33

const LOGGER_SERVICE = Symbol('LoggerService');

34

35

interface LoggerService {

36

log(message: string): void;

37

error(message: string, error?: Error): void;

38

}

39

40

// Using @service decorator in controllers

41

class UserController {

42

constructor(

43

@service(EmailService) private emailService: EmailService,

44

@service(LOGGER_SERVICE) private logger: LoggerService

45

) {}

46

47

@service() // Type inferred from property type

48

private validationService: ValidationService;

49

50

async createUser(userData: UserData): Promise<User> {

51

this.logger.log('Creating new user');

52

53

// Use injected services

54

const user = await this.userRepository.create(userData);

55

await this.emailService.sendEmail(

56

user.email,

57

'Welcome!',

58

'Welcome to our platform'

59

);

60

61

return user;

62

}

63

}

64

65

// Service injection with metadata

66

class OrderController {

67

constructor(

68

@service(PaymentService, {optional: true})

69

private paymentService?: PaymentService

70

) {}

71

}

72

```

73

74

### Service Binding Creation

75

76

Functions for creating service bindings with proper configuration and interface mapping.

77

78

```typescript { .api }

79

/**

80

* Create a service binding from a class or provider

81

*/

82

function createServiceBinding<S>(

83

cls: ServiceOrProviderClass<S>,

84

options?: ServiceOptions

85

): Binding<S>;

86

87

/**

88

* Create a binding template for a service interface

89

*/

90

function asService(serviceInterface: ServiceInterface): BindingTemplate;

91

```

92

93

**Usage Examples:**

94

95

```typescript

96

import {

97

createServiceBinding,

98

asService,

99

ServiceOptions,

100

Application

101

} from "@loopback/core";

102

103

// Service class

104

class NotificationService {

105

async notify(message: string): Promise<void> {

106

console.log('Notification:', message);

107

}

108

}

109

110

// Provider class

111

class ConfigServiceProvider implements Provider<ConfigService> {

112

value(): ConfigService {

113

return new ConfigService({

114

env: process.env.NODE_ENV || 'development',

115

apiUrl: process.env.API_URL || 'http://localhost:3000'

116

});

117

}

118

}

119

120

const app = new Application();

121

122

// Create service binding manually

123

const notificationBinding = createServiceBinding(NotificationService);

124

app.add(notificationBinding);

125

126

// Create service binding with options

127

const configBinding = createServiceBinding(ConfigServiceProvider, {

128

name: 'app-config',

129

interface: Symbol.for('ConfigService')

130

});

131

app.add(configBinding);

132

133

// Apply service template

134

const loggerBinding = app.bind('services.logger')

135

.toClass(LoggerService)

136

.apply(asService(Symbol.for('LoggerService')));

137

```

138

139

### Service Interface Types

140

141

Type definitions for service interfaces and discovery mechanisms.

142

143

```typescript { .api }

144

/**

145

* Representing an interface for services. In TypeScript, the interface does

146

* not have reflections at runtime. We use a string, a symbol or a Function as

147

* the type for the service interface.

148

*/

149

type ServiceInterface = string | symbol | Function;

150

151

/**

152

* Options to register a service binding

153

*/

154

interface ServiceOptions extends BindingFromClassOptions {

155

interface?: ServiceInterface;

156

}

157

```

158

159

**Usage Examples:**

160

161

```typescript

162

import { ServiceInterface, ServiceOptions } from "@loopback/core";

163

164

// Different service interface types

165

const stringInterface: ServiceInterface = 'UserService';

166

const symbolInterface: ServiceInterface = Symbol.for('PaymentService');

167

const classInterface: ServiceInterface = EmailService;

168

169

// Service registration with different interfaces

170

class ServiceManager {

171

registerServices(app: Application): void {

172

// Register with class as interface

173

app.service(EmailService, {

174

interface: EmailService

175

});

176

177

// Register with symbol interface

178

app.service(PaymentServiceProvider, {

179

interface: Symbol.for('PaymentService'),

180

name: 'payment-processor'

181

});

182

183

// Register with string interface

184

app.service(LoggerService, {

185

interface: 'services.logger'

186

});

187

}

188

}

189

```

190

191

### Service Filtering

192

193

Function for filtering bindings by service interface to support service discovery.

194

195

```typescript { .api }

196

/**

197

* Create a binding filter by service class

198

*/

199

function filterByServiceInterface(

200

serviceInterface: ServiceInterface

201

): BindingFilter;

202

```

203

204

**Usage Examples:**

205

206

```typescript

207

import { filterByServiceInterface, ContextView } from "@loopback/core";

208

209

// Service interface

210

const NOTIFICATION_SERVICE = Symbol.for('NotificationService');

211

212

class NotificationManager {

213

constructor(

214

@inject.context() private context: Context

215

) {}

216

217

async getAllNotificationServices(): Promise<NotificationService[]> {

218

// Filter bindings by service interface

219

const filter = filterByServiceInterface(NOTIFICATION_SERVICE);

220

const view = new ContextView(this.context, filter);

221

return view.values();

222

}

223

224

async sendToAllServices(message: string): Promise<void> {

225

const services = await this.getAllNotificationServices();

226

await Promise.all(

227

services.map(service => service.notify(message))

228

);

229

}

230

}

231

```

232

233

### Provider Pattern Integration

234

235

How services integrate with the Provider pattern for dynamic value creation.

236

237

**Usage Examples:**

238

239

```typescript

240

import { Provider, service, ServiceOptions } from "@loopback/core";

241

242

// Configuration interface

243

interface DatabaseConfig {

244

host: string;

245

port: number;

246

database: string;

247

ssl: boolean;

248

}

249

250

// Provider for database configuration

251

class DatabaseConfigProvider implements Provider<DatabaseConfig> {

252

value(): DatabaseConfig {

253

return {

254

host: process.env.DB_HOST || 'localhost',

255

port: parseInt(process.env.DB_PORT || '5432'),

256

database: process.env.DB_NAME || 'myapp',

257

ssl: process.env.NODE_ENV === 'production'

258

};

259

}

260

}

261

262

// Service that uses the configuration

263

class DatabaseService {

264

constructor(

265

@service('database.config') private config: DatabaseConfig

266

) {}

267

268

async connect(): Promise<void> {

269

console.log(`Connecting to ${this.config.host}:${this.config.port}/${this.config.database}`);

270

// Database connection logic

271

}

272

}

273

274

// Registration

275

const app = new Application();

276

277

// Register config provider

278

app.service(DatabaseConfigProvider, {

279

name: 'database.config',

280

interface: 'database.config'

281

});

282

283

// Register database service

284

app.service(DatabaseService);

285

```

286

287

### Service Composition

288

289

How to compose complex services from multiple dependencies.

290

291

**Usage Examples:**

292

293

```typescript

294

import { service, inject } from "@loopback/core";

295

296

// Individual services

297

class EmailService {

298

async send(to: string, subject: string, body: string): Promise<void> {

299

// Email implementation

300

}

301

}

302

303

class SmsService {

304

async send(to: string, message: string): Promise<void> {

305

// SMS implementation

306

}

307

}

308

309

class PushNotificationService {

310

async send(userId: string, message: string): Promise<void> {

311

// Push notification implementation

312

}

313

}

314

315

// Composite service using multiple dependencies

316

class NotificationService {

317

constructor(

318

@service(EmailService) private emailService: EmailService,

319

@service(SmsService) private smsService: SmsService,

320

@service(PushNotificationService) private pushService: PushNotificationService,

321

@inject('config.notifications') private config: NotificationConfig

322

) {}

323

324

async notifyUser(userId: string, message: NotificationMessage): Promise<void> {

325

const user = await this.getUserById(userId);

326

327

// Send via multiple channels based on configuration

328

if (this.config.enableEmail && user.email) {

329

await this.emailService.send(user.email, message.subject, message.body);

330

}

331

332

if (this.config.enableSms && user.phone) {

333

await this.smsService.send(user.phone, message.text);

334

}

335

336

if (this.config.enablePush) {

337

await this.pushService.send(userId, message.text);

338

}

339

}

340

341

private async getUserById(userId: string): Promise<User> {

342

// User lookup implementation

343

return {} as User;

344

}

345

}

346

347

// Register all services

348

const app = new Application();

349

app.service(EmailService);

350

app.service(SmsService);

351

app.service(PushNotificationService);

352

app.service(NotificationService); // Will receive all dependencies

353

```

354

355

### Service Lifecycle Integration

356

357

How services integrate with the application lifecycle system.

358

359

**Usage Examples:**

360

361

```typescript

362

import {

363

service,

364

lifeCycleObserver,

365

LifeCycleObserver,

366

inject

367

} from "@loopback/core";

368

369

// Service that implements lifecycle observer

370

@lifeCycleObserver('external-services')

371

class ExternalApiService implements LifeCycleObserver {

372

private client: ApiClient | null = null;

373

374

constructor(

375

@inject('config.external-api') private config: ExternalApiConfig

376

) {}

377

378

async init(): Promise<void> {

379

console.log('Initializing external API client...');

380

this.client = new ApiClient(this.config);

381

}

382

383

async start(): Promise<void> {

384

console.log('Connecting to external API...');

385

await this.client?.connect();

386

}

387

388

async stop(): Promise<void> {

389

console.log('Disconnecting from external API...');

390

await this.client?.disconnect();

391

this.client = null;

392

}

393

394

async fetchData(endpoint: string): Promise<any> {

395

if (!this.client) {

396

throw new Error('API client not initialized');

397

}

398

return this.client.get(endpoint);

399

}

400

}

401

402

// Controller using the lifecycle-aware service

403

class DataController {

404

constructor(

405

@service(ExternalApiService) private apiService: ExternalApiService

406

) {}

407

408

async getData(): Promise<any> {

409

return this.apiService.fetchData('/api/data');

410

}

411

}

412

413

// Register both service and controller

414

const app = new Application();

415

app.service(ExternalApiService); // Will be managed by lifecycle system

416

app.controller(DataController);

417

```

418

419

### Dynamic Service Discovery

420

421

How to dynamically discover and work with multiple services of the same interface.

422

423

**Usage Examples:**

424

425

```typescript

426

import {

427

service,

428

extensions,

429

extensionPoint,

430

filterByServiceInterface,

431

ContextView

432

} from "@loopback/core";

433

434

// Plugin interface

435

interface PaymentProcessor {

436

processPayment(amount: number, method: string): Promise<PaymentResult>;

437

getSupportedMethods(): string[];

438

}

439

440

// Multiple implementations

441

class StripePaymentProcessor implements PaymentProcessor {

442

async processPayment(amount: number, method: string): Promise<PaymentResult> {

443

// Stripe implementation

444

return { success: true, transactionId: 'stripe_123' };

445

}

446

447

getSupportedMethods(): string[] {

448

return ['card', 'bank_transfer'];

449

}

450

}

451

452

class PayPalPaymentProcessor implements PaymentProcessor {

453

async processPayment(amount: number, method: string): Promise<PaymentResult> {

454

// PayPal implementation

455

return { success: true, transactionId: 'paypal_456' };

456

}

457

458

getSupportedMethods(): string[] {

459

return ['paypal', 'credit'];

460

}

461

}

462

463

// Service that uses all payment processors

464

const PAYMENT_PROCESSOR = Symbol.for('PaymentProcessor');

465

466

class PaymentService {

467

constructor(

468

@inject.context() private context: Context

469

) {}

470

471

async getAvailableProcessors(): Promise<PaymentProcessor[]> {

472

const filter = filterByServiceInterface(PAYMENT_PROCESSOR);

473

const view = new ContextView<PaymentProcessor>(this.context, filter);

474

return view.values();

475

}

476

477

async processPayment(amount: number, method: string): Promise<PaymentResult> {

478

const processors = await this.getAvailableProcessors();

479

480

for (const processor of processors) {

481

if (processor.getSupportedMethods().includes(method)) {

482

return processor.processPayment(amount, method);

483

}

484

}

485

486

throw new Error(`No processor found for method: ${method}`);

487

}

488

}

489

490

// Register all services

491

const app = new Application();

492

493

app.service(StripePaymentProcessor, {

494

interface: PAYMENT_PROCESSOR

495

});

496

497

app.service(PayPalPaymentProcessor, {

498

interface: PAYMENT_PROCESSOR

499

});

500

501

app.service(PaymentService);

502

```

503

504

## Types

505

506

```typescript { .api }

507

type ServiceInterface = string | symbol | Function;

508

509

interface ServiceOptions extends BindingFromClassOptions {

510

interface?: ServiceInterface;

511

}

512

513

type ServiceOrProviderClass<T = any> =

514

| Constructor<T | Provider<T>>

515

| DynamicValueProviderClass<T>;

516

517

interface InjectionMetadata {

518

optional?: boolean;

519

asProxyWithInterceptors?: boolean;

520

bindingComparator?: BindingComparator;

521

}

522

```