# Responses

> Set headers and status from a resolver, or return a response of your own.

`response` sets the call's headers and status; Effect code uses the `Response` service over the same handle:

```ts
import { Response } from '@trpc12/trpc/server';
import { Effect } from 'effect';

const login = t.procedure.mutation((opts) => {
  opts.response.headers.append('set-cookie', 'session=…; HttpOnly');
  opts.response.status = 201;
});

const list = t.procedure.query(() =>
  Effect.gen(function* () {
    yield* Response.setHeader('cache-control', 'max-age=60');
    return db.post.list();
  }),
);
```

In a batch, headers merge (`set-cookie` appends) and each call keeps its own status. `cache-control` is the strictest policy every call allows: `no-store` or `no-cache` from any call wins, `private` beats `public`, and lifetimes take the shortest, so `max-age=60` and `public, max-age=10` send `max-age=10`. A batch with a call that sets none, an error included, isn't cached. Streamed responses (JSONL, SSE) send `no-cache` unless the call sets its own.

## Returning a response

A procedure can also return its own response, an `HttpServerResponse` from `effect/http` or a web `Response`, for redirects, proxied upstreams or downloads. It's sent as is, with the handle's headers added, and the client's output is the web `Response`:

```ts
const report = t.procedure.query(() =>
  HttpServerResponse.text(toCsv(rows), { contentType: 'text/csv' }),
);
const res = await client.report.query(); // Response
```

Only an unbatched HTTP call can carry one: in a batch, over WebSockets or in-process, the call fails with `BAD_REQUEST`. With `httpLink({ batch: true })`, the client's types require `{ batch: false }` on calls to procedures that may return a response, and the link sends them alone: `client.report.query(undefined, { batch: false })`. A `batch` that may be on, such as a `boolean` variable, counts as on.

## Reading the response on the client

`httpLink` hands each call's response to an `onResponse` call option (`{ status, headers }`). A batched call gets its own line's status and the batch's headers ([Custom links](https://v12.trpc.io/v12/docs/client/custom-links.md#reading-the-response)).
