> Markdown version of [Client Middleware](https://vaadin.com/docs/next/hilla/guides/client-middleware). Section index: [llms.txt](https://vaadin.com/docs/next/hilla/llms.txt)

# Client Middleware

The middleware in Hilla is a special TypeScript async callback function that’s executed by the frontend during calls to the backend. It intercepts the request and the response for every call. The middleware has access to the call context information, including the service and method names, the supplied parameters, and other client call options.

Client middleware is an advanced topic and not recommended for most users.

## <a id="when-useful"></a>When Useful

Middleware can be used to process requests and their responses. The typical use cases are:

- performance measurement

- logging the requests

- retrying

- batching

- caching the response

- custom request and response headers and body handling

## <a id="middleware-structure"></a>Middleware Structure

Here is an example of logging middleware, with an explanation of the structure.

my-log-middleware.ts

```typescript
import { Middleware, MiddlewareContext, MiddlewareNext } from '@vaadin/hilla-frontend';

// A middleware is an async function, that receives the `context` and `next`
export const MyLogMiddleware: Middleware = async function(
  context: MiddlewareContext,
  next: MiddlewareNext
) {
  // The context object contains the call arguments. See the `call` method
  // of the `ConnectClient` class for their descriptions.
  const {endpoint, method, params} = context;
  console.log(
    `Sending request to service: ${endpoint} ` +
    `method: ${method} ` +
    `parameters: ${JSON.stringify(params)} `
  );

  // Also, the context contains the `request`, which is a Fetch API `Request`
  // instance to be sent over the network.
  const request: Request = context.request;
  console.log(`${request.method} ${request.url}`);

  // Call the `next` async function to send the request and get the response.
  const response: Response = await next(context);

  // The response is a Fetch API `Response` object.
  console.log(`Received response: ${response.status} ${response.statusText}`);

  // Clone the response if reading the body is necessary
  const responseClone = response.clone();
  console.log(await responseClone.text());

  // A middleware returns a response.
  return response;
}
```

> **Note: Request and Response are Fetch API interfaces**
>
> The Hilla middleware doesn’t invent a new data structure to represent the network request and response, but uses the interfaces declared by the [Fetch API specification](https://fetch.spec.whatwg.org) instead.
>
> See the MDN web docs to learn more about the [Request API](https://developer.mozilla.org/en-US/docs/Web/API/Request) and [Response API](https://developer.mozilla.org/en-US/docs/Web/API/Response).

## <a id="using-middleware-with-a-client"></a>Using Middleware with a Client

To use middleware, when the Hilla TypeScript client is instantiated, include your middleware in the `middlewares` array option:

`connect-client.ts`

```typescript
import { ConnectClient } from '@vaadin/hilla-frontend';
import { MyLogMiddleware } from './my-log-middleware';

const client = new ConnectClient({
  prefix: '/connect',
  middlewares: [MyLogMiddleware]
});

export default client;
```

Alternatively, you can modify the `middlewares` array property on the existing client, for example if you use a generated client:

index.ts

```typescript
import client from 'Frontend/generated/connect-client.default';
import { MyLogMiddleware } from './my-log-middleware';

client.middlewares = [MyLogMiddleware];
```

> **Caution: Modifying middleware at runtime**
>
> If you modify the `middlewares` array, only calls initiated after the modification use the new `middlewares` array. To avoid issues connected with this, it’s better to avoid modifying `middlewares`, or to modify `middlewares` only before the first call.

## <a id="changing-a-request-using-middleware"></a>Changing a Request Using Middleware

To make a low-level modification of the request in middleware, replace the `context.request` with a new Fetch API `Request` instance:

`my-api-dispatcher-middleware.ts`

```typescript
import { Middleware, MiddlewareContext, MiddlewareNext } from '@vaadin/hilla-frontend';

// An example middleware that uses a different server for selected requests
export const MyApiDispatcherMiddleware: Middleware = async function(
  context: MiddlewareContext,
  next: MiddlewareNext
) {
  if (context.endpoint === 'ExternalService') {
    const url = context.request.url.replace(
      'https//my-app.example.com',
      'https://external-endpoint.example.com'
    );
    context.request = new Request(url, context.request);
  }

  return await next(context);
};
```

## <a id="custom-response-using-middleware"></a>Custom Response using Middleware

Middleware can also replace the response by returning a custom `Response` instance:

`my-stub-middleware.ts`

```typescript
import { Middleware, MiddlewareContext, MiddlewareNext } from '@vaadin/hilla-frontend';

// An example middleware that returns an empty response instead of calling the backend service
export const MyStubMiddleware: Middleware = async function(
  context: MiddlewareContext,
  next: MiddlewareNext
) {
  if (context.endpoint === 'StubService') {
    //
    return new Response('{}');
  }

  return await next(context);
}
```
