inngest-middleware

Use when adding cross-cutting concerns to durable functions — structured logging or tracing across all functions, error tracking with Sentry, payload…

INSTALLATION
npx skills add https://github.com/inngest/inngest-skills --skill inngest-middleware
Run in your project or agent environment. Adjust flags if your CLI version differs.

SKILL.md

$28

  • Observability: Add logging, tracing, or metrics
  • Dependency injection: Share client instances across functions
  • Data transformation: Encrypt/decrypt, validate, or enrich data
  • Error handling: Custom error tracking and alerting
  • Authentication: Validate user context or permissions

Middleware Lifecycle

Middleware can be registered at client-level (affects all functions) or function-level (affects specific functions).

Execution Order

const inngest = new Inngest({

  id: "my-app",

  middleware: [

    loggingMiddleware, // Runs 1st

    errorMiddleware // Runs 2nd

  ]

});

inngest.createFunction(

  {

    id: "example",

    middleware: [

      authMiddleware, // Runs 3rd

      metricsMiddleware // Runs 4th

    ],

    triggers: [{ event: "test" }]

  },

  async () => {

    /* function code */

  }

);

Order matters: Client middleware runs first, then function middleware, in the order specified.

Creating Custom Middleware

Basic Middleware Structure

import { InngestMiddleware } from "inngest";

const loggingMiddleware = new InngestMiddleware({

  name: "Logging Middleware",

  init() {

    // Setup phase - runs when client initializes

    const logger = setupLogger();

    return {

      // Function execution lifecycle

      // Note: `fn` is loosely typed in middleware generics; fn.id works at runtime

      onFunctionRun({ ctx, fn }) {

        return {

          beforeExecution() {

            logger.info("Function starting", {

              functionId: fn.id,

              eventName: ctx.event.name,

              runId: ctx.runId

            });

          },

          afterExecution() {

            logger.info("Function completed", {

              functionId: fn.id,

              runId: ctx.runId

            });

          },

          transformOutput({ result }) {

            // Log function output

            logger.debug("Function output", {

              functionId: fn.id,

              output: result.data

            });

            // Return unmodified result

            return { result };

          }

        };

      },

      // Event sending lifecycle

      onSendEvent() {

        return {

          transformInput({ payloads }) {

            logger.info("Sending events", {

              count: payloads.length,

              events: payloads.map((p) => p.name)

            });

            // Spread to convert readonly array to mutable array

            return { payloads: [...payloads] };

          }

        };

      }

    };

  }

});

Python Implementation

Python middleware follows a similar pattern. See Dependency Injection Reference for complete Python examples.

## Dependency Injection

Share expensive or stateful clients across all functions. **See [Dependency Injection Reference](./references/dependency-injection.md) for detailed patterns.**

### Quick Example - Built-in DI

import { dependencyInjectionMiddleware } from "inngest";

const inngest = new Inngest({

id: 'my-app',

middleware: [

dependencyInjectionMiddleware({

openai: new OpenAI(),

db: new PrismaClient(),

}),

],

});

// Functions automatically get injected dependencies

inngest.createFunction(

{ id: "ai-summary", triggers: [{ event: "document/uploaded" }] },

async ({ event, openai, db }) => {

// Dependencies available in function context

const summary = await openai.chat.completions.create({

messages: [{ role: "user", content: event.data.content }],

model: "gpt-4",

});

await db.document.update({

where: { id: event.data.documentId },

data: { summary: summary.choices[0].message.content }

});

}

);


## Middleware Packages

Beyond `dependencyInjectionMiddleware` (built-in, shown above), Inngest provides official middleware as **separate packages**. **See [Middleware Reference](https://github.com/inngest/inngest-skills/blob/HEAD/skills/inngest-middleware/./references/built-in-middleware.md) for complete details.**

### Encryption Middleware

npm install @inngest/middleware-encryption

import { encryptionMiddleware } from "@inngest/middleware-encryption";

const inngest = new Inngest({

id: "my-app",

middleware: [

encryptionMiddleware({

key: process.env.ENCRYPTION_KEY

})

]

});


Automatically encrypts all step data, function output, and event `data.encrypted` field. Supports key rotation via `fallbackDecryptionKeys`.

### Sentry Error Tracking

npm install @inngest/middleware-sentry

import * as Sentry from "@sentry/node";

import { sentryMiddleware } from "@inngest/middleware-sentry";

Sentry.init({

/ your Sentry config /

});

const inngest = new Inngest({

id: "my-app",

middleware: [sentryMiddleware()]

});


Captures exceptions, adds tracing to each function run, and includes function ID and event names as context. Requires `@sentry/*@>=8.0.0`.

## Common Middleware Patterns

### Metrics and Performance Tracking

const metricsMiddleware = new InngestMiddleware({

name: "Metrics Tracking",

init() {

return {

onFunctionRun({ ctx, fn }) {

let startTime: number;

return {

beforeExecution() {

startTime = Date.now();

metrics.increment("inngest.step.started", {

function: fn.id,

event: ctx.event.name

});

},

afterExecution() {

const duration = Date.now() - startTime;

metrics.histogram("inngest.step.duration", duration, {

function: fn.id,

event: ctx.event.name

});

},

transformOutput({ result }) {

const status = result.error ? "error" : "success";

metrics.increment("inngest.step.completed", {

function: fn.id,

status: status

});

return { result };

}

};

}

};

}

});


### Advanced Patterns

**Authentication:** Validate tokens and inject user context
**Conditional logic:** Apply middleware based on event type or function
**Circuit breakers:** Prevent cascading failures from external services

### Configuration-Based Middleware

Create reusable middleware with configuration options for different environments and use cases. See reference documentation for complete examples.

## Best Practices

### Design Principles

- **Keep middleware focused:** One concern per middleware

- **Handle errors gracefully:** Don't let middleware crash functions

- **Consider performance:** Middleware runs on every execution

- **Use proper typing:** Let TypeScript infer middleware types

- **Test thoroughly:** Middleware affects all functions that use it

### Common Use Cases to Implement

- **Retry logic** for transient failures

- **Circuit breakers** for external service calls

- **Request/response logging** for debugging

- **User context enrichment** from external sources

- **Feature flags** for gradual rollouts

- **Custom authentication** and authorization checks

### Error Handling in Middleware

const robustMiddleware = new InngestMiddleware({

name: "Robust Middleware",

init() {

return {

onFunctionRun({ ctx, fn }) {

return {

transformOutput({ result }) {

try {

// Your middleware logic here

return performTransformation(result);

} catch (middlewareError) {

// Log error but don't break the function

console.error("Middleware error:", middlewareError);

// Return original result on middleware failure

return { result };

}

}

};

}

};

}

});

BrowserAct

Let your agent run on any real-world website

Bypass CAPTCHA & anti-bot for free. Start local, scale to cloud.

Explore BrowserAct Skills →

Stop writing automation&scrapers

Install the CLI. Run your first Skill in 30 seconds. Scale when you're ready.

Start free
free · no credit card