a collection of lightweight TypeScript packages for AT Protocol, the protocol powering Bluesky
atproto bluesky typescript npm
101
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat(xrpc-server)!: accept HEAD on query routes

HEAD is the spec-defined no-body GET. the runtime (Node adapter, Bun, Deno,
CF Workers) strips the response body per the Fetch API, so the router just
needs to dispatch it to the same handler as GET and return the same
`allow: GET, HEAD` in 405 responses.

Mary 14cacc77 fa028bfd

+29 -2
+6
.changeset/xrpc-server-head-queries.md
··· 1 + --- 2 + '@atcute/xrpc-server': major 3 + --- 4 + 5 + accept `HEAD` requests on query routes. previously returned 405; now dispatches to the same handler 6 + as `GET` and lets the runtime strip the response body per the Fetch API.
+18
packages/servers/xrpc-server/lib/main/router.test.ts
··· 32 32 expect(response.status).toBe(404); 33 33 }); 34 34 35 + it('accepts HEAD requests on query routes', async () => { 36 + const querySchema = v.query('com.example.query', { 37 + params: null, 38 + output: null, 39 + }); 40 + 41 + const mock = vi.fn(); 42 + 43 + const router = new XRPCRouter(); 44 + router.addQuery(querySchema, { handler: mock }); 45 + 46 + const request = new Request('https://example.com/xrpc/com.example.query', { method: 'HEAD' }); 47 + const response = await router.fetch(request); 48 + 49 + expect(response.status).toBe(200); 50 + expect(mock).toHaveBeenCalledOnce(); 51 + }); 52 + 35 53 it('forbids incorrect HTTP method', async () => { 36 54 const querySchema = v.query('com.example.query', { 37 55 params: null,
+5 -2
packages/servers/xrpc-server/lib/main/router.ts
··· 112 112 return this.#handleNotFound(request); 113 113 } 114 114 115 - if (request.method !== route.method) { 115 + // allow HEAD alongside GET; the runtime is responsible for stripping the 116 + // response body per the Fetch API. 117 + const allowed = request.method === route.method || (route.method === 'GET' && request.method === 'HEAD'); 118 + if (!allowed) { 116 119 return Response.json( 117 120 { error: 'InvalidRequest', message: `invalid http method (expected ${route.method})` }, 118 - { status: 405, headers: { allow: `${route.method}` } }, 121 + { status: 405, headers: { allow: route.method === 'GET' ? 'GET, HEAD' : route.method } }, 119 122 ); 120 123 } 121 124