Skip to content
Files SDK
Esc
↑↓navigate↵open⌘Jpreview
On this page

NestJS

First-class NestJS integration - a dynamic FilesModule that mounts the gateway, plus InjectFiles() for sharing the Files instance through DI.

files-sdk/nestjs wraps the gateway in an idiomatic NestJS module. FilesModule.forRoot() (or forRootAsync()) configures createFilesRouter, mounts it at a configurable path via Nest’s middleware layer, and exposes the Files instance through dependency injection — inject it anywhere with @InjectFiles(). Both the Express and Fastify platform adapters are supported.

import { Module } from "@nestjs/common";
import { createFiles } from "files-sdk";
import { FilesModule } from "files-sdk/nestjs";
import { r2 } from "files-sdk/r2";

@Module({
  imports: [
    FilesModule.forRoot({
      files: createFiles({ adapter: r2({ bucket: "uploads" }) }),
      path: "/api/files", // the default
      allowedOrigins: ["https://app.example.com"],
      operations: ["upload", "download", "list", "delete", "url"],
      secret: process.env.FILES_API_SECRET,
    }),
  ],
})
export class AppModule {}
import { NestFactory } from "@nestjs/core";
import { AppModule } from "./app.module";

// Express adapter: disable the global body parser (see below).
const app = await NestFactory.create(AppModule, { bodyParser: false });
await app.listen(3000);

CSRF protection and security headers

NestJS 12.1’s opt-in app.enableCsrfProtection() and app.useSecurityHeaders() both run before Nest middleware, so they apply to the gateway too. The CSRF check rejects every cross-origin POST and PUT, so list the same origins you pass to allowedOrigins in its trustedOrigins. The security headers default to Cross-Origin-Resource-Policy: same-origin, which blocks other origins from embedding proxied downloads; relax it if they do.

const app = await NestFactory.create(AppModule, { bodyParser: false });
app.enableCsrfProtection({ trustedOrigins: ["https://app.example.com"] });
// Only if another origin embeds proxied downloads (an <img>, a PDF viewer):
app.useSecurityHeaders({
  crossOriginResourcePolicy: { policy: "cross-origin" },
});
await app.listen(3000);

Async configuration

forRootAsync() follows the standard Nest pattern — imports, inject, and a useFactory that returns the same options:

import { Module } from "@nestjs/common";
import { ConfigModule, ConfigService } from "@nestjs/config";
import { createFiles } from "files-sdk";
import { FilesModule } from "files-sdk/nestjs";
import { r2 } from "files-sdk/r2";

@Module({
  imports: [
    ConfigModule.forRoot(),
    FilesModule.forRootAsync({
      inject: [ConfigService],
      useFactory: (config: ConfigService) => ({
        files: createFiles({
          adapter: r2({ bucket: config.getOrThrow("R2_BUCKET") }),
        }),
        secret: config.getOrThrow("FILES_API_SECRET"),
        allowedOrigins: [config.getOrThrow("APP_ORIGIN")],
        operations: ["upload", "download", "list", "delete", "url"],
      }),
    }),
  ],
})
export class AppModule {}

Injecting the Files instance

The module registers globally by default (global: false to opt out), so any provider can inject the shared instance without importing FilesModule:

import { Injectable } from "@nestjs/common";
import type { Files } from "files-sdk";
import { InjectFiles } from "files-sdk/nestjs";

@Injectable()
export class UploadsService {
  constructor(@InjectFiles() private readonly files: Files) {}

  async uploadAvatar(userId: string, file: Blob) {
    return await this.files.upload(`avatars/${userId}.png`, file, {
      contentType: file.type,
    });
  }
}

The configured gateway router is also available under the FILES_API token for manual wiring or tests.

Options beyond path and global are the gateway options (minus the per-request files factory form — mount files-sdk/express manually for multi-tenant routing), and the endpoint speaks the same protocol as every other binding: point useFiles or createFilesClient at your mount path and lock it down with authorize.

Last updated on

Was this page helpful?