# Dependency injection

> Give functions a shared, typed database or API client without creating one in each handler.

Provide a typed Prisma client to every function created with one Inngest client. Middleware adds the existing client to each handler's context, so handlers can use it without constructing another connection.

## TypeScript v4 example

Create one client in your application process. Register a class-based middleware on the Inngest client so every function in that app can use it.

```typescript {{ title: "TypeScript" }}
import { Inngest, Middleware } from "inngest";
import { PrismaClient } from "@prisma/client";

const prisma = new PrismaClient();

class PrismaMiddleware extends Middleware.BaseMiddleware {
  id = "prisma";

  transformFunctionInput(args: Middleware.TransformFunctionInputArgs) {
    return {
      ...args,
      ctx: {
        ...args.ctx,
        prisma,
      },
    };
  }
}

export const inngest = new Inngest({
  id: "my-app",
  middleware: [PrismaMiddleware],
});
```

```python {{ title: "Python" }}
import typing

import inngest

# Assumes `Database` is your own database client.
db = Database()

class DatabaseMiddleware(inngest.Middleware):
    async def transform_input(
        self,
        ctx: inngest.Context | inngest.ContextSync,
        function: inngest.Function[typing.Any],
        steps: inngest.StepMemos,
    ) -> None:
        # The context has no typed extension point, so attach the client as
        # an attribute.
        setattr(ctx, "db", db)

def get_db(ctx: inngest.Context | inngest.ContextSync) -> Database:
    """Return the database client added by DatabaseMiddleware."""
    return typing.cast(Database, getattr(ctx, "db"))

inngest_client = inngest.Inngest(
    app_id="my-app",
    middleware=[DatabaseMiddleware],
)
```

```go {{ title: "Go" }}
import (
	"context"

	"github.com/inngest/inngestgo"
	"github.com/inngest/inngestgo/middleware"
)

// Assumes `Database` and `NewDatabase` are your own database client.
var db = NewDatabase()

type dbContextKey struct{}

// DatabaseMiddleware adds the shared database client to each function's
// context.Context.
type DatabaseMiddleware struct {
	middleware.BaseMiddleware
	db *Database
}

func (m *DatabaseMiddleware) TransformInput(
	ctx context.Context,
	call middleware.CallContext,
	input *middleware.TransformableInput,
) {
	input.WithContext(context.WithValue(input.Context(), dbContextKey{}, m.db))
}

// DatabaseFromContext returns the database client added by DatabaseMiddleware.
func DatabaseFromContext(ctx context.Context) *Database {
	d, _ := ctx.Value(dbContextKey{}).(*Database)
	return d
}

func NewClient() (inngestgo.Client, error) {
	return inngestgo.NewClient(inngestgo.ClientOpts{
		AppID: "my-app",
		Middleware: []func() middleware.Middleware{
			func() middleware.Middleware { return &DatabaseMiddleware{db: db} },
		},
	})
}
```

A function created with this client receives a typed `prisma` property:

```typescript {{ title: "TypeScript" }}
export const writeAuditLog = inngest.createFunction(
  { id: "write-audit-log", triggers: { event: "app/user.logged-in" } },
  async ({ event, prisma, step }) => {
    await step.run("write-audit-log", () =>
      prisma.auditLog.create({
        data: { userId: event.data.userId, action: "logged-in" },
      })
    );
  }
);
```

```python {{ title: "Python" }}
import inngest

@inngest_client.create_function(
    fn_id="write-audit-log",
    trigger=inngest.TriggerEvent(event="app/user.logged-in"),
)
async def write_audit_log(ctx: inngest.Context) -> dict[str, str]:
    db = get_db(ctx)

    async def create_audit_log() -> dict[str, str]:
        return await db.audit_log.create(
            user_id=str(ctx.event.data["userId"]),
            action="logged-in",
        )

    return await ctx.step.run("write-audit-log", create_audit_log)
```

```go {{ title: "Go" }}
import (
	"context"

	"github.com/inngest/inngestgo"
	"github.com/inngest/inngestgo/step"
)

type UserLoggedIn struct {
	UserID string `json:"userId"`
}

func WriteAuditLog(client inngestgo.Client) (inngestgo.ServableFunction, error) {
	return inngestgo.CreateFunction(
		client,
		inngestgo.FunctionOpts{ID: "write-audit-log"},
		inngestgo.EventTrigger("app/user.logged-in", nil),
		func(ctx context.Context, input inngestgo.Input[UserLoggedIn]) (any, error) {
			db := DatabaseFromContext(ctx)
			return step.Run(ctx, "write-audit-log", func(ctx context.Context) (any, error) {
				return db.AuditLog.Create(ctx, AuditLog{
					UserID: input.Event.Data.UserID,
					Action: "logged-in",
				})
			})
		},
	)
}
```

The SDK creates a middleware instance for each request. The `prisma` client above belongs to your application process; manage its credentials and connection lifecycle there. Keep the database write inside `step.run()` so Inngest records its successful result and retries the step independently. Register middleware on one function instead when only that function needs the dependency.

## Version and scope

TypeScript v4 uses classes that extend `Middleware.BaseMiddleware`. The earlier `dependencyInjectionMiddleware()` API does not apply to v4 code. Middleware is also available in the Python and Go SDKs; their setup differs from TypeScript. Python middleware attaches the dependency to `ctx` in `transform_input`, and Go middleware adds it to the function's `context.Context` in `TransformInput`.

## Next

- [Middleware](/docs-markdown/durable-execution/guides-and-advanced/middleware) explains where middleware runs.
- [Creating middleware](/docs-markdown/durable-execution/guides-and-advanced/middleware/creating-middleware) shows how to write another v4 middleware.