Home/Blog/How do I handle file uploads in a Node.js Express API?

How do I handle file uploads in a Node.js Express API?

To handle file uploads in a Node.js Express API with Uploadcare, receive files via multer middleware, then forward them using the official @uploadcare/upload-client SDK. This requires express, multer, and @uploadcare/upload-client. The full implementation takes 4 steps.

Install Express, multer, and the Uploadcare SDK

Install Express and multer for handling multipart requests, and @uploadcare/upload-client — the official Uploadcare JS SDK for server-side uploads:

npm install express multer @uploadcare/upload-client

Add environment variables to your .env file:

UPLOADCARE_PUBLIC_KEY=your_public_key_here
UPLOADCARE_SECRET_KEY=your_secret_key_here

Upload a file to Uploadcare from Express

The uploadFile function from @uploadcare/upload-client accepts a Buffer and returns file info including the UUID and CDN URL. Wrap it in an Express route that uses multer to parse the incoming multipart request:

const express = require('express');
const multer = require('multer');
const { uploadFile } = require('@uploadcare/upload-client');

const app = express();
const upload = multer({ storage: multer.memoryStorage() });

app.post('/upload', upload.single('file'), async (req, res) => {
  try {
    const file = req.file;

    if (!file) {
      return res.status(400).json({ error: 'No file provided' });
    }

    const result = await uploadFile(file.buffer, {
      publicKey: process.env.UPLOADCARE_PUBLIC_KEY,
      fileName: file.originalname,
      contentType: file.mimetype,
    });

    res.json({
      uuid: result.uuid,
      fileUrl: `https://ucarecdn.com/${result.uuid}/`,
    });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

app.listen(3000, () => console.log('Server running on port 3000'));

This accepts a file via POST, uploads it through the SDK, and returns the file UUID and CDN URL. A public key alone means anyone that knows your endpoint can trigger uploads to your project.

Add signed uploads and file validation

For production, add signed uploads using the SDK’s secureSignature and secureExpire options, plus file validation and proper error handling:

const crypto = require('crypto');
const express = require('express');
const multer = require('multer');
const { uploadFile } = require('@uploadcare/upload-client');

const app = express();
const upload = multer({
  storage: multer.memoryStorage(),
  limits: { fileSize: 100 * 1024 * 1024 }, // 100 MB limit
});

const ALLOWED_TYPES = ['image/jpeg', 'image/png', 'application/pdf'];

function generateSignature() {
  const expire = Math.floor(Date.now() / 1000) + 3600; // 1 hour
  const signature = crypto
    .createHmac('sha256', process.env.UPLOADCARE_SECRET_KEY)
    .update(String(expire))
    .digest('hex');
  return { signature, expire };
}

app.post('/upload', upload.single('file'), async (req, res) => {
  try {
    const file = req.file;
    if (!file) {
      return res.status(400).json({ error: 'No file provided' });
    }

    if (!ALLOWED_TYPES.includes(file.mimetype)) {
      return res.status(400).json({ error: 'File type not allowed' });
    }

    const { signature, expire } = generateSignature();

    const result = await uploadFile(file.buffer, {
      publicKey: process.env.UPLOADCARE_PUBLIC_KEY,
      fileName: file.originalname,
      contentType: file.mimetype,
      secureSignature: signature,
      secureExpire: String(expire),
    });

    res.json({
      uuid: result.uuid,
      fileUrl: `https://ucarecdn.com/${result.uuid}/`,
      timestamp: new Date().toISOString(),
    });
  } catch (error) {
    console.error('Upload error:', error);
    res.status(500).json({
      error: error.message,
      details: process.env.NODE_ENV === 'development' ? error.stack : undefined,
    });
  }
});

app.listen(process.env.PORT || 3000);

This pattern generates HMAC-SHA256 signatures server-side so uploads are cryptographically verified, enforces file type and size restrictions before the upload reaches Uploadcare, and returns structured error responses.

Common errors and troubleshooting

  • Problem: secureExpire must be a string
    Cause: The SDK expects secureExpire as a string, not a number
    Fix: Wrap the Unix timestamp with String(expire) or use a template literal

  • Problem: Signature invalid error
    Cause: The expire value is in milliseconds instead of seconds
    Fix: Divide Date.now() by 1000 before using it

  • Problem: Large file uploads time out
    Cause: For files over 10MB, the SDK automatically switches to multipart upload
    Fix: Ensure your Express timeout and multer fileSize limit are set high enough to accommodate your largest expected file

Next steps

Explore adding webhook listeners to process uploaded files automatically, or secure your frontend uploads with signed tokens. For an alternative approach that skips multer entirely, see how to upload files in a Node.js application using direct uploads. If you need to handle files larger than 100 MB, read about multipart file uploads and the full Upload Client SDK reference.

Further reading

Build file handling in minutesStart for free

Ready to get started?

Join developers who use Uploadcare to build file handling quickly and reliably.

Sign up for free