Skip to content
Blog / The easiest way to add file uploads to your app
6 min

The easiest way to add file uploads to your app

A practical guide to implementing file uploads in your application using Appwrite Storage, covering setup, upload, access control, and image transformations.

Adding file uploads to an application is one of those features that looks simple until you're actually doing it. Choose a storage provider, configure permissions, generate upload URLs, handle chunking for large files, serve files with proper content types, restrict access to authorized users, and avoid storing sensitive files publicly. Each of these is a real problem that needs a real solution.

This post walks through the fastest path from "we need file uploads" to "file uploads are working in production" using Appwrite Storage.

What is Appwrite?

Appwrite is an open-source developer infrastructure platform for building web, mobile, and AI apps. It includes both a backend server, providing authentication, databases, file storage, serverless functions, real-time subscriptions, and messaging, and a fully integrated hosting solution for deploying static and server-side rendered frontends. Appwrite can be fully self-hosted on any Docker-compatible infrastructure or used as a managed service through Appwrite Cloud.

Appwrite Storage is the file management component of the Appwrite platform. It handles the infrastructure concerns of file uploads (chunking large files automatically, enforcing file type and size validation, managing per-user access controls, performing on-the-fly image transformations, and integrating antivirus scanning) so that adding file uploads to your application is a configuration and SDK task rather than a backend engineering project.

What you'll need

  • An Appwrite Cloud account or a self-hosted Appwrite instance
  • The Appwrite SDK for your platform (we'll use JavaScript/Web in this guide)

Setting up a storage bucket

In Appwrite, files are organized into buckets. Each bucket has its own permission settings, file type restrictions, and size limits. Think of a bucket as a folder with configurable access control.

To create a bucket:

  1. Open your Appwrite console and navigate to Storage
  2. Click Create bucket
  3. Give it a name (e.g., "profile-photos")
  4. Set the maximum file size and allowed file extensions appropriate for your use case
  5. Configure the permissions. For user-specific files, you'll typically want users to be able to create files and read their own files

You can also create buckets programmatically using the Appwrite Server SDK:

JavaScript
import { Client, Storage, Permission, Role } from 'node-appwrite';

const client = new Client()
    .setEndpoint('https://<REGION>.cloud.appwrite.io/v1')
    .setProject('<PROJECT_ID>')
    .setKey('<API_KEY>');

const storage = new Storage(client);

const bucket = await storage.createBucket({
    bucketId: 'profile-photos',
    name: 'Profile Photos',
    permissions: [
        Permission.read(Role.users()),
        Permission.create(Role.users()),
        Permission.update(Role.users()),
        Permission.delete(Role.users())
    ],
    fileSecurity: true,
    enabled: true,
    maximumFileSize: 10000000, // 10MB
    allowedFileExtensions: ['jpg', 'jpeg', 'png', 'gif', 'webp']
});

Uploading a file from the browser

With the bucket created, file uploads from the browser use the Appwrite Web SDK:

JavaScript
import { Client, Storage, ID } from 'appwrite';

const client = new Client()
    .setEndpoint('https://<REGION>.cloud.appwrite.io/v1')
    .setProject('<PROJECT_ID>');

const storage = new Storage(client);

async function uploadFile(file) {
    const response = await storage.createFile({
        bucketId: 'profile-photos',
        fileId: ID.unique(),
        file: file
    });
    return response.$id;     // Store this ID to retrieve the file later
}

// Connect to a file input
document.getElementById('file-input').addEventListener('change', async (event) => {
    const file = event.target.files[0];
    if (file) {
        const fileId = await uploadFile(file);
        console.log('Uploaded file ID:', fileId);
    }
});

The createFile method handles chunking for large files automatically. Files up to 5GB are supported with automatic chunked upload handling built into the SDK.

Build fast, scale faster

Backend infrastructure and web hosting built for developers who ship.

  • checkmark icon Start for free
  • checkmark icon Open source
  • checkmark icon Support for over 13 SDKs
  • checkmark icon Managed cloud solution

Retrieving and displaying files

Once uploaded, you can get a file's URL for display using the getFileView method:

JavaScript
const fileUrl = storage.getFileView({ bucketId: 'profile-photos', fileId: fileId });
// Use fileUrl as the src of an img tag or href for download links

For images, Appwrite Storage supports on-the-fly transformations via URL parameters. You can request a resized version of an image without modifying the original:

JavaScript
// Get a 200x200 thumbnail of a profile photo
const thumbnailUrl = storage.getFilePreview({
    bucketId: 'profile-photos',
    fileId: fileId,
    width: 200,
    height: 200,
    gravity: 'center',
    quality: 100
});

Restricting file access

Appwrite's permission system controls who can read, create, update, and delete files. Permissions can be set at the bucket level (applying to all files in the bucket) or at the individual file level.

For user-owned files, set fileSecurity to true on the bucket (which we have already done) and pass file permissions when creating the file:

JavaScript
import { Permission, Role, ID } from 'appwrite';

const response = await storage.createFile({
    bucketId: 'user-documents',
    fileId: ID.unique(),
    file: file,
    permissions: [
        Permission.read(Role.user(currentUserId)),
        Permission.delete(Role.user(currentUserId))
    ]
});

With this configuration, only the user who uploaded the file can view or delete it. Other authenticated users won't be able to access it. Unauthenticated requests will receive a 401 response.

Handling file deletions

Deleting a file is straightforward:

JavaScript
await storage.deleteFile({ bucketId: 'profile-photos', fileId: fileId });

For applications that need to clean up orphaned files (files whose associated database records have been deleted), Appwrite Functions can be used to trigger file deletions on database document delete events.

Implement production-ready file uploads in hours, not days

File uploads don't have to be a week of backend work. Appwrite Storage handles the infrastructure: chunked uploads, access control, image transformations, file validation. You write the feature, not the plumbing.

To go further with Appwrite Storage, explore the documentation for advanced topics like bucket encryption settings, antivirus scanning configuration, and integrating uploads with Appwrite Functions for post-upload processing.

Start building with Appwrite today

Get started