Building a File Upload Feature Without Backend Code

March 31, 2026 · 7 min read

File uploads are one of those features that seem simple until you start building them. The user picks a file, you send it somewhere, you get a URL. Three steps. But the "somewhere" part traditionally means running a backend server that handles multipart form data, writes files to disk or cloud storage, manages cleanup, and serves those files back to users. That's a lot of infrastructure for what should be a straightforward feature.

If you're building a static site, a single-page app, or working inside a no-code platform, you may not have a backend at all. And even if you do, dedicating server resources to handling file uploads is often overkill for what your app actually needs.

This guide walks through three practical methods for adding file uploads to your project without writing a single line of backend code.

The Backend Problem with File Uploads

Handling file uploads on your own server introduces several responsibilities that have nothing to do with your app's core features:

A file upload API offloads every one of these problems to a dedicated service. You send the file over HTTP, and the API handles storage, security, CDN delivery, and scaling. You get back a URL.

How File Upload APIs Eliminate Backend Code

A file upload API like FilePost exposes a single HTTP endpoint. You send a multipart POST request with the file, and the API returns a JSON response containing a permanent, CDN-backed URL. The flow is:

  1. User selects a file in your UI
  2. Your JavaScript (or no-code tool) sends the file directly to the API
  3. The API stores the file, distributes it to a CDN, and returns the public URL
  4. You use that URL however you need, display it, store it in a database, email it

There's no server-side code sitting between the user and the storage. The API is the backend for your file upload feature.

Method 1: Direct Upload from JavaScript

This is the most flexible approach. You write a small amount of frontend JavaScript that sends the file to the API when the user selects it. Here's a complete, working example:

<!DOCTYPE html>
<html>
<head>
    <title>File Upload Demo</title>
</head>
<body>
    <h1>Upload a File</h1>
    <input type="file" id="fileInput">
    <button onclick="uploadFile()">Upload</button>
    <div id="result"></div>

    <script>
    async function uploadFile() {
        const fileInput = document.getElementById('fileInput');
        const resultDiv = document.getElementById('result');

        if (!fileInput.files[0]) {
            resultDiv.textContent = 'Please select a file first.';
            return;
        }

        const formData = new FormData();
        formData.append('file', fileInput.files[0]);

        resultDiv.textContent = 'Uploading...';

        try {
            const response = await fetch('https://filepost.dev/v1/upload', {
                method: 'POST',
                headers: {
                    'X-API-Key': 'your-api-key'
                },
                body: formData
            });

            const data = await response.json();
            resultDiv.innerHTML =
                'File uploaded! URL: <a href="' + data.url + '">'
                + data.url + '</a>';
        } catch (error) {
            resultDiv.textContent = 'Upload failed: ' + error.message;
        }
    }
    </script>
</body>
</html>

That's the entire thing. No npm packages, no build step, no server. This HTML file works if you open it directly in a browser, host it on GitHub Pages, deploy it to Netlify, or drop it into any static hosting service.

The key parts:

Adding a Progress Indicator

For larger files, users want to see upload progress. You can use XMLHttpRequest instead of fetch to get progress events:

function uploadWithProgress(file, apiKey) {
    return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        const formData = new FormData();
        formData.append('file', file);

        xhr.upload.addEventListener('progress', (e) => {
            if (e.lengthComputable) {
                const percent = Math.round((e.loaded / e.total) * 100);
                console.log(`Upload progress: ${percent}%`);
            }
        });

        xhr.addEventListener('load', () => {
            resolve(JSON.parse(xhr.responseText));
        });

        xhr.addEventListener('error', () => {
            reject(new Error('Upload failed'));
        });

        xhr.open('POST', 'https://filepost.dev/v1/upload');
        xhr.setRequestHeader('X-API-Key', apiKey);
        xhr.send(formData);
    });
}

Method 2: Upload from No-Code Platforms

If you're using a no-code or low-code automation platform, you can add file uploads without writing any code at all. These platforms can send HTTP requests to FilePost's API as part of automated workflows.

Zapier

In Zapier, use the "Webhooks by Zapier" action to send a POST request to https://filepost.dev/v1/upload. Set the X-API-Key header and attach the file from a previous trigger step (like a Gmail attachment or form submission). The response URL can be used in subsequent actions. See our complete Zapier file upload guide for step-by-step instructions.

Make (formerly Integromat)

In Make, use the HTTP module's "Make a request" action. Set the method to POST, the URL to https://filepost.dev/v1/upload, add the X-API-Key header, and set the body type to Multipart/form-data. Map the file from your trigger module to the file field.

n8n

n8n has a dedicated FilePost community node (n8n-nodes-filepost) that makes this even simpler. Install it from the community nodes menu, add your API key as a credential, and use the Upload File operation. No HTTP configuration needed. Alternatively, you can use the HTTP Request node with binary data. Our n8n file upload guide walks through both approaches.

Upload Files Without a Backend

Get a file upload API running in under a minute. Free plan includes 300 uploads/month with 2GB storage and CDN delivery.

Get Your API Key

Method 3: Upload from Static Sites

Static sites, built with tools like Hugo, Jekyll, Eleventy, or even plain HTML, don't have a backend by definition. But that doesn't mean they can't handle file uploads. The JavaScript approach from Method 1 works perfectly on any static site.

Here's a more polished example that you can drop into any static site. It includes drag-and-drop support, file type validation, and a copy-to-clipboard button for the resulting URL:

<div id="dropzone" style="border: 2px dashed #ccc; padding: 40px;
     text-align: center; border-radius: 8px; cursor: pointer;">
    Drop a file here or click to select
    <input type="file" id="fileInput" style="display: none">
</div>
<div id="status"></div>

<script>
const dropzone = document.getElementById('dropzone');
const fileInput = document.getElementById('fileInput');
const status = document.getElementById('status');
const API_KEY = 'your-api-key';
const ALLOWED_TYPES = ['image/png', 'image/jpeg', 'image/gif', 'application/pdf'];

dropzone.addEventListener('click', () => fileInput.click());
dropzone.addEventListener('dragover', (e) => {
    e.preventDefault();
    dropzone.style.borderColor = '#4f46e5';
});
dropzone.addEventListener('dragleave', () => {
    dropzone.style.borderColor = '#ccc';
});
dropzone.addEventListener('drop', (e) => {
    e.preventDefault();
    dropzone.style.borderColor = '#ccc';
    handleFile(e.dataTransfer.files[0]);
});
fileInput.addEventListener('change', () => {
    handleFile(fileInput.files[0]);
});

async function handleFile(file) {
    if (!file) return;

    if (!ALLOWED_TYPES.includes(file.type)) {
        status.textContent = 'File type not allowed.';
        return;
    }

    status.textContent = 'Uploading...';
    const formData = new FormData();
    formData.append('file', file);

    try {
        const res = await fetch('https://filepost.dev/v1/upload', {
            method: 'POST',
            headers: { 'X-API-Key': API_KEY },
            body: formData
        });
        const data = await res.json();
        status.innerHTML = '<a href="' + data.url + '">'
            + data.url + '</a> '
            + '<button onclick="navigator.clipboard.writeText(\''
            + data.url + '\')">Copy</button>';
    } catch (err) {
        status.textContent = 'Upload failed: ' + err.message;
    }
}
</script>

This works on any hosting platform: Netlify, Vercel, GitHub Pages, Cloudflare Pages, or even a simple Nginx server serving static files. The upload happens entirely in the browser, your hosting provider never sees the file.

Security Considerations

Whenever you make API calls from client-side code, you need to think about security. Here are the key considerations when using a file upload API without a backend:

API Key Exposure

If your API key is in client-side JavaScript, anyone can view your page source and see it. This means anyone could use your API key to upload files on your account. There are several ways to mitigate this:

File Validation

Always validate files on the client side before uploading. Check file types (using the type property on the File object), enforce size limits, and provide clear error messages. While server-side validation on FilePost's end protects against actual attacks, client-side checks prevent wasted uploads and improve the user experience.

CORS

FilePost's upload endpoint supports CORS, so browser-based uploads work without any configuration on your part. This is one of the advantages over self-hosted solutions where you'd need to configure CORS headers yourself.

Getting Started

Adding file uploads without backend code takes less than five minutes:

  1. Get an API key. Sign up at FilePost by sending a POST request or visiting the signup page:
    curl -X POST https://filepost.dev/v1/signup \
      -H "Content-Type: application/json" \
      -d '{"email": "you@example.com"}'
  2. Choose your method. Use JavaScript for custom UIs, no-code platforms for automated workflows, or the static site approach for simple HTML pages.
  3. Copy the example code above and replace your-api-key with the key you received.
  4. Deploy. Push to your hosting provider. The upload feature works immediately.

The free plan includes 300 uploads per month, 50MB max file size, 2GB storage, unlimited bandwidth, and CDN delivery. Paid plans include unlimited storage. If you need more uploads, the Starter plan ($9/month) gives you 5,000 uploads with 200MB file size, and the Pro plan ($29/month) gives you 25,000 uploads with 500MB file size.

No backend required.