# Connect a LoopBack 4 app

> Add ApiTraffic to a LoopBack 4 API as a component: install, register, credentials, what is recorded, firewall, APM and troubleshooting.

Source: https://www.apitraffic.io/docs/integrations/loopback

`@apitraffic/loopback` is a LoopBack 4 component. Register it once, in your application's
constructor, and every request your REST server serves is recorded and checked by the ApiTraffic
firewall, whichever sequence your application uses.

It is [`@apitraffic/node`](https://www.apitraffic.io/docs/integrations/node), the one-install SDK, wired into LoopBack: the
same capture, the same firewall and APM bridge, the same helpers, all enabled from your ApiTraffic
account rather than in code.

## Installation

**npm**

```bash
npm install @apitraffic/loopback
```

**yarn**

```bash
yarn add @apitraffic/loopback
```

**pnpm**

```bash
pnpm add @apitraffic/loopback
```

> **Note:** **Node 20+ and `@loopback/rest` 13, 14 or 15 (`@loopback/core` 5, 6 or 7) required.**

## Basic Setup

```typescript
// src/application.ts
import { ApplicationConfig } from '@loopback/core';
import { RestApplication } from '@loopback/rest';
import { ApiTrafficBindings, ApiTrafficComponent, type ApiTrafficOptions } from '@apitraffic/loopback';

export class MyApplication extends RestApplication {
  constructor(options: ApplicationConfig = {}) {
    super(options);

    this.configure<ApiTrafficOptions>(ApiTrafficBindings.COMPONENT).to({
      token: process.env.API_TRAFFIC_TOKEN,
      bucket: process.env.API_TRAFFIC_BUCKET,
    });
    this.component(ApiTrafficComponent);

    // ...your controllers, sequence, other components
  }
}
```

Configure the component **before** registering it: LoopBack builds a component as soon as it is
registered. Without a configuration every option falls back to its `API_TRAFFIC_*` environment
variable, so `this.component(ApiTrafficComponent)` alone is a complete setup.

It works with any `basePath`, with the default middleware sequence and with the action-based
`DefaultSequence` older applications were generated with, on every REST server the application has,
and in an application mounted on an Express app of your own (`expressApp.use('/api',
lbApp.requestHandler)`). Mounted under a path with parameters (`/tenants/:tenantId`), or through an
Express `Router`, requests are recorded without the route, and the endpoint is worked out from the
path.

## What is recorded

Every request, including the ones a controller never sees. The component runs ahead of LoopBack's
whole sequence (its CORS handling, middleware, routing, authentication and body parsing), so these
are all recorded, with their final status and whatever your code tagged on the way:

- a request **authentication** or a **middleware** rejects or answers itself (`401`),
- a request an **interceptor** rejects (`@loopback/authorization`'s `403`),
- a request whose **parameters fail validation** (`400`, `422`),
- a request **no route** matches (`404`), including one outside the REST server's `basePath`,
- a request whose controller **throws**, as the response LoopBack's reject action sent.

The route LoopBack matched is sent with each request, controller base path and REST `basePath`
included, so `@get('/users/{id}')` in a controller with `@api({ basePath: '/v1' })` on a server
with `basePath: '/api'` is the endpoint `/api/v1/users/{id}`. See
[Endpoint Handling](https://www.apitraffic.io/docs/endpoint-handling#framework-routes). Requests served by an Express router
you mounted with `mountExpressRouter()`, or by `app.static()`, are recorded without one, and the
endpoint is worked out from the path.

Requests are captured by `@apitraffic/express` on the REST server's Express application: exactly
what the [Express](https://www.apitraffic.io/docs/integrations/express) page describes under "What is captured" holds here
too.

## Tagging, tracing and spans

The helpers work anywhere a request is being handled: controllers, services, repositories,
interceptors, middleware and sequence actions.

```typescript
import { Interceptor } from '@loopback/core';
import { RestBindings } from '@loopback/rest';
import { tag } from '@apitraffic/loopback';

export const accountTag: Interceptor = async (invocationCtx, next) => {
  const request = await invocationCtx.get(RestBindings.Http.REQUEST);
  // Searchable on this request, whether or not it gets any further.
  tag('accountId', String(request.headers['x-account-id']));
  return next();
};
```

```typescript
import { repository } from '@loopback/repository';
import { span, trace } from '@apitraffic/loopback';
import { UserRepository } from '../repositories';

export class UserService {
  constructor(@repository(UserRepository) private users: UserRepository) {}

  async findOne(id: string) {
    trace(`Loading user ${id}`);
    return span('db.query', 'load user', () => this.users.findById(id));
  }
}
```

Outside a request (a cron job, a queue consumer) the helpers do nothing and never throw.

`stats()` reports the SDK's delivery health (delivered, queued, dropped), for a health endpoint or
a log line.

## Shutdown

`app.stop()` (which the application generated by `lb4` calls on `SIGTERM` and `SIGINT` when
`shutdown.signals` is set) delivers what was captured and releases the SDK's timers and
connections once the last application using the component has stopped. The REST server has
stopped by then, so every request it served has been reported. An application started again
afterwards sets the SDK up again and is recorded as before; the APM bridge is not restarted.

## Firewall

The ApiTraffic firewall is mounted by the component, and whether it runs is decided in your
ApiTraffic account: nothing is loaded until you switch a policy on, and a policy change reaches
every server without a deploy. It runs twice on a request at most, and counts it once:

- **ahead of the sequence**, right after the request is recorded, so a request it blocks there
  never reaches LoopBack's middleware, routing or authentication;
- **once LoopBack has parsed the body**, so body rules see it: with the default middleware sequence,
  between parameter parsing and the method, ahead of every interceptor; with the action-based
  `DefaultSequence`, as a global interceptor in the group `apitraffic.firewall` (list it first in
  your global interceptor order to run it ahead of the others). It is evaluated again there only if
  it had no body before.

Blocked requests are recorded with the policy's status and tagged `waf.*`.

To refuse it locally whatever the dashboard says (change control, or an incident switch that does
not depend on the control plane), set `waf: false` or `API_TRAFFIC_WAF_ENABLED=false`:

```typescript
this.configure<ApiTrafficOptions>(ApiTrafficBindings.COMPONENT).to({ waf: false });
```

See [Firewall setup](https://www.apitraffic.io/docs/waf/setup) for policies and rules.

## Application Performance Monitoring (APM)

The APM bridge captures database, cache, queue, GraphQL, gRPC, and `fetch`/`undici` spans within
each ApiTraffic request. Install the OpenTelemetry peers it uses:

```bash
npm install @opentelemetry/api @opentelemetry/sdk-trace-node @opentelemetry/instrumentation @opentelemetry/auto-instrumentations-node
```

OpenTelemetry patches modules as they are loaded, so it has to start before LoopBack and your
datasource connectors do. Preload it:

```bash
node -r @apitraffic/loopback/register .
```

or import it on the very first line of `src/index.ts`, ahead of every other import:

```typescript
import '@apitraffic/loopback/register';
```

(Not `register()` from `@apitraffic/loopback` itself: importing the component loads LoopBack's REST
stack, which then loads before the bridge can patch it.)

If your application already configures OpenTelemetry (with `@loopback/opentelemetry`, say), add
`ApiTrafficSpanProcessor` from `@apitraffic/apm` to that provider instead, so spans are not
double-instrumented.

## Configuration Options

| Option | Environment Variable | Required | Type | Description |
|--------|---------------------|----------|------|-------------|
| `token` | `API_TRAFFIC_TOKEN` | Yes | String | Ingest token from your ApiTraffic account |
| `bucket` | `API_TRAFFIC_BUCKET` | Yes | String | Bucket ID for data organization |
| `interceptOutbound` | `API_TRAFFIC_INTERCEPT_OUTBOUND` | No | Boolean | Monitor outbound HTTP requests (default: true) |
| `debug` | `API_TRAFFIC_DEBUG` | No | Boolean | Enable debug logging (default: false) |
| `waf` | `API_TRAFFIC_WAF_ENABLED` | No | Boolean or object | `false` refuses the firewall locally; an object passes settings to `@apitraffic/waf`. Normally left unset: the firewall is switched on from your account |

An option passed in code wins over the environment.

The options are typed: with `configure()`, a value of the wrong type is a
compile error, and so is a misspelled key in an object literal.

## Embed tokens

The [embeddable request log](https://www.apitraffic.io/docs/integrations/embed) authenticates with a token your server
mints. `embedToken` is exported here, as from every ApiTraffic package:

```typescript
import { get } from '@loopback/rest';
import { embedToken } from '@apitraffic/loopback';

export class EmbedController {
  @get('/embed/token')
  token() {
    return embedToken({ filters: { tags: { customerId: 'cus_123' } }, ttl: 900 });
  }
}
```

## Troubleshooting

**No requests are recorded**

Check that `this.component(ApiTrafficComponent)` runs in the application you start, and that
`API_TRAFFIC_TOKEN` and `API_TRAFFIC_BUCKET` are set. Set `debug: true` to see what the SDK is
doing.

**The options I configured are ignored**

`this.configure(ApiTrafficBindings.COMPONENT)` has to come before
`this.component(ApiTrafficComponent)`: the component reads its configuration when it is
registered.

**Tags set in a cron job or queue consumer are missing**

Tags, traces and spans belong to an HTTP request. Code that does not run while a request is
being handled has none to attach them to, and the helpers do nothing there.

**I already mount @apitraffic/node with expressMiddleware()**

Remove it when you register the component. Left in place it does no harm (each request is
still recorded and evaluated once), but the component records more of your traffic than it can:
`expressMiddleware()` runs inside the sequence, after a request outside the `basePath` has
already been answered.

## Sample Application

A complete working example is available in the [GitHub repository](https://github.com/apitraffic/platform/tree/main/libs/client-libraries/apitraffic-loopback/examples/basic).
