Start typing to search...
Docs
Guidelines and best practices
Uploading files from the frontend
Search

Uploading files from the frontend

Some templates provide users with the ability to upload files from a Koji’s frontend. For example, a public image board. When developing such a template, the recommended way to upload files is to use the generateSignedUploadRequest method from the @withkoji/database package. This method enables files to be uploaded directly from the Koji’s frontend to the Koji CDN, so that the template doesn’t need to manage file storage and clean up on the backend server.

To upload files with generateSignedUploadRequest, you configure the backend to acquire a presigned URL, which grants temporary access for uploading a file. Then, you configure the frontend to retrieve a presigned URL from the backend and use it to upload a file.

Prerequisites

This guide covers advanced topics. It is suggested that you are familiar with the following topics before proceeding.

  • Familiarity with web development. React and ES6 basics are a plus.

  • Familiarity with the Express.js web framework.

  • Familiarity with the Koji editor and remix process. For an overview, go through the Koji quick-start tutorial.

  • Familiarity with the Koji VCC package. For an overview, see build-custom-vcc.html.

  • Basic understanding of backend communication.

  • Basic understanding of the Koji database. See koji-database.html.

  • Performing HTTP requests (this example uses axios).

Configuring the backend

To upload from the frontend, you first need to acquire a presigned URL by using the Koji database SDK.

In a backend service for your Koji project, set up an Express.js server with a POST method to the /get-upload-url endpoint. From this endpoint, call the following function.

import Database from '@withkoji/database';

const getUploadURL = async (req, res) => {
  try {
    const { filename } = req.body;

    const db = new Database({
      projectId: res.locals.KOJI_PROJECT_ID,
      projectToken: res.locals.KOJI_PROJECT_TOKEN,
    });

    const data = await db.generateSignedUploadRequest(filename);

    res.status(200).json({ data });
  } catch (err) {
    console.log('err', err);
    res.status(500).json({ err: err.toString() });
  }
};

export default getUploadURL;

Configuring the frontend

From your frontend service, call the /get-upload-url endpoint to retrieve the presigned URL. For more information on the response value, see Signed request response.

const { data: { data: { signedRequest, url } } = {} } = await axios({
  method: 'post',
  url: `${serviceMap.backend}/get-upload-url`,
  data: {
    filename: file.name,
  },
  headers: {
    'Content-Type': 'application/json',
  },
});

Create form data and include the presigned URL data and the file object to be uploaded.

// Build form data to send to s3
const formData = new FormData();

// Attach each of the request fields to the form
Object.keys(signedRequest.fields).forEach((key) => {
  formData.append(key, signedRequest.fields[key]);
});

// Append file last
formData.append('file', file);

Post this data to the URL, and then check for a successful status (since there is no response data). If the status is 204, you can now use signedRequest.url which will point to your uploaded file.

// Post to s3
const { status } = await axios({
  headers: { 'content-type': 'application/x-www-form-urlencoded' },
  method: 'POST',
  data: formData,
  url: signedRequest.url,
});

if (status === 204) {
  const image = new window.Image()
  image.onload = () => {
    document.body.append(image);
  }
  image.src = signedRequest.url;
}

Signed request response

As the generateSignedUploadRequest method is a wrapper for the presigned upload request in Amazon S3, the return objects are equivalent. The data in the signedRequest property relates to the Amazon Web Services functionality. The response object includes the follow properties:

{
  data: {
    signedRequest: {
      url: string; (1)
      fields: {
        Policy: string; (2)
        X-Amz-Algorithm: string; (3)
        X-Amz-Credential: string; (4)
        X-Amz-Date: string; (5)
        X-Amz-Signature: string; (6)
        bucket: string; (7)
        key: string; (8)
      }
    },
    url: string; (9)
  }
}
  1. data.signedRequest.url – The upload URL that points to the S3 bucket and where the file be sent.

  2. data.signedRequest.fields.Policy – The Base64-encoded security policy for the request.

  3. data.signedRequest.fields.X-Amz-Algorithm – The signing algorithm used.

  4. data.signedRequest.fields.X-Amz-Credential – The Amazon scope credentials used to sign this URL.

  5. data.signedRequest.fields.X-Amz-Date – The ISO8601-formatted date when the URL was signed.

  6. data.signedRequest.fields.X-Amz-Signature – The HMAC-SHA256 hash of the Amazon security policy.

  7. data.signedRequest.fields.bucket – The Amazon S3 bucket name.

  8. data.signedRequest.fields.key – The Amazon S3 object key.

  9. data.url – The destination URL. The file will be accessible via this URL when uploaded.