September 4, 20245 minute read
Upload files to S3 with AWS Lambda and AWS API Gateway in TypeScript: A Step-by-Step Guide
author
Yiren Lu@YirenLu
Solutions Engineer

Are you looking to create a serverless solution for uploading JPEG images to Amazon S3 using AWS API Gateway and Lambda? This guide will walk you through creating an API Gateway endpoint that uploads JPEG images to an S3 bucket using AWS Lambda, with the added feature of using a “filename” parameter to name the S3 object. We’ll be using TypeScript to write our Lambda function, ensuring type safety and improved developer experience.

Prerequisites

Before we begin, make sure you have:

  1. An AWS account
  2. AWS CLI installed and configured
  3. Node.js and npm installed
  4. TypeScript installed globally (npm install -g typescript)
  5. AWS SAM CLI installed

Step 1: Set Up the Project

First, let’s create a new directory for our project and initialize it:

mkdir aws-image-upload
cd aws-image-upload
npm init -y
npm install aws-sdk @types/aws-lambda @types/node typescript
npm install --save-dev @types/aws-sdk

Create a tsconfig.json file in the root directory:

{
  "compilerOptions": {
    "target": "es2018",
    "module": "commonjs",
    "strict": true,
    "esModuleInterop": true,
    "outDir": "./dist"
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}

Step 2: Create the Lambda Function

Create a new file src/index.ts and add the following code:

import { APIGatewayProxyHandler } from "aws-lambda";
import { S3 } from "aws-sdk";

const s3 = new S3();

export const handler: APIGatewayProxyHandler = async (event) => {
  if (!event.body || !event.queryStringParameters?.filename) {
    return {
      statusCode: 400,
      body: JSON.stringify({ message: "Missing file or filename parameter" }),
    };
  }

  const filename = event.queryStringParameters.filename;
  const fileContent = Buffer.from(event.body, "base64");

  if (
    !filename.toLowerCase().endsWith(".jpg") &&
    !filename.toLowerCase().endsWith(".jpeg")
  ) {
    return {
      statusCode: 400,
      body: JSON.stringify({ message: "File must be a JPEG image" }),
    };
  }

  const params = {
    Bucket: process.env.S3_BUCKET_NAME!,
    Key: filename,
    Body: fileContent,
    ContentType: "image/jpeg",
  };

  try {
    const result = await s3.upload(params).promise();
    return {
      statusCode: 200,
      body: JSON.stringify({
        message: "File uploaded successfully",
        fileUrl: result.Location,
      }),
    };
  } catch (error) {
    console.error("Error uploading file:", error);
    return {
      statusCode: 500,
      body: JSON.stringify({ message: "Error uploading file" }),
    };
  }
};

This Lambda function does the following:

  1. Checks for the presence of the file content and filename parameter
  2. Verifies that the file is a JPEG image
  3. Uploads the file to S3 using the provided filename
  4. Returns a success message with the file URL or an error message

Step 3: Create the SAM Template

Create a template.yaml file in the root directory:

AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: API Gateway for uploading JPEG images to S3

Resources:
  ImageUploadFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: dist/index.handler
      Runtime: nodejs20.x
      CodeUri: ./
      Environment:
        Variables:
          S3_BUCKET_NAME: !Ref UploadBucket
      Policies:
        - S3CrudPolicy:
            BucketName: !Ref UploadBucket
      Events:
        UploadAPI:
          Type: Api
          Properties:
            Path: /upload
            Method: post

  UploadBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub ${AWS::StackName}-uploads
      AccessControl: Private

Outputs:
  ApiUrl:
    Description: URL of the API endpoint
    Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/upload"

This SAM template sets up:

  1. A Lambda function with the necessary permissions
  2. An S3 bucket for storing the uploaded images
  3. An API Gateway endpoint that triggers the Lambda function

Step 4: Build and Deploy

First, compile your TypeScript code:

npx tsc

Then, use SAM CLI to build and deploy your application:

sam build
sam deploy --guided

Follow the prompts to complete the deployment. Once finished, you’ll receive an API endpoint URL.

Step 5: Test Your API

You can test your new API using cURL. Replace YOUR_API_ENDPOINT with the URL provided after deployment:

curl -X POST \
  'YOUR_API_ENDPOINT?filename=test-image.jpg' \
  --data-binary '@/path/to/your/image.jpg' \
  -H 'Content-Type: image/jpeg'

This command sends a POST request to your API with the image file and the filename parameter.

Conclusion

Congratulations! You’ve successfully created an AWS API Gateway endpoint that uploads JPEG images to an S3 bucket using AWS Lambda and TypeScript. This serverless solution provides a scalable and efficient way to handle image uploads in your applications.

Remember to implement additional security measures such as authentication and input validation based on your specific use case.

Ship your first app in minutes

with $30 / month free compute