Skip to main content

Installation

Install the Filejar client and required dependencies:
npm install filejar fastify @fastify/multipart

Setup

Create Filejar Client

Initialize the Filejar client in your Fastify application:
import Fastify from 'fastify';
import Filejar from 'filejar';

const app = Fastify();

// Initialize Filejar client
const filejar = new Filejar({
  apiKey: process.env.FILEJAR_API_KEY,
});

// Register multipart plugin for file uploads
await app.register(import('@fastify/multipart'), {
  limits: {
    fileSize: 10 * 1024 * 1024, // 10MB limit
  },
});

Single File Upload Endpoint

Create an endpoint to upload a single file:
import { fileRepo } from '@/lib/db-repo';

// Single file upload endpoint
app.post('/api/files/upload', async (request, reply) => {
  try {
    const data = await request.file();

    if (!data) {
      return reply.code(400).send({ error: 'No file uploaded' });
    }

    // Read file buffer
    const buffer = await data.toBuffer();
    
    // Convert buffer to File-like object for uploadFile
    const file = new File([buffer], data.filename, {
      type: data.mimetype,
    });

    // Upload file to Filejar
    // The body parameter is optional - Filejar will automatically use the file's original name
    const result = await filejar.upload.uploadFile([file]);
    // Or with explicit file name:
    // const result = await filejar.upload.uploadFile([file], {
    //   body: [{ file_name: data.filename }],
    // });

    if (!result.response || result.response.length === 0) {
      return reply.code(500).send({ error: 'Failed to upload file' });
    }

    const uploadResult = result.response[0];
    const acknowledged = result.acknowledge[0];
    
    // Check if acknowledgment was successful
    if ('error' in acknowledged) {
      return reply.code(500).send({ error: acknowledged.error });
    }

    // Construct file URL using the key
    const fileUrl = `https://cdn.filejar.dev/${uploadResult.key}`;
    
    // Store file metadata in database
    const savedFile = await fileRepo.create({
      key: uploadResult.key,
      uploadId: uploadResult.upload_id,
      originalName: data.filename,
      contentType: acknowledged.content_type,
      size: acknowledged.size,
      url: fileUrl,
      uploadedBy: request.user?.id, // Assuming you have auth plugin
    });

    return reply.send({
      success: true,
      file: savedFile,
    });
  } catch (error) {
    console.error('Upload error:', error);
    if (error instanceof Filejar.APIError) {
      return reply.code(error.status || 500).send({ 
        error: error.message,
        status: error.status 
      });
    }
    return reply.code(500).send({ error: 'Failed to upload file' });
  }
});

Multiple Files Upload Endpoint

Create an endpoint to upload multiple files:
// Multiple files upload endpoint
app.post('/api/files/upload-multiple', async (request, reply) => {
  try {
    const parts = request.parts();
    const files: File[] = [];

    // Collect all files from multipart form
    for await (const part of parts) {
      if (part.type === 'file') {
        const buffer = await part.toBuffer();
        files.push(
          new File([buffer], part.filename, {
            type: part.mimetype,
          })
        );
      }
    }

    if (files.length === 0) {
      return reply.code(400).send({ error: 'No files uploaded' });
    }

    // Upload all files to Filejar
    // The body parameter is optional - Filejar will automatically use each file's original name
    const result = await filejar.upload.uploadFile(files);
    // Or with explicit file names:
    // const result = await filejar.upload.uploadFile(files, {
    //   body: files.map(file => ({ file_name: file.name })),
    // });

    if (!result.response || result.response.length === 0) {
      return reply.code(500).send({ error: 'Failed to upload files' });
    }

    // Store file metadata in database
    const filePromises = result.response.map(async (uploadResult, index) => {
      const originalFile = files[index];
      const acknowledged = result.acknowledge[index];
      
      // Skip if acknowledgment failed
      if ('error' in acknowledged) {
        console.error(`Failed to acknowledge ${originalFile.name}:`, acknowledged.error);
        return null;
      }

      // Construct file URL using the key
      const fileUrl = `https://cdn.filejar.dev/${uploadResult.key}`;
      
      const savedFile = await fileRepo.create({
        key: uploadResult.key,
        uploadId: uploadResult.upload_id,
        originalName: originalFile.name,
        contentType: acknowledged.content_type,
        size: acknowledged.size,
        url: fileUrl,
        uploadedBy: request.user?.id,
      });

      return savedFile;
    });

    const uploadedFiles = (await Promise.all(filePromises)).filter(file => file !== null);

    return reply.send({
      success: true,
      files: uploadedFiles,
      count: uploadedFiles.length,
    });
  } catch (error) {
    console.error('Upload error:', error);
    if (error instanceof Filejar.APIError) {
      return reply.code(error.status || 500).send({ 
        error: error.message,
        status: error.status 
      });
    }
    return reply.code(500).send({ error: 'Failed to upload files' });
  }
});

Retrieve Files from Database

Create an endpoint to retrieve file information using the stored key:
import { fileRepo } from '@/lib/db-repo';

// Get file by ID
app.get('/api/files/:id', async (request, reply) => {
  try {
    const { id } = request.params as { id: string };

    const file = await fileRepo.findById(id);

    if (!file) {
      return reply.code(404).send({ error: 'File not found' });
    }

    return reply.send(file);
  } catch (error) {
    console.error('Error retrieving file:', error);
    return reply.code(500).send({ error: 'Failed to retrieve file' });
  }
});

// Get all files
app.get('/api/files', async (request, reply) => {
  try {
    const files = await fileRepo.findAll();

    return reply.send({ files });
  } catch (error) {
    console.error('Error retrieving files:', error);
    return reply.code(500).send({ error: 'Failed to retrieve files' });
  }
});

Client-Side Usage

Upload Single File

// Client-side: Upload single file
async function uploadFile(file: File) {
  const formData = new FormData();
  formData.append('file', file);

  const response = await fetch(`${API_BASE_URL}/api/files/upload`, {
    method: 'POST',
    body: formData,
  });

  const data = await response.json();
  return data.file; // Contains id, filejarId, filejarKey, name, url
}

Upload Multiple Files

// Client-side: Upload multiple files
async function uploadFiles(files: File[]) {
  const formData = new FormData();
  files.forEach(file => {
    formData.append('files', file);
  });

  const response = await fetch(`${API_BASE_URL}/api/files/upload-multiple`, {
    method: 'POST',
    body: formData,
  });

  const data = await response.json();
  return data.files; // Array of file objects
}

Retrieve File

Simply use the file key to construct the URL:
// Direct URL access using the file key
const fileUrl = `https://cdn.filejar.dev/${key}`;

// Example: Display file in img tag
<img src={`https://cdn.filejar.dev/${fileKey}`} alt="Uploaded file" />

Complete Example

Here’s a complete Fastify server setup with all endpoints:
import Fastify from 'fastify';
import Filejar from 'filejar';
import { fileRepo } from '@/lib/db-repo';

const app = Fastify();

// Initialize Filejar client
const filejar = new Filejar({
  apiKey: process.env.FILEJAR_API_KEY,
});

// Register multipart plugin for file uploads
await app.register(import('@fastify/multipart'), {
  limits: {
    fileSize: 10 * 1024 * 1024, // 10MB
  },
});

// Single file upload
app.post('/api/files/upload', async (request, reply) => {
  // ... single upload code from above
});

// Multiple files upload
app.post('/api/files/upload-multiple', async (request, reply) => {
  // ... multiple upload code from above
});

// Get file by ID
app.get('/api/files/:id', async (request, reply) => {
  // ... get file code from above
});

// Get all files
app.get('/api/files', async (request, reply) => {
  // ... get all files code from above
});

// Start server
const start = async () => {
  try {
    await app.listen({ port: 3000 });
    console.log('Server running on http://localhost:3000');
  } catch (err) {
    app.log.error(err);
    process.exit(1);
  }
};

start();