# Using Middleware for Dependency Injection

Inngest Functions running in the same application often need to share common clients instances such as database clients or third-party
libraries.

The following is an example of adding a OpenAI client to all Inngest functions, allowing them immediate access without needing to create the client themselves.

We can use the `dependencyInjectionMiddleware` to add arguments to a
function's input.

Check out the [TypeScript example](?guide=typescript) for a customized middleware.

```ts
import { dependencyInjectionMiddleware } from "inngest";
import OpenAI from 'openai';

const openai = new OpenAI();

const inngest = new Inngest({
  id: 'my-app',
  middleware: [
    dependencyInjectionMiddleware({ openai }),
  ],
});
```

Our Inngest Functions can now access the OpenAI client through the context:

```ts
inngest.createFunction(
 { name: "user-create", triggers: { event: "app/user.create" } },
 async ({ openai }) => {
   const chatCompletion = await openai.chat.completions.create({
     messages: [{ role: "user", content: "Say this is a test" }],
     model: "gpt-3.5-turbo",
   });

   // ...
 },
);

```

> **Callout:** 💡 Types are inferred from middleware outputs, so your Inngest functions will see an appropriately-typed openai property in their input.

Explore other examples in the [TypeScript SDK Middleware examples page](/docs-markdown/reference/typescript/v4/middleware/examples).

### Advanced mutation

When the middleware runs, the types and data within the passed `ctx` are merged on top of the default provided by the library. This means that you can use a few tricks to overwrite data and types safely and more accurately.

For example, here we use a `const` assertion to infer the literal value of our `foo` example above.

```ts
// In middleware
dependencyInjectionMiddleware({
  foo: "bar",
} as const)

// In a function
async ({ event, foo }) => {
  //             ^? (parameter) foo: "bar"
}
```

## Ordering middleware and types

Middleware runs in the order specified when registering it (see [Middleware lifecycle](/docs-markdown/reference/typescript/v4/middleware/lifecycle)), which affects typing too.

When inferring a mutated input or output, the SDK will apply changes from each middleware in sequence, just as it will at runtime. This means that for two middlewares that add a `foo` value to input arguments, the last one to run will be what it seen both in types and at runtime.

Our custom `OpenAIMiddleware` relies on the [`transformFunctionInput` hook](/docs-markdown/reference/typescript/v4/middleware/lifecycle#transformfunctioninput) to mutate the function's context:

```ts
import { Middleware } from "inngest";
import OpenAI from "openai";

const openai = new OpenAI();

class OpenAIMiddleware extends Middleware.BaseMiddleware {
  id = "openai";
  transformFunctionInput(args: Middleware.TransformFunctionInputArgs) {
    return {
      ...args,
      ctx: {
        ...args.ctx,
        // Anything passed via `ctx` will be merged with the function's arguments
        openai,
      },
    };
  }
}
```

Our Inngest Functions can now access the OpenAI client through the context:

```ts
inngest.createFunction(
 { name: "user-create", triggers: { event: "app/user.create" } },
 async ({ openai }) => {
   const chatCompletion = await openai.chat.completions.create({
     messages: [{ role: "user", content: "Say this is a test" }],
     model: "gpt-3.5-turbo",
   });

   // ...
 },
);

```

> **Callout:** 💡 Types are inferred from middleware outputs, so your Inngest functions will see an appropriately-typed openai property in their input.

Explore other examples in the [TypeScript SDK Middleware examples page](/docs-markdown/reference/typescript/v4/middleware/examples).

### Advanced mutation

When `transformFunctionInput()` returns a new `ctx`, the types and data within that returned `ctx` are merged on top of the default provided by the library. This means that you can use a few tricks to overwrite data and types safely and more accurately.

For example, here we use a `const` assertion to infer the literal value of our `foo` example above.

```ts
// In middleware
transformFunctionInput(args: Middleware.TransformFunctionInputArgs) {
  return {
    ...args,
    ctx: {
      ...args.ctx,
      foo: "bar",
    } as const,
  };
}

// In a function
async ({ event, foo }) => {
  //             ^? (parameter) foo: "bar"
}
```

Because the returned `ctx` object and the default are merged together, sometimes good inferred types are overwritten by more generic types from middleware. A common example of this might be when handling event data in middleware.

To get around this, you can provide the data but omit the type by using an `as` type assertion. For example, here we use a type assertion to add `foo` and alter the event data without affecting the type.

```ts
transformFunctionInput(args: Middleware.TransformFunctionInputArgs) {
  const event = await decrypt(args.ctx.event);

  const newCtx = {
    ...args.ctx,
    foo: "bar",
    event,
  };

  return {
    ...args,
    // Don't affect the `event` type
    ctx: newCtx as Omit<typeof newCtx, "event">,
  };
}
```

## Ordering middleware and types

Middleware runs in the order specified when registering it (see [Middleware lifecycle](/docs-markdown/reference/typescript/v4/middleware/lifecycle)), which affects typing too.

When inferring a mutated input or output, the SDK will apply changes from each middleware in sequence, just as it will at runtime. This means that for two middlewares that add a `foo` value to input arguments, the last one to run will be what it seen both in types and at runtime.

Our `OpenAIMiddleware` uses the [`transform_input` hook](/docs-markdown/reference/python/middleware/lifecycle#transform-input) to inject context:

```py
import inngest
from openai import OpenAI

class OpenAIMiddleware(inngest.Middleware):
    def __init__(
        self,
        client: inngest.Inngest,
        raw_request: object,
    ) -> None:
        self.openai = OpenAI(
            # This is the default and can be omitted
            api_key=os.environ.get("OPENAI_API_KEY"),
        )

    def transform_input(
        self,
        ctx: execution_lib.Context,
        function: function.Function,
        steps: step_lib.StepMemos,
    ) -> None:
        ctx.openai = self.openai  # type: ignore

inngest_client = inngest.Inngest(
    app_id="my_app",
    middleware=[OpenAIMiddleware],
)
```

Our Inngest Functions can now access the `openai` client through the context:

```py
    @inngest_client.create_function(
        fn_id="user-create",
        trigger=inngest.TriggerEvent(event="app/user.create"),
    )
    async def fn(ctx: inngest.Context):
        chat_completion = ctx.openai.chat.completions.create(
            messages=[
                {
                    "role": "user",
                    "content": "Say this is a test",
                }
            ],
            model="gpt-3.5-turbo",
        )
```

> **Callout:** Support for Middleware in the Go SDK in planned.