How to create a presigned url for accessing AWS S3 images using express ?
Tags: aws, express, node, s3
Published: 2022-10-13 12:56:10

## AWS S3

S3 is a storage service where we can store files. But when we want to expose those files to a front end or any other service. The particular object ( file ) would need a permission to be accessed for public. There are a few ways to achieve it. One of the ways to achieve that would be using a presigned URL.

## What is a Presigned URL ?

Below is a snippet from the AWS documentation,

> By default, all S3 objects are private. Only the object owner has permission to access them. However, the object owner can optionally share objects with others by creating a presigned URL, using their own security credentials, to grant time-limited permission to download the objects.

> When you create a presigned URL for your object, you must provide your security credentials and then specify a bucket name, an object key, an HTTP method (GET to download the object), and an expiration date and time. The presigned URLs are valid only for the specified duration. If you created a presigned URL using a temporary token, then the URL expires when the token expires, even if the URL was created with a later expiration time.

> Anyone who receives the presigned URL can then access the object. For example, if you have a video in your bucket and both the bucket and the object are private, you can share the video with others by generating a presigned URL. Because presigned URLs grant access to your Amazon S3 buckets to whoever has the URL, we recommend that you protect them appropriately.

## Generating a presigned URL
Let's create a small service that generates a presigned URL. We will first create a small upload service to upload the object into the bucket. Then we will retrieve that object using a presigned URL.

### Setup

Firstly, you would need a AWS account and you need to create a bucket in S3. 

I have listed the **package.json** dependecies below,

```
"@aws-sdk/client-s3": "^3.186.0",
"@aws-sdk/s3-request-presigner": "^3.186.0",
"cors": "^2.8.5",
"express": "^4.18.1",
"morgan": "^1.10.0",
"multer": "^1.4.5-lts.1",
```

Once we are ready with our dependecies let's create a **index.js** file and write the following code

```javascript
const express = require("express");
const app = express();
const BUCKET_NAME = "YOURBUCKETNAME";
const { getSignedUrl } = require("@aws-sdk/s3-request-presigner");
const { S3Client, GetObjectCommand, ListObjectsCommand, PutObjectCommand } = require("@aws-sdk/client-s3");
app.use(morgan("dev"));
const client = new S3Client({ region: "YOURS3BUCKETREGION" });

app.post("/upload", multer().single("avatar"), async (req, res) => {
const command = new PutObjectCommand({ Bucket: BUCKET_NAME, Body: req.file.buffer, Key: req.file.originalname });
const url = await getSignedUrl(client, command, { expiresIn: 3600 });
const response = await client.send(command);
res.json({ message: "File Uploaded", url: url });
});

app.listen(3001, () => console.log("API is Running is http://localhost:3001"));

```

From the above code, you can see I am creating a POST endpoint _/upload_ which has a middleware which checks for a file input with key as _avatar_. 

Next, I am creating a command to Put an object into S3 with the <u>BUCKET_NAME</u> and a <u>Body</u> and the <u>Key</u>.

Finally, I am using the getSignedUrl function to get the presigned URL for the object and returning it back as a response.

Let's try uploading an image to S3 bucket. 

![Uploading Image to S3 using Thunder Client](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mlgjy2s5phvxskfugb9k.png)

We have successfully uploaded an image to the bucket and we are able to immediately access the image using the presigned url.

Let's Check the S3 bucket


![Verifying S3 bucket that image has been uploaded](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ihbcrvd0vwijnjaozc9k.png)

In the S3 bucket we can try to open the file we will not be able to access it, Click on the object and try to access the external link in the console.


![Object URL Link](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pd4kue0zcmq3k855daae.png)



![Error in access](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pb0k1xpn4id4lkcj4iru.png)


But if access the presigned URL given back to us we will be able to see the image. But only given for a specific time.


## Another Scenario to retrieve the presigned URL

Let's say we have a case where we upload an image into the S3 bucket and we want the retrieve the presigned URL by just getting a single object.

We can use the same pattern like _/upload_ 

```javascript
app.get("/image/:name", async (req, res) => {
	if (req.params.name) {
		const command = new GetObjectCommand({ Bucket: BUCKET_NAME, Key: req.params.name });
		const url = await getSignedUrl(client, command, { expiresIn: 36000 });
		res.json({ url: url });
	} else {
		res.status(404).json({ message: "No Name Found in Key" });
	}
});
```

As long as we are using a command like GetObjectCommand, PutObjectCommand, we can always use that command and use getSignedUrl function.  Key is very important to be given for S3 to identify the object. Key is basically your file name you have uploaded to S3.

We have seen above 2 scenarios on how we can use pre signed URL to retrieve a object from public. Thank you, hope you had fun reading this blog.