Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions apps/content/docs/plugins/head-method.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
title: "HEAD Method Plugin"
description: "Use HeadMethodHandlerPlugin to answer HEAD requests with the status and headers of the matching GET procedure, without sending its body."
sidebar:
label: "HEAD Method"
---

## How It Works

A [`HEAD`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Methods/HEAD) request asks for the headers a `GET` request would receive, without the body, so clients, caches, and link checkers can learn whether a resource exists, its type, and its size without downloading it. Like Fastify's [`exposeHeadRoutes`](https://fastify.dev/docs/latest/Reference/Server/#exposeheadroutes), the plugin answers a `HEAD` request that matches no procedure with the matching `GET` procedure:

- The response keeps the status and headers of `GET`, such as `Content-Type`, `Content-Encoding`, and `ETag`, plus `Content-Length` when the size is known without reading a stream.
- The body is discarded: streams are cancelled and [event iterators](/docs/async-iterator-object) end before they start.
- A procedure routed as `HEAD` takes precedence over the `GET` one.
- The request reaches the procedure as `GET`, so plugins guarding `GET`, such as the [GET Method CSRF Protection Plugin](/docs/plugins/get-method-csrf-protection), guard `HEAD` too.

## Setup

```ts
import { OpenAPIHandler } from '@orpc/openapi/fetch'
import { HeadMethodHandlerPlugin } from '@orpc/server/plugins'

const handler = new OpenAPIHandler(router, {
plugins: [
new HeadMethodHandlerPlugin(),
],
})
```

:::info
The `handler` can be any supported oRPC handler, such as [RPCHandler](/docs/rpc/handler), [OpenAPIHandler](/docs/openapi/handler), or a custom one. `RPCHandler` only answers `HEAD` for procedures it allows `GET` for, see [Enabling the GET Method](/docs/rpc/handler#enabling-the-get-method).
:::

:::tip
The `GET` procedure runs in full to produce the headers. When that is expensive, route a cheaper procedure as `HEAD` on the same path, for example one that only looks up the `ETag`.
:::

## Learn More

For implementation details, see the [source code](https://github.com/middleapi/orpc/blob/main/packages/server/src/plugins/head-method.ts).
1 change: 1 addition & 0 deletions apps/content/docs/plugins/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ Plugins add reusable behavior through the `plugins` option. Handler plugins (`..

- [Request Headers](/docs/plugins/request-headers) (server): read headers through `context.reqHeaders`.
- [Response Headers](/docs/plugins/response-headers) (server): set headers and cookies through `context.resHeaders`.
- [HEAD Method](/docs/plugins/head-method) (server): answer `HEAD` requests with the headers of `GET` procedures.
- [Method Override](/docs/plugins/method-override) (server): let HTML forms call `PUT`, `PATCH`, and `DELETE`.
- [Rethrow](/docs/plugins/rethrow) (server): pass errors on to your framework's error handling.

Expand Down
6 changes: 6 additions & 0 deletions packages/node/src/static-file-handler-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,12 @@ export class StaticFileHandlerPlugin<T extends Context> implements StandardHandl
*/
after = ['~tracing']

/**
* Should run outside the HEAD method plugin's `GET` fallback, so files keep answering `HEAD`
* themselves and procedures routed as `GET` still take precedence over them.
*/
before = ['~head-method']

private readonly rootDir: string
/** `rootDir` with a trailing separator, precomputed for the lexical containment check. */
private readonly rootDirPrefix: string
Expand Down
Loading
Loading