Skip to Content

H3 Integration

This guide explains how to integrate FileZen with H3 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.

H3 is a lightweight and composable server framework that works universally across Node.js, Bun, Deno, and edge runtimes.

📁 Code Example: See the complete H3 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.

.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. Initialize ZenStorage

Create a ZenStorage instance with your configuration. The SDK will automatically detect the FILEZEN_API_KEY from your environment variables.

storage.js
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.

upload_example.js
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.

single_upload.js
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.

bulk_upload.js
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.

url_upload.js
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.

base64_upload.js
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:

multipart_upload.js
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.

signed_url.js
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.

delete_file.js
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}`); } }

Error Handling

H3 provides excellent error handling capabilities with structured error system. All endpoints return standardized error responses:

Standard Error Response

{ "statusCode": 400, "statusMessage": "Error description", "data": { "message": "Detailed error message" } }

Upload-Specific Error Handling

The server implements comprehensive error checking for FileZen SDK upload operations:

  1. Single File Upload Errors: Checks upload.error before returning success
  2. Bulk Upload Errors: Validates each upload in the batch and provides detailed failure information
  3. Network Errors: Handles fetch failures for URL uploads
  4. Validation Errors: Returns 400 status for missing required parameters

Bulk Upload Error Response

For bulk uploads, if some files fail while others succeed, the server returns detailed error information:

{ "statusCode": 500, "statusMessage": "Some uploads failed", "data": { "message": "2 out of 5 uploads failed", "failures": [ { "name": "failed-file1.jpg", "error": "File too large" }, { "name": "failed-file2.pdf", "error": "Invalid file type" } ] } }

Complete H3 Server Example

Here’s a complete H3 server implementation with all FileZen endpoints. This example demonstrates how to integrate FileZen with H3’s event-driven architecture, built-in utilities, and error handling.

Why H3?

H3 offers several advantages for FileZen integration:

  • Lightweight: Minimal overhead and fast startup
  • Universal: Works with Node.js, Bun, Deno, and edge runtimes
  • Composable: Event-driven architecture with reusable handlers
  • Modern: Built-in TypeScript support and ESM-first
  • Cross-platform: Designed for multiple JavaScript runtimes
  • Performance: Optimized for speed and efficiency

Framework Dependencies: This example uses H3 and listhen for development. Install them with: npm install h3 listhen. These are just for the example - you can use any server setup method with FileZen.

complete-server.js
import { createApp, createRouter, defineEventHandler, readMultipartFormData, readBody, createError, serveStatic } from 'h3'; import { listen } from 'listhen'; import { ZenStorage } from '@filezen/js'; const app = createApp(); const router = createRouter(); // Initialize FileZen storage const zenStorage = new ZenStorage(); // Serve static files app.use('/public', serveStatic('./public')); // Health check router.get('/', defineEventHandler(async (event) => { return { message: 'FileZen H3 Server Running' }; })); // Single file upload router.post('/api/files/upload', defineEventHandler(async (event) => { try { const formData = await readMultipartFormData(event); if (!formData || formData.length === 0) { throw createError({ statusCode: 400, statusMessage: 'No file provided' }); } const fileItem = formData.find(item => item.name === 'file'); if (!fileItem) { throw createError({ statusCode: 400, statusMessage: 'No file found in form data' }); } const upload = await zenStorage.upload(fileItem.data, { name: fileItem.filename || 'uploaded-file', mimeType: fileItem.type || 'application/octet-stream', }); if (upload.error) { throw createError({ statusCode: 400, statusMessage: 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) { if (error.statusCode) { throw error; } throw createError({ statusCode: 500, statusMessage: 'Internal server error' }); } })); // Bulk file upload router.post('/api/files/bulk-upload', defineEventHandler(async (event) => { try { const formData = await readMultipartFormData(event); if (!formData || formData.length === 0) { throw createError({ statusCode: 400, statusMessage: 'No files provided' }); } const fileItems = formData.filter(item => item.name === 'files'); if (fileItems.length === 0) { throw createError({ statusCode: 400, statusMessage: 'No files found in form data' }); } const uploadItems = fileItems.map(file => ({ source: file.data, options: { name: file.filename || 'uploaded-file', mimeType: file.type || 'application/octet-stream', }, })); 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 createError({ statusCode: 500, statusMessage: 'Some uploads failed', data: { 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) { if (error.statusCode) { throw error; } throw createError({ statusCode: 500, statusMessage: 'Internal server error' }); } })); // Upload from URL router.post('/api/files/upload-from-url', defineEventHandler(async (event) => { try { const { url, name } = await readBody(event); if (!url) { throw createError({ statusCode: 400, statusMessage: 'URL is required' }); } const upload = await zenStorage.uploadFromUrl(url, { name: name || 'downloaded-file', }); if (upload.error) { throw createError({ statusCode: 400, statusMessage: 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) { if (error.statusCode) { throw error; } throw createError({ statusCode: 500, statusMessage: 'Internal server error' }); } })); // Upload from Base64 router.post('/api/files/upload-from-base64', defineEventHandler(async (event) => { try { const { base64Data, name, mimeType } = await readBody(event); if (!base64Data) { throw createError({ statusCode: 400, statusMessage: 'Base64 data is required' }); } const upload = await zenStorage.uploadFromBase64(base64Data, { name: name || 'base64-file', mimeType: mimeType, }); if (upload.error) { throw createError({ statusCode: 400, statusMessage: 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) { if (error.statusCode) { throw error; } throw createError({ statusCode: 500, statusMessage: 'Internal server error' }); } })); // Generate signed URL router.post('/api/files/generate-signed-url', defineEventHandler(async (event) => { try { const { fileKey, expiresIn = 3600 } = await readBody(event); if (!fileKey) { throw createError({ statusCode: 400, statusMessage: 'fileKey is required' }); } const signedUrl = zenStorage.generateSignedUrl({ path: '/files/upload', fileKey: fileKey, expiresIn: expiresIn, }); return { success: true, signedUrl: signedUrl, expiresIn: expiresIn, }; } catch (error) { if (error.statusCode) { throw error; } throw createError({ statusCode: 500, statusMessage: 'Internal server error' }); } })); // Delete file router.delete('/api/files/delete', defineEventHandler(async (event) => { try { const { url } = await readBody(event); if (!url) { throw createError({ statusCode: 400, statusMessage: 'File URL is required' }); } await zenStorage.deleteByUrl(url); return { success: true, message: 'File deleted successfully', }; } catch (error) { if (error.statusCode) { throw error; } throw createError({ statusCode: 500, statusMessage: 'Internal server error' }); } })); // Register router app.use(router); // Start server const port = process.env.PORT || 3003; await listen(app, { port }); console.log(`Server running on http://localhost:${port}`);
Last updated on
© 2026 FileZen. All rights reserved.