inngest-middleware
inngest/inngest-skills
Add cross-cutting concerns to Inngest functions: logging, error tracking, encryption, dependency injection, and custom telemetry.
What is inngest-middleware?
Inngest middleware runs at key points in the function lifecycle to handle observability, dependency injection, data transformation, and error handling uniformly across all functions. Use this skill when you need structured logging, tracing, Sentry integration, payload encryption, or shared client instances (DB, Stripe, etc.) injected into function handlers.
- Create custom middleware with lifecycle hooks (beforeExecution, afterExecution, transformOutput, transformInput)
- Inject dependencies like database clients and API instances into all function handlers via dependencyInjectionMiddleware
- Encrypt sensitive data automatically with @inngest/middleware-encryption, supporting key rotation
- Track errors and add distributed tracing with @inngest/middleware-sentry
- Implement metrics, logging, and custom telemetry across all functions
- Apply middleware at client-level (all functions) or function-level (specific functions) with controlled execution order
How to install inngest-middleware
npx skills add https://github.com/inngest/inngest-skills --skill inngest-middleware- Inngest v4 (middleware API differs significantly from v3)
- TypeScript (skills focused on TS; Python/Go refer to Inngest docs)
- For Sentry: @sentry/*@>=8.0.0
How to use inngest-middleware
- 1.Install the skill: npx skills add https://github.com/inngest/inngest-skills --skill inngest-middleware
- 2.For dependency injection, use the built-in dependencyInjectionMiddleware: pass client instances and access them in function handlers
- 3.For encryption, install @inngest/middleware-encryption and configure with your encryption key
- 4.For Sentry, install @inngest/middleware-sentry, initialize Sentry, and add sentryMiddleware() to your Inngest client
- 5.Create custom middleware by extending InngestMiddleware with init() returning onFunctionRun() and/or onSendEvent() hooks
- 6.Register middleware at client-level (affects all functions) or function-level (affects specific functions); order matters
Use cases
- Add structured logging and tracing to all Inngest functions without modifying each handler
- Inject a shared Prisma database client and Stripe API instance into every function
- Automatically encrypt sensitive fields in event payloads and step outputs
- Capture exceptions and add Sentry tracing context to all function runs
- Track function execution duration and success/error metrics across your workflow
- Backend engineers building durable workflows with Inngest
- Teams needing observability and error tracking across many functions
- Applications handling sensitive data requiring encryption at rest
- Projects using dependency injection patterns to share clients across handlers
inngest-middleware FAQ
The middleware system was significantly rewritten in v4. The lifecycle hooks and API differ from v3. If migrating, consult the v3-to-v4 migration guide. Do NOT use @inngest/realtime on v4 projects; realtime is now built-in via step.realtime.publish.
No. Inngest v4 ships realtime natively with step.realtime.publish built-in. Do not install @inngest/realtime (v3-era package) on v4 projects; it causes TypeError at runtime. Use the inngest-realtime skill for v4 patterns.
Yes. Register middleware at function-level in the function config object. Client-level middleware runs first (in order), then function-level middleware (in order).
Pass an object to dependencyInjectionMiddleware with all clients: {openai: new OpenAI(), db: new PrismaClient(), stripe: new Stripe(...)}. All are available in function context.
onFunctionRun() provides beforeExecution(), afterExecution(), and transformOutput(). onSendEvent() provides transformInput(). Use these to log, track metrics, transform data, or handle errors.
Full instructions (SKILL.md)
Source of truth, from inngest/inngest-skills.
name: inngest-middleware description: Use when adding cross-cutting concerns to durable functions — structured logging or tracing across all functions, error tracking with Sentry, payload encryption for sensitive data, dependency injection of clients (DB, Stripe, etc.) into function handlers, custom telemetry, or behavior that should apply uniformly across many functions. Covers Inngest middleware lifecycle, creating custom middleware, dependencyInjectionMiddleware, @inngest/middleware-encryption, @inngest/middleware-sentry, and custom middleware patterns.
Inngest Middleware
Master Inngest middleware to handle cross-cutting concerns like logging, error tracking, dependency injection, and data transformation. Middleware runs at key points in the function lifecycle, enabling powerful patterns for observability and shared functionality.
These skills are focused on TypeScript. For Python or Go, refer to the Inngest documentation for language-specific guidance. Core concepts apply across all languages.
Note: The middleware system was significantly rewritten in v4. The lifecycle hooks documented here reflect the v4 API. If migrating from v3, consult the migration guide for details on breaking changes.
⚠ For Realtime use the
inngest-realtimeskill, NOT this one. Inngest v3 usedrealtimeMiddleware()from@inngest/realtimeto inject apublisharg into function handlers. v4 ships realtime natively —step.realtime.publishis built-in, no middleware required. Do NOT install@inngest/realtimeon a v4 project (it's a v3-era package and producesTypeError: Cls is not a constructorat runtime). See theinngest-realtimeskill for the v4 pattern.
What is Middleware?
Middleware allows code to run at various points in an Inngest client's lifecycle - during function execution, event sending, and more. Think of middleware as hooks into the Inngest execution pipeline.
When to use middleware:
- 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
```typescript
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 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 };
}
}
};
}
};
}
});
Testing Middleware
Use Inngest's testing utilities (createMockContext, createMockFunction) to unit test middleware behavior.
For complete implementation examples and advanced patterns, see:
Related skills
More from inngest/inngest-skills and the wider catalog.

inngest-setup
Set up Inngest durable execution in TypeScript projects with retry-safe handlers, background jobs, and scheduled tasks.

inngest-steps
Build durable workflows with Inngest steps—handle delays, events, and async work that survive restarts.

inngest-durable-functions
Build fault-tolerant, long-running workflows with automatic retries, event triggers, and durable execution across infrastructure failures.

inngest-events
Design event-driven workflows with Inngest: idempotent event handling, fan-out patterns, and system event monitoring.

insforge
SDK integration for InsForge app features: database, auth, storage, functions, AI, realtime, email, and payments.

insforge-cli
Manage InsForge backend infrastructure, databases, deployments, and cloud services via CLI.