Skip to content
 

How can I use AWS S3 with Vercel?

Amazon Web Services (AWS) S3 is a popular way to upload and store files. You can use AWS S3 together with Vercel to upload, store, and retrieve objects.

With an Existing S3 Bucket

If you already have an existing AWS S3 bucket configured, you can retrieve (or generate) the access key and secret key for that IAM User. Ensure it has access to AmazonS3FullAccess.

Ensure the access key and secret key are added an Environment Variables inside your Vercel project.

import S3 from 'aws-sdk/clients/s3' const s3 = new S3({ apiVersion: '2006-03-01', accessKeyId: process.env.ACCESS_KEY, secretAccessKey: process.env.SECRET_KEY, })

Example code to connect to the AWS SDK.

Creating a New S3 Bucket

We’ve created an example Next.js application that allows you to upload photos to an S3 bucket. After cloning the repository, the following steps will enable you to create an S3 bucket with the proper permissions using the AWS CDK:

  1. Create a new IAM User:
    1. Choose programmatic access.
    2. Select "Attach existing policies directly"
    3. Add AmazonS3FullAccess.
  2. Save the access key and secret key for the IAM User.
    1. This is used for programmatic access in the API Route.
  3. Install the AWS CLI:
    1. Run aws configure.
    2. Enter your root AWS user access key and secret key.
    3. Enter your default region.
  4. Create an .env.local file similar to .env.example.
    1. Enter your access key and secret key from the IAM user.
    2. Add your S3 bucket name.
  5. Run cdk bootstrap.
  6. Run cdk deploy to create an S3 bucket with an IAM policy.
  7. Run yarn dev to start the Next.js app at localhost:3000.
  8. Choose a .png or .jpg file.
  9. You should see your file successfully uploaded to S3.

Uploading files on the server

You can use Vercel Serverless Functions to upload files to AWS S3 on the server. After creating an instance of the AWS S3 client, you can use upload to create a new object with the given file name and body (based on a stream).

import { createReadStream } from 'fs'; import S3 from 'aws-sdk/clients/s3' import { NextApiRequest, NextApiResponse } from 'next' export default async function handler( req: NextApiRequest, res: NextApiResponse ) { const s3 = new S3({ apiVersion: '2006-03-01', accessKeyId: process.env.ACCESS_KEY, secretAccessKey: process.env.SECRET_KEY, }) await s3.upload({ Bucket: process.env.S3_BUCKET, Key: 'file-name', Body: createReadStream('file-path'), }) res.status(200).json(post) }

An API Route to upload a file to an S3 bucket.

Uploading files in the browser

Alternatively, you can allow file uploads directly from the browser. For example, a user can select a file using an input, which then generates a pre-signed POST from an API Route to allow for secure uploads.

// pages/index.tsx export default function Upload() { return ( <> <p>Upload a .png or .jpg image (max 1MB).</p> <input onChange={uploadPhoto} type="file" accept="image/png, image/jpeg" /> </> ) } const uploadPhoto = async (e: React.ChangeEvent<HTMLInputElement>) => { const file = e.target.files?.[0]! const filename = encodeURIComponent(file.name) const fileType = encodeURIComponent(file.type) const res = await fetch( `/api/upload-url?file=${filename}&fileType=${fileType}` ) const { url, fields } = await res.json() const formData = new FormData() Object.entries({ ...fields, file }).forEach(([key, value]) => { formData.append(key, value as string) }) const upload = await fetch(url, { method: 'POST', body: formData, }) if (upload.ok) { console.log('Uploaded successfully!') } else { console.error('Upload failed.') } }

A React component that uploads files using an input.

The API Route to generate the pre-signed POST is as follows:

// pages/api/index.ts import S3 from 'aws-sdk/clients/s3' import { NextApiRequest, NextApiResponse } from 'next' export default async function handler( req: NextApiRequest, res: NextApiResponse ) { const s3 = new S3({ apiVersion: '2006-03-01', accessKeyId: process.env.ACCESS_KEY, secretAccessKey: process.env.SECRET_KEY, }) const post = await s3.createPresignedPost({ Bucket: process.env.BUCKET_NAME, Fields: { key: req.query.file, 'Content-Type': req.query.fileType, }, Expires: 60, // seconds Conditions: [ ['content-length-range', 0, 1048576], // up to 1 MB ], }) res.status(200).json(post) }

An API Route to generate a pre-signed POST URL.

If you need to support pausing and resuming uploads, as well as other advanced file upload strategies, explore libraries like Evaporate.

Updated June 15th 2022