---
title: Fastify
description: Mount the Files gateway on a Fastify server. Hijacks the reply and bridges Fastify's raw IncomingMessage/ServerResponse to the Web Request/Response the gateway speaks.
---

`files-sdk/fastify` mounts a [`createFilesRouter`](/ui/server/gateway) on a Fastify server. Fastify hands you Node `IncomingMessage`/`ServerResponse` under `request.raw`/`reply.raw`, so the binding bridges them to the Web `Request`/`Response` the gateway speaks (the same `Readable.toWeb`/`fromWeb` seam as the Express adapter). `reply.hijack()` cedes the response to the gateway, and a client disconnect is wired through to abort the upstream read on a proxied download.

```ts title="server.ts" lineNumbers
import Fastify from "fastify";
import { createFiles } from "files-sdk";
import { s3 } from "files-sdk/s3";
import { createFilesRouter } from "files-sdk/api";
import { createRouteHandler } from "files-sdk/fastify";

const router = createFilesRouter({
  files: createFiles({ adapter: s3({ bucket: "uploads" }) }),
  allowedOrigins: ["https://app.example.com"],
  authorize: async ({ req }) => {
    /* throw to deny, or return a per-user constraint — see /ui/server/authorization */
  },
});

const app = Fastify();

// Don't let Fastify consume the body — the gateway reads the raw stream itself.
// Dropping the built-in json/text parsers routes every content type through the
// no-op catch-all, leaving the raw body intact.
app.removeAllContentTypeParsers();
app.addContentTypeParser("*", (_req, _payload, done) => done(null));

app.all("/api/files", createRouteHandler(router));

app.listen({ port: 3000 });
```

<Callout type="warn">
  Fastify runs its content-type parsers **before** your handler, and the
  built-in `application/json`/`text/plain` parsers consume the request body the
  gateway needs — both for the JSON verbs and for the proxy/explicit-key `PUT`
  upload. Drop them (`removeAllContentTypeParsers()`) and add the no-op
  catch-all (`addContentTypeParser("*", …)`, as above) so the raw body reaches
  the gateway. If you don't want that app-wide, register the route inside an
  [encapsulated
  plugin](https://fastify.dev/docs/latest/Reference/Encapsulation/) with its own
  parsers.
</Callout>

See the [gateway options](/ui/server/gateway) for the full configuration and the [`authorize`](/ui/server/authorization) model for locking it down.
