Fastify Integration
This guide explains how to integrate FileZen with Fastify applications using the @filezen/js package. This package provides a ZenStorage class that simplifies file upload and management operations with comprehensive error handling and automatic multipart upload support.
Fastify offers several advantages over Express including better performance, native TypeScript support, modern plugin system, and built-in structured logging.
📁 Code Example: See the complete Fastify server example for a working implementation with interactive test interface.
Installation
Install the FileZen JavaScript 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.
FILEZEN_API_KEY=your_api_key_hereThe 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. Initialize ZenStorage
Create a ZenStorage instance with your configuration. The SDK will automatically detect the FILEZEN_API_KEY from your environment variables.
import { ZenStorage } from '@filezen/js';
// Initialize storage with automatic environment variable detection
const zenStorage = new ZenStorage({
apiKey: "your_api_key_here", // Optional: can use FILEZEN_API_KEY env var
});3. Upload Files
You can now use the ZenStorage instance to upload files with various methods.
import fs from 'fs';
// Upload a single file from file path
const fileBuffer = fs.readFileSync(filePath);
const upload = await zenStorage.upload(fileBuffer, {
name: 'my-file.jpg',
mimeType: 'image/jpeg',
});
if (upload.error) {
console.error('Upload failed:', upload.error);
} else {
console.log('File uploaded:', upload.file.url);
}FileZen Functions
Single File Upload
Upload individual files with error handling.
import fs from 'fs';
async function uploadSingleFile(filePath, filename, mimeType) {
const fileBuffer = fs.readFileSync(filePath);
const upload = await 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.
async function uploadMultipleFiles(filesData) {
const uploadItems = filesData.map(file => ({
source: file.buffer,
options: {
name: file.filename,
mimeType: file.mimetype,
},
}));
const uploads = await 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.
async function uploadFromUrl(sourceUrl, fileName) {
const upload = await 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.
async function uploadFromBase64(base64Data, fileName, mimeType) {
const upload = await 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:
async function manualMultipartUpload(file, fileName, mimeType) {
// Start multipart upload session
const { id: sessionId } = await zenStorage.multipart.start({
fileName: fileName,
mimeType: mimeType,
totalSize: file.size,
uploadMode: 'STREAMING',
metadata: { type: 'manual_upload' }
});
let uploadedSize = 0;
const chunkSize = 5 * 1024 * 1024; // 5MB chunks
// Upload file in chunks
while (uploadedSize < file.size) {
// Create a chunk from the current position
const chunk = file.slice(
uploadedSize,
Math.min(file.size, uploadedSize + chunkSize)
);
// Upload this specific chunk
await zenStorage.multipart.uploadPart({
sessionId: sessionId,
chunk: chunk
});
// Update progress
uploadedSize = Math.min(file.size, uploadedSize + chunkSize);
console.log(`Uploaded ${uploadedSize}/${file.size} bytes`);
}
// Finalize the multipart upload
const result = await zenStorage.multipart.finish({
sessionId: sessionId
});
return result;
}Generate Signed URL
Generate secure signed URLs for direct uploads from client applications.
function generateUploadUrl(fileKey, expirationTime = 3600) {
const signedUrl = 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.
async function deleteFile(fileUrl) {
try {
await zenStorage.deleteByUrl(fileUrl);
return { success: true, message: 'File deleted successfully' };
} catch (error) {
throw new Error(`Failed to delete file: ${error.message}`);
}
}Complete Fastify Server Example
Here’s a complete Fastify server implementation with all FileZen endpoints. This example demonstrates how to integrate FileZen with Fastify’s plugin system, multipart handling, and built-in logging.
Why Fastify?
Fastify offers several advantages over Express for FileZen integration:
- Performance: Significantly faster than Express with better throughput
- TypeScript: Native TypeScript support with better type safety
- Plugins: Modern plugin system instead of middleware
- Validation: Built-in JSON schema validation
- Logging: Built-in structured logging
- Multipart Handling: Uses
@fastify/multipartinstead ofmulter
import Fastify from 'fastify';
import { ZenStorage } from '@filezen/js';
const fastify = Fastify({
logger: true
});
// Register plugins
await fastify.register(import('@fastify/multipart'), {
limits: {
fileSize: 100 * 1024 * 1024, // 100MB limit
},
});
await fastify.register(import('@fastify/cors'));
await fastify.register(import('@fastify/static'), {
root: './public', // Serve static files
});
// Initialize FileZen storage
const zenStorage = new ZenStorage();
// Health check
fastify.get('/', async (request, reply) => {
return { message: 'FileZen Fastify Server Running' };
});
// Single file upload
fastify.post('/api/files/upload', async (request, reply) => {
try {
const data = await request.file();
if (!data) {
return reply.status(400).send({
success: false,
error: 'No file provided'
});
}
const fileBuffer = await data.toBuffer();
const upload = await zenStorage.upload(fileBuffer, {
name: data.filename,
mimeType: data.mimetype,
});
if (upload.error) {
return reply.status(400).send({
success: false,
error: upload.error.message
});
}
return {
success: true,
file: {
url: upload.file.url,
name: upload.file.name,
size: upload.file.size,
mimeType: upload.file.mimeType,
},
};
} catch (error) {
fastify.log.error('Upload error:', error);
return reply.status(500).send({
success: false,
error: 'Internal server error'
});
}
});
// Bulk file upload
fastify.post('/api/files/bulk-upload', async (request, reply) => {
try {
const files = await request.saveRequestFiles();
if (!files || files.length === 0) {
return reply.status(400).send({
success: false,
error: 'No files provided'
});
}
const uploadItems = files.map(file => ({
source: file.file,
options: {
name: file.filename,
mimeType: file.mimetype,
},
}));
const uploads = await 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'
}));
return reply.status(400).send({
error: 'Some uploads failed',
message: `${failedUploads.length} out of ${uploads.length} uploads failed`,
failures: failures
});
}
const results = uploads.map(upload => ({
url: upload.file.url,
name: upload.file.name,
size: upload.file.size,
mimeType: upload.file.mimeType,
}));
return {
success: true,
files: results,
};
} catch (error) {
fastify.log.error('Bulk upload error:', error);
return reply.status(500).send({
success: false,
error: 'Internal server error'
});
}
});
// Upload from URL
fastify.post('/api/files/upload-from-url', async (request, reply) => {
try {
const { url, name } = request.body;
if (!url) {
return reply.status(400).send({
success: false,
error: 'URL is required'
});
}
const upload = await zenStorage.uploadFromUrl(url, {
name: name || 'downloaded-file',
});
if (upload.error) {
return reply.status(400).send({
success: false,
error: upload.error.message
});
}
return {
success: true,
file: {
url: upload.file.url,
name: upload.file.name,
size: upload.file.size,
mimeType: upload.file.mimeType,
},
};
} catch (error) {
fastify.log.error('URL upload error:', error);
return reply.status(500).send({
success: false,
error: 'Internal server error'
});
}
});
// Upload from Base64
fastify.post('/api/files/upload-from-base64', async (request, reply) => {
try {
const { base64Data, name, mimeType } = request.body;
if (!base64Data) {
return reply.status(400).send({
success: false,
error: 'Base64 data is required'
});
}
const upload = await zenStorage.uploadFromBase64(base64Data, {
name: name || 'base64-file',
mimeType: mimeType,
});
if (upload.error) {
return reply.status(400).send({
success: false,
error: upload.error.message
});
}
return {
success: true,
file: {
url: upload.file.url,
name: upload.file.name,
size: upload.file.size,
mimeType: upload.file.mimeType,
},
};
} catch (error) {
fastify.log.error('Base64 upload error:', error);
return reply.status(500).send({
success: false,
error: 'Internal server error'
});
}
});
// Generate signed URL
fastify.post('/api/files/generate-signed-url', async (request, reply) => {
try {
const { fileKey, expiresIn = 3600 } = request.body;
if (!fileKey) {
return reply.status(400).send({
success: false,
error: 'fileKey is required'
});
}
const signedUrl = zenStorage.generateSignedUrl({
path: '/files/upload',
fileKey: fileKey,
expiresIn: expiresIn,
});
return {
success: true,
signedUrl: signedUrl,
expiresIn: expiresIn,
};
} catch (error) {
fastify.log.error('Signed URL generation error:', error);
return reply.status(500).send({
success: false,
error: 'Internal server error'
});
}
});
// Delete file
fastify.delete('/api/files/delete', async (request, reply) => {
try {
const { url } = request.body;
if (!url) {
return reply.status(400).send({
success: false,
error: 'File URL is required'
});
}
await zenStorage.deleteByUrl(url);
return {
success: true,
message: 'File deleted successfully',
};
} catch (error) {
fastify.log.error('Delete error:', error);
return reply.status(500).send({
success: false,
error: 'Internal server error'
});
}
});
// Start server
const start = async () => {
try {
await fastify.listen({ port: 3002, host: '0.0.0.0' });
console.log('Server running on http://localhost:3002');
} catch (err) {
fastify.log.error(err);
process.exit(1);
}
};
start();