DEV Community

Rostislav Jadavan
Rostislav Jadavan

Posted on

Designing file upload endpoint in REST API

When designing a REST API for file uploads, it's important to consider not only the technical implementation but also the user's needs and the type of content they'll be uploading. Also, consider where the uploaded files will be stored. For applications expecting a high volume of user-generated content, it's often best to store files in an object storage service like Amazon S3. This approach not only offloads the heavy lifting from your server but also allows you to leverage a Content Delivery Network (CDN) to serve the files efficiently and quickly to users around the world.

Now, let's dive into three methods to handle file uploads via your REST API.

Method 1: Uploading Files as Base64 Encoded Strings

This is a REST-compliant approach since you are still transmitting JSON. Base64 encoding is a straightforward and widely used method for small file uploads. Despite the 33% overhead in the payload size due to encoding, it can be conveniently utilized for handling files such as icons or user avatars.

const express = require("express");
const app = express();
app.use(express.json({ limit: "50mb" }));

app.post("/upload", (req, res) => {
  const { fileBase64, filename } = req.body;
  const fileBuffer = Buffer.from(fileBase64, "base64");

  const url = saveFile(fileBuffer, filename);

  res.json({ url, filename });
});
Enter fullscreen mode Exit fullscreen mode

Method 2: Handling Multipart/Form-Data Uploads

Alright, if you’re looking to handle the larger files multipart/form-data is your go-to. It's not the textbook REST way, but let's be real, having a separate endpoint that makes uploading a breeze is totally worth it.

const express = require("express");
const fileUpload = require("express-fileupload");
const app = express();

app.use(fileUpload());

app.post("/upload", async (req, res) => {
  let uploadedFile = req.files.myfile;

  try {
    await uploadedFile.mv(uploadPath);
    res.json({
      url: generateUrl(uploadedFile),
      filename: uploadedFile.name,
    });
  } catch (err) {
    res.status(500).send(err.message);
  }
});
Enter fullscreen mode Exit fullscreen mode

Method 3: Streaming Files with Application/Octet-Stream

For high-performance file uploads, especially when handling very large files, using the application/octet-stream content type is efficient as it streams the file directly to the server or storage solution.

const express = require("express");
const fs = require("fs");
const app = express();

app.post("/upload", (req, res) => {
  const filename = req.headers["x-filename"]; // Filename should be sent as a header
  const filePath = `uploads/${filename}`;
  const writeStream = fs.createWriteStream(filePath);

  req.pipe(writeStream);

  req.on("end", () => res.json({ url: generateUrl(filePath), filename }));
  writeStream.on("error", err => res.status(500).json({ error: err.message }));
});
Enter fullscreen mode Exit fullscreen mode

Remember, if you choose to store your files in a service like S3, you'd replace the local file system operations with
the appropriate SDK calls to your storage service provider. Not only does this improve scalability, but it also allows
you to utilize a CDN to distribute the content efficiently.

When it comes to file uploads in a REST API, there isn't a one-size-fits-all approach. The truth is, there's no
absolute "correct" RESTful method to cover every scenario. The key is to select the method that best fits your unique
requirements, not the one that just seems the most technically correct.

Top comments (0)