---
title: Middleware
description: Composable job execution middleware for logging, tracing, metrics, recovery, and scope.
---

Middleware wraps each job execution with cross-cutting logic. It is composed into a chain using `middleware.Chain` and applied synchronously before and after each job runs.

## Built-in middleware

| Middleware | Function | Purpose |
|-----------|----------|---------|
| Logging | `middleware.Logging(logger)` | Logs job name, queue, duration, and outcome |
| Recover | `middleware.Recover()` | Catches panics and converts them to errors |
| Timeout | `middleware.Timeout()` | Cancels the job context after `job.Timeout` |
| Tracing | `middleware.Tracing()` | Wraps execution in an OpenTelemetry span |
| Metrics | `middleware.Metrics()` | Records per-job duration and outcome counters |
| Scope | `middleware.Scope()` | Injects Forge app/org IDs from job into context |

## Registering middleware

```go
eng := engine.Build(d,
    engine.WithMiddleware(middleware.Logging(slog.Default())),
    engine.WithMiddleware(middleware.Recover()),
    engine.WithMiddleware(middleware.Timeout()),
    engine.WithMiddleware(middleware.Tracing()),
    engine.WithMiddleware(middleware.Metrics()),
    engine.WithMiddleware(middleware.Scope()),
)
```

Middleware are applied **right-to-left** (last registered = outermost wrapper). With the list above, execution order is:

```
Logging → Recover → Timeout → Tracing → Metrics → Scope → handler
```

## Writing custom middleware

```go
func RateLimitMiddleware(limiter *rate.Limiter) middleware.Middleware {
    return func(ctx context.Context, j *job.Job, next middleware.Handler) error {
        if err := limiter.Wait(ctx); err != nil {
            return err
        }
        return next(ctx)
    }
}
```

Custom middleware must call `next(ctx)` to continue the chain, unless intentionally short-circuiting.

## Composing manually

```go
chain := middleware.Chain(
    middleware.Logging(logger),
    middleware.Recover(),
    middleware.Timeout(),
)
```

`Chain` returns a single `Middleware` that applies each middleware in order.
