Skip to Content

NestJS Integration

This guide explains how to integrate FileZen with NestJS applications using the @filezen/nest package. This package provides a FileZenModule that simplifies configuration and makes the ZenStorage service available for dependency injection throughout your application.

📁 Code Example: See the complete NestJS server example  for a working implementation with interactive test interface.

Installation

Install the FileZen NestJS SDK:

Quick Start

1. Environment Configuration

Set your FileZen API key as an environment variable. This can be done in your local .env file during development or as a system environment variable in production.

.env
FILEZEN_API_KEY=your_api_key_here

The FileZen SDK automatically detects the FILEZEN_API_KEY environment variable for server-side operations, so no additional configuration is needed in your server setup. The keepUploads option defaults to false for server-side usage, so temporary uploads are automatically cleaned up.

2. Import the Module

Import FileZenModule into your root AppModule. The module will automatically detect the FILEZEN_API_KEY from your environment variables.

app.module.ts
import { Module } from '@nestjs/common'; import { FileZenModule } from '@filezen/nest'; @Module({ imports: [ FileZenModule.forRoot(), ], }) export class AppModule {}

Default Controller: The FileZen module includes a built-in controller for generating signed URLs at POST /upload/sign. This controller is enabled by default and can be customized or disabled as needed.

3. Inject ZenStorage

You can now inject the ZenStorage service into any of your services or controllers using the @InjectZenStorage() decorator.

file.service.ts
import { Injectable } from '@nestjs/common'; import { ZenStorage } from '@filezen/js'; import { InjectZenStorage } from '@filezen/nest'; @Injectable() export class FileService { constructor( @InjectZenStorage() private readonly zenStorage: ZenStorage ) {} async uploadFile(file: Buffer, fileName: string) { const result = await this.zenStorage.upload(file, { name: fileName, }); if (result.error) { throw result.error; } return result.file; } }

FileZen Functions

Single File Upload

Upload individual files with error handling.

single_upload.ts
import { Injectable } from '@nestjs/common'; import { ZenStorage } from '@filezen/js'; import { InjectZenStorage } from '@filezen/nest'; @Injectable() export class FileService { constructor( @InjectZenStorage() private readonly zenStorage: ZenStorage ) {} async uploadSingleFile(fileBuffer: Buffer, filename: string, mimeType: string) { const upload = await this.zenStorage.upload(fileBuffer, { name: filename, mimeType: mimeType, }); if (upload.error) { throw new Error(`Upload failed: ${upload.error.message}`); } return { url: upload.file.url, name: upload.file.name, size: upload.file.size, mimeType: upload.file.mimeType, }; } }

Bulk Upload

Upload multiple files concurrently for better performance.

bulk_upload.ts
import { Injectable } from '@nestjs/common'; import { ZenStorage } from '@filezen/js'; import { InjectZenStorage } from '@filezen/nest'; @Injectable() export class FileService { constructor( @InjectZenStorage() private readonly zenStorage: ZenStorage ) {} async uploadMultipleFiles(filesData: Array<{ buffer: Buffer; originalname: string; mimetype: string }>) { const uploadItems = filesData.map(file => ({ source: file.buffer, options: { name: file.originalname, mimeType: file.mimetype, }, })); const uploads = await this.zenStorage.bulkUpload(...uploadItems); // Check for any upload errors const failedUploads = uploads.filter(upload => upload.error); if (failedUploads.length > 0) { const failures = failedUploads.map(upload => ({ name: upload.name || 'unknown', error: upload.error?.message || 'Unknown error' })); throw new Error(`${failedUploads.length} uploads failed`); } return uploads.map(upload => ({ url: upload.file.url, name: upload.file.name, size: upload.file.size, mimeType: upload.file.mimeType, })); } }

Upload from URL

Upload files directly from external URLs.

url_upload.ts
import { Injectable } from '@nestjs/common'; import { ZenStorage } from '@filezen/js'; import { InjectZenStorage } from '@filezen/nest'; @Injectable() export class FileService { constructor( @InjectZenStorage() private readonly zenStorage: ZenStorage ) {} async uploadFromUrl(sourceUrl: string, fileName: string) { const upload = await this.zenStorage.uploadFromUrl(sourceUrl, { name: fileName || 'downloaded-file', }); if (upload.error) { throw new Error(`URL upload failed: ${upload.error.message}`); } return { url: upload.file.url, name: upload.file.name, size: upload.file.size, mimeType: upload.file.mimeType, }; } }

Upload from Base64

Upload files from base64 encoded data.

base64_upload.ts
import { Injectable } from '@nestjs/common'; import { ZenStorage } from '@filezen/js'; import { InjectZenStorage } from '@filezen/nest'; @Injectable() export class FileService { constructor( @InjectZenStorage() private readonly zenStorage: ZenStorage ) {} async uploadFromBase64(base64Data: string, fileName: string, mimeType: string) { const upload = await this.zenStorage.uploadFromBase64(base64Data, { name: fileName || 'base64-file', mimeType: mimeType, }); if (upload.error) { throw new Error(`Base64 upload failed: ${upload.error.message}`); } return { url: upload.file.url, name: upload.file.name, size: upload.file.size, mimeType: upload.file.mimeType, }; } }

Manual Multipart Upload Control

For fine-grained control over multipart uploads, especially for large files or when you need to track progress:

multipart_upload.ts
import { Injectable } from '@nestjs/common'; import { ZenStorage } from '@filezen/js'; import { InjectZenStorage } from '@filezen/nest'; @Injectable() export class FileService { constructor( @InjectZenStorage() private readonly zenStorage: ZenStorage ) {} async manualMultipartUpload(file: Buffer, fileName: string, mimeType: string) { // Start multipart upload session const { id: sessionId } = await this.zenStorage.multipart.start({ fileName: fileName, mimeType: mimeType, totalSize: file.length, uploadMode: 'STREAMING', metadata: { type: 'manual_upload' } }); let uploadedSize = 0; const chunkSize = 5 * 1024 * 1024; // 5MB chunks // Upload file in chunks while (uploadedSize < file.length) { // Create a chunk from the current position const endPos = Math.min(file.length, uploadedSize + chunkSize); const chunk = file.slice(uploadedSize, endPos); // Upload this specific chunk await this.zenStorage.multipart.uploadPart({ sessionId: sessionId, chunk: chunk }); // Update progress uploadedSize = endPos; console.log(`Uploaded ${uploadedSize}/${file.length} bytes`); } // Finalize the multipart upload const result = await this.zenStorage.multipart.finish({ sessionId: sessionId }); return result; } }

Generate Signed URL

Generate secure signed URLs for direct uploads from client applications.

signed_url.ts
import { Injectable } from '@nestjs/common'; import { ZenStorage } from '@filezen/js'; import { InjectZenStorage } from '@filezen/nest'; @Injectable() export class FileService { constructor( @InjectZenStorage() private readonly zenStorage: ZenStorage ) {} generateUploadUrl(fileKey: string, expirationTime: number = 3600) { const signedUrl = this.zenStorage.generateSignedUrl({ path: '/files/upload', fileKey: fileKey, expiresIn: expirationTime, // 1 hour by default }); return { signedUrl: signedUrl, expiresIn: expirationTime, }; } }

Delete File

Delete files by their URL.

delete_file.ts
import { Injectable } from '@nestjs/common'; import { ZenStorage } from '@filezen/js'; import { InjectZenStorage } from '@filezen/nest'; @Injectable() export class FileService { constructor( @InjectZenStorage() private readonly zenStorage: ZenStorage ) {} async deleteFile(fileUrl: string) { try { await this.zenStorage.deleteByUrl(fileUrl); return { success: true, message: 'File deleted successfully' }; } catch (error) { throw new Error(`Failed to delete file: ${error.message}`); } } }

URL Presigning Controller

The FileZen module includes a built-in controller for generating signed URLs for direct file uploads. This is useful for client-side uploads where you want to bypass your server.

Basic Usage

By default, the controller is enabled and available at POST /upload/sign:

// POST /upload/sign { "path": "/files/upload", "fileKey": "my-file.jpg", "expiresIn": 3600 // optional, defaults to 1 hour } // Response { "url": "https://api.filezen.dev/files/upload?signature=...&accessKey=...&expires=..." }

Controller Configuration

You can customize the controller behavior when setting up the module:

app.module.ts
import { Module } from '@nestjs/common'; import { FileZenModule } from '@filezen/nest'; @Module({ imports: [ FileZenModule.forRoot({ apiKey: 'your-api-key', // Optional: can use FILEZEN_API_KEY env var controller: { enabled: true, // Enable/disable the controller (default: true) path: 'custom/upload/sign', // Custom path (default: 'upload/sign') } }), ], }) export class AppModule {}

Authentication

You can add authentication to the controller by providing a middleware function:

app.module.ts
import { Module, Injectable } from '@nestjs/common'; import { FileZenModule } from '@filezen/nest'; @Injectable() export class AuthService { async validateRequest(request: any): Promise<boolean> { // Your authentication logic here const token = request.headers.authorization?.replace('Bearer ', ''); if (!token) return false; // Validate token, check user permissions, etc. return await this.validateToken(token); } } @Module({ imports: [ FileZenModule.forRoot({ apiKey: 'your-api-key', // Optional: can use FILEZEN_API_KEY env var controller: { enabled: true, path: 'upload/sign', middleware: (request) => { // You can inject services here if needed return this.authService.validateRequest(request); } } }), ], providers: [AuthService], }) export class AppModule {}

Middleware Behavior: The middleware function should return false to reject the request. If the middleware returns false, the controller will throw a BadRequestException with the message “Middleware not passed”. Any other return value (including true, undefined, null, etc.) will allow the request to proceed.

Disable Controller

If you don’t need the presigning functionality, you can disable it:

app.module.ts
import { Module } from '@nestjs/common'; import { FileZenModule } from '@filezen/nest'; @Module({ imports: [ FileZenModule.forRoot({ apiKey: 'your-api-key', // Optional: can use FILEZEN_API_KEY env var controller: { enabled: false, // Disable the controller } }), ], }) export class AppModule {}

Complete NestJS Server Example

Here’s a complete NestJS server implementation with all FileZen endpoints.

Framework Dependencies: This example uses @nestjs/platform-express for file upload handling. Install it with: npm install @nestjs/platform-express. These are just for the example - you can use any file upload handling method with FileZen.

complete-server.ts
import { Module, Controller, Post, Get, UploadedFile, UploadedFiles, UseInterceptors, Body, Delete } from '@nestjs/common'; import { FileInterceptor, FilesInterceptor } from '@nestjs/platform-express'; import { FileZenModule } from '@filezen/nest'; import { ZenStorage } from '@filezen/js'; import { InjectZenStorage } from '@filezen/nest'; // File Service @Injectable() export class FileService { constructor( @InjectZenStorage() private readonly zenStorage: ZenStorage ) {} async uploadSingleFile(file: Express.Multer.File) { const result = await this.zenStorage.upload(file.buffer, { name: file.originalname, mimeType: file.mimetype, }); if (result.error) { throw result.error; } return result.file; } async uploadMultipleFiles(files: Express.Multer.File[]) { const uploadItems = files.map(file => ({ source: file.buffer, options: { name: file.originalname, mimeType: file.mimetype, }, })); const uploads = await this.zenStorage.bulkUpload(...uploadItems); // Check for any upload errors const failedUploads = uploads.filter(upload => upload.error); if (failedUploads.length > 0) { const failures = failedUploads.map(upload => ({ name: upload.name || 'unknown', error: upload.error?.message || 'Unknown error' })); throw new Error(`${failedUploads.length} uploads failed`); } return uploads.map(upload => ({ url: upload.file.url, name: upload.file.name, size: upload.file.size, mimeType: upload.file.mimeType, })); } async uploadFromUrl(url: string, name?: string) { const result = await this.zenStorage.uploadFromUrl(url, { name: name || 'downloaded-file', }); if (result.error) { throw result.error; } return result.file; } async uploadFromBase64(base64Data: string, name?: string, mimeType?: string) { const result = await this.zenStorage.uploadFromBase64(base64Data, { name: name || 'base64-file', mimeType: mimeType, }); if (result.error) { throw result.error; } return result.file; } async deleteFile(url: string) { await this.zenStorage.deleteByUrl(url); return { success: true, message: 'File deleted successfully' }; } } // Files Controller @Controller('api/files') export class FilesController { constructor(private readonly fileService: FileService) {} @Get() getHealth() { return { message: 'FileZen NestJS Server Running' }; } @Post('upload') @UseInterceptors(FileInterceptor('file')) async uploadFile(@UploadedFile() file: Express.Multer.File) { if (!file) { throw new Error('No file provided'); } return await this.fileService.uploadSingleFile(file); } @Post('bulk-upload') @UseInterceptors(FilesInterceptor('files', 10)) async uploadFiles(@UploadedFiles() files: Express.Multer.File[]) { if (!files || files.length === 0) { throw new Error('No files provided'); } return await this.fileService.uploadMultipleFiles(files); } @Post('upload-from-url') async uploadFromUrl(@Body() body: { url: string; name?: string }) { if (!body.url) { throw new Error('URL is required'); } return await this.fileService.uploadFromUrl(body.url, body.name); } @Post('upload-from-base64') async uploadFromBase64(@Body() body: { base64Data: string; name?: string; mimeType?: string }) { if (!body.base64Data) { throw new Error('Base64 data is required'); } return await this.fileService.uploadFromBase64(body.base64Data, body.name, body.mimeType); } @Delete('delete') async deleteFile(@Body() body: { url: string }) { if (!body.url) { throw new Error('File URL is required'); } return await this.fileService.deleteFile(body.url); } } // App Module @Module({ imports: [ FileZenModule.forRoot({ controller: { enabled: true, // Enable the built-in URL presigning controller path: 'upload/sign', } }), ], controllers: [FilesController], providers: [FileService], }) export class AppModule {}

Configuration Options

Static Configuration

app.module.ts
import { Module } from '@nestjs/common'; import { FileZenModule } from '@filezen/nest'; @Module({ imports: [ FileZenModule.forRoot({ apiKey: 'your-api-key', // Optional: can use FILEZEN_API_KEY env var baseURL: 'https://api.filezen.com', keepUploads: false, // Default for Nest.js (server-side) global: true, // Make the module global controller: { enabled: true, path: 'upload/sign', middleware: (request) => Promise<boolean>, } }), ], }) export class AppModule {}

Async Configuration

app.module.ts
import { Module } from '@nestjs/common'; import { ConfigModule, ConfigService } from '@nestjs/config'; import { FileZenModule } from '@filezen/nest'; @Module({ imports: [ ConfigModule.forRoot(), FileZenModule.forRootAsync({ imports: [ConfigModule], useFactory: (configService: ConfigService) => ({ apiKey: configService.get('FILEZEN_API_KEY'), // Optional: can use FILEZEN_API_KEY env var baseURL: configService.get('FILEZEN_BASE_URL'), keepUploads: false, // Default for Nest.js (server-side) controller: { enabled: true, path: 'upload/sign', middleware: (request) => configService.get('AUTH_ENABLED') ? validateRequest(request) : true, } }), inject: [ConfigService], global: true, }), ], }) export class AppModule {}

Server-Side Optimizations

The Nest.js module automatically sets keepUploads: false by default, which is optimized for server-side usage:

  • Memory Efficiency: Uploads are not stored in memory after completion
  • Stateless Operations: Each upload is independent and doesn’t maintain state
  • Better Performance: Reduces memory footprint in long-running server processes

If you need to track uploads (e.g., for progress monitoring), you can explicitly enable it:

app.module.ts
import { Module } from '@nestjs/common'; import { FileZenModule } from '@filezen/nest'; @Module({ imports: [ FileZenModule.forRoot({ apiKey: 'your-api-key', // Optional: can use FILEZEN_API_KEY env var keepUploads: true, // Enable upload tracking }), ], }) export class AppModule {}
Last updated on
© 2026 FileZen. All rights reserved.