Introduction#
This GitHub shows how to create a simple nextjs app with backend serverless using lambda and api gateway
- Create APIs for generating signed url s3
- Upload and download using the singed url
Lambda Functions#
We need to create a lambda function for generating signed url for downloading file from s3
import { Context, APIGatewayProxyResult, APIGatewayEvent } from "aws-lambda";import { S3Client, GetObjectCommand } from "@aws-sdk/client-s3";import { getSignedUrl } from "@aws-sdk/s3-request-presigner";const s3Client = new S3Client({region: process.env.REGION,});export const handler = async (event: APIGatewayEvent,context: Context): Promise<APIGatewayProxyResult> => {console.log(`Event: ${JSON.stringify(event, null, 2)}`);console.log(`Context: ${JSON.stringify(context, null, 2)}`);// parse argument from contextlet key: string = "";let url: string = "";try {key = (event.queryStringParameters as any).key as string;} catch (error) {}const command = new GetObjectCommand({Bucket: process.env.BUCKET,Key: key,});try {url = await getSignedUrl(s3Client as any, command as any, {expiresIn: 3600,});} catch (error) {}return {statusCode: 200,headers: {"Access-Control-Allow-Origin": "*","Access-Control-Allow-Headers": "Content-Type","Access-Control-Allow-Methods": "OPTIONS,GET",},body: JSON.stringify({signedUrl: url,}),};};
Then similarly, create a lambda function for generating signed url for uplading images to s3
const command = new PutObjectCommand({Bucket: process.env.BUCKET,Key: key,});try {url = await getSignedUrl(s3Client as any, command as any, {expiresIn: 3600,});} catch (error) {}
API Stack#
Create an API Gateway and integrate with two lambda functions. First, create a role for lambda functions
// role for lambdaconst role = new cdk.aws_iam.Role(this, "RoleForLamdbaNext", {assumedBy: new cdk.aws_iam.ServicePrincipal("lambda.amazonaws.com"),});role.addManagedPolicy(cdk.aws_iam.ManagedPolicy.fromAwsManagedPolicyName("service-role/AWSLambdaBasicExecutionRole"));role.addToPolicy(new cdk.aws_iam.PolicyStatement({effect: Effect.ALLOW,resources: ["*"],actions: ["s3:*"],}));
Create the first lambda function in Typscript, which is for generating download signed url
const downloadSignedUrl = new NodejsFunction(this, "GetSignedUrlDownloadFunc", {entry: path.join(__dirname, "./../lambda/handler_download.ts"),handler: "handler",bundling: {externalModules: [],},environment: {REGION: props.region,BUCKET: props.bucket,},role: role,});
Create second lambda function, which is for generating upload signed url
const uploadSignedUrl = new NodejsFunction(this, "GetSignedUrlUploadFunc", {entry: path.join(__dirname, "./../lambda/handler_upload.ts"),handler: "handler",bundling: {externalModules: [],},environment: {REGION: props.region,BUCKET: props.bucket,},role: role,});
Integrate with API Gateway
const apiGw = new cdk.aws_apigateway.RestApi(this, "ApiGatewayNextApp", {restApiName: "ApiGatewayNextApp",});// api gateawy resourcesconst download = apiGw.root.addResource("download");// integrate lambda with api gatewaydownload.addMethod("GET",new cdk.aws_apigateway.LambdaIntegration(downloadSignedUrl));// api gateway resourcesconst upload = apiGw.root.addResource("upload");upload.addMethod("GET",new cdk.aws_apigateway.LambdaIntegration(uploadSignedUrl));
Signed URL S3#
- Download image from signed url
- Upload image from signed url
Let download an image from a signed url
const response = await axios.get(config.API_DOWNLOAD_URL, {params: {key: "pirf/tree.jpg",},});console.log(response.data.signedUrl);
Then let upload image from a singed url using axios
const submitTest = async () => {const fileName = file ? file.name : "";const response = await axios.get(config.API_UPLOAD_URL, {params: {key: "pirf/" + fileName,},});await axios.put(response.data.signedUrl as string, file);const signedDownloadUrl = await axios.get(config.API_DOWNLOAD_URL, {params: {key: "pirf/" + fileName,},});setUrl(signedDownloadUrl.data.signedUrl);return false;};
Finally let upload an image from a signed url using fetch
const submit = async () => {const fileName = file ? file.name : "";const response = await axios.get(config.API_UPLOAD_URL, {params: {key: "pirf/" + fileName,},});const upload = await fetch(response.data.signedUrl as string, {method: "PUT",body: file,});const signedDownloadUrl = await axios.get(config.API_DOWNLOAD_URL, {params: {key: "pirf/" + fileName,},});setUrl(signedDownloadUrl.data.signedUrl);return false;};
S3 CORS#
[{"AllowedHeaders": ["*"],"AllowedMethods": ["GET", "PUT", "POST"],"AllowedOrigins": ["*"],"ExposeHeaders": []}]