@trpc/server
Version:
116 lines (91 loc) • 4.71 kB
Markdown
---
name: caching
description: Set HTTP cache headers on tRPC query responses via the adapter's responseMeta callback (info, ctx, type, errors, eagerGeneration) for CDN and browser caching. Configure Cache-Control, s-maxage, stale-while-revalidate. Handle caching with batching, streaming and authenticated requests. Avoid caching mutations, errors, and authenticated responses.
requires:
- server-setup
sources:
- www/v11/docs/server/caching.md
metadata:
type: core
library: trpc
library_version: 11.23.0
---
<!-- Generated from www/v11/docs/server/caching.md by www/build/skills.ts. Edit the docs instead. -->
# Response Caching
From https://trpc.io/docs/server/caching
Since all tRPC queries are normal HTTP `GET` requests, you can use standard HTTP cache headers to cache responses. This can make responses snappy, give your database a rest, and help scale your API.
> [!NOTE] Info
> Always be careful with caching - especially if you handle personal information.
>
>
> Since batching is enabled by default, it's recommended to set your cache headers in the `responseMeta` function and make sure that there are not any concurrent calls that may include personal data - or to omit cache headers completely if there is an auth header or cookie.
>
>
> You can also use a [`splitLink`](https://trpc.io/docs/client/links/splitLink) to split your public requests and those that should be private and uncached.
## Using `responseMeta` to cache responses
Most tRPC adapters support a `responseMeta` callback that lets you set HTTP headers (including cache headers) based on the procedures being called.
> This works with any hosting provider that supports standard HTTP cache headers (e.g. Vercel, Cloudflare, AWS CloudFront).
```ts no-check title="server.ts"
import { initTRPC } from '/server';
import { createHTTPServer } from '/server/adapters/standalone';
import type { CreateHTTPContextOptions } from '/server/adapters/standalone';
export const createContext = async (opts: CreateHTTPContextOptions) => {
return {
req: opts.req,
res: opts.res,
};
};
type Context = Awaited<ReturnType<typeof createContext>>;
export const t = initTRPC.context<Context>().create();
const waitFor = async (ms: number) =>
new Promise((resolve) => setTimeout(resolve, ms));
export const appRouter = t.router({
public: t.router({
slowQueryCached: t.procedure.query(async (opts) => {
await waitFor(5000); // wait for 5s
return {
lastUpdated: new Date().toJSON(),
};
}),
}),
});
// Exporting `type AppRouter` only exposes types that can be used for inference
// https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#type-only-imports-and-export
export type AppRouter = typeof appRouter;
// export API handler
const server = createHTTPServer({
router: appRouter,
createContext,
responseMeta(opts) {
const { info, errors, type, eagerGeneration } = opts;
// assuming all your public routes are in the `public` router
const allPublic = info?.calls.every((call) =>
call.path.startsWith('public.'),
);
// checking that no procedures errored
// (when streaming, headers are sent before any results are known)
const allOk = !eagerGeneration && errors.length === 0;
// checking we're doing a query request
const isQuery = type === 'query';
if (allPublic && allOk && isQuery) {
// cache request for 1 day + revalidate once every second
const ONE_DAY_IN_SECONDS = 60 * 60 * 24;
return {
headers: new Headers([
[
'cache-control',
`s-maxage=1, stale-while-revalidate=${ONE_DAY_IN_SECONDS}`,
],
]),
};
}
return {};
},
});
server.listen(3000);
```
`responseMeta` runs once per HTTP response, so with batching it decides the headers for every procedure in the batch: only cache if all of them are public. It also gets the request's `ctx`, e.g. to skip caching for signed-in users. For streamed responses, like those of [`httpBatchStreamLink`](https://trpc.io/docs/client/links/httpBatchStreamLink), the headers are sent before any procedure has finished, so `responseMeta` runs with `eagerGeneration: true` and an empty `errors` array.
> [!TIP]
> If you are using the deprecated Next.js Pages Router integration (`/next`), see the [Next.js SSR caching guide](https://trpc.io/docs/client/nextjs/pages-router/ssr#response-caching-with-ssr) for Next.js-specific caching examples using `createTRPCNext` and the Next.js adapter.
## Related skills
- `server-setup` (https://trpc.io/docs/server/overview): `npx @tanstack/intent@latest load @trpc/server#server-setup`