Last active
April 10, 2026 08:50
-
-
Save VirtualPirate/bd6949db7b5d0b898639dbe48e89e03c to your computer and use it in GitHub Desktop.
NestJS AWS S3 service wrapper for uploading, deleting, fetching files, and generating signed URLs using AWS SDK v3.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import { PinoLogger } from "nestjs-pino"; | |
| import { Readable } from "stream"; | |
| import { | |
| DeleteObjectCommand, | |
| GetObjectCommand, | |
| PutObjectCommand, | |
| S3Client, | |
| S3ClientConfig, | |
| } from "@aws-sdk/client-s3"; | |
| import { getSignedUrl } from "@aws-sdk/s3-request-presigner"; | |
| import { Injectable } from "@nestjs/common"; | |
| import { ConfigService } from "@nestjs/config"; | |
| import { BaseService } from "./base.client"; | |
| @Injectable() | |
| export class AWSService extends BaseService { | |
| private readonly s3Client: S3Client; | |
| private readonly region = "us-east-1"; | |
| private readonly bucketName = ""; | |
| constructor(private configService: ConfigService, private logger: PinoLogger) { | |
| super(); | |
| this.logger.setContext(AWSService.name); | |
| const s3Config: S3ClientConfig = { | |
| region: this.region, | |
| }; | |
| this.s3Client = new S3Client(s3Config); | |
| this.bucketName = this.configService.getOrThrow("BUCKET_NAME"); | |
| } | |
| init() { | |
| // any methods here | |
| } | |
| /** | |
| * This function take the following parameters and upload the file to amazon s3 bucket | |
| * | |
| * @param key This will be the path of the file inside the bucket | |
| * @param content The file content you want to upload to s3 | |
| * @returns The url of the file that is uploaded | |
| */ | |
| async uploadFile( | |
| key: string, | |
| content: Buffer | Readable | string, | |
| contentType: string, | |
| ): Promise<string> { | |
| this.logger.info(`Uploading file to bucket: ${this.bucketName} in region: ${this.region}`); | |
| const command = new PutObjectCommand({ | |
| Bucket: this.bucketName, | |
| Key: key, | |
| Body: content, | |
| ContentType: contentType, | |
| }); | |
| try { | |
| await this.s3Client.send(command); | |
| const objectUrl = `https://s3.${this.region}.amazonaws.com/${this.bucketName}/${key}`; | |
| this.logger.info(`file uploaded to s3, ${objectUrl}`); | |
| return objectUrl; | |
| } catch (error) { | |
| this.logger.error(`Error uploading file to S3: ${JSON.stringify(error)}`); | |
| throw error; | |
| } | |
| } | |
| async deleteS3Object(key: string) { | |
| const command = new DeleteObjectCommand({ | |
| Bucket: this.bucketName, | |
| Key: key, | |
| }); | |
| try { | |
| await this.s3Client.send(command); | |
| } catch (error) { | |
| this.logger.error(`Error deleting S3 object: ${key}`, error); | |
| throw error; | |
| } | |
| } | |
| async getSignedUrl(key: string, directDownload = false): Promise<string> { | |
| const command = new GetObjectCommand({ | |
| Bucket: this.bucketName, | |
| Key: key, | |
| ...(directDownload ? { ResponseContentType: "application/octet-stream" } : {}), | |
| }); | |
| try { | |
| const url = await getSignedUrl(this.s3Client, command, { expiresIn: 3600 }); // URL expires in 1 hour | |
| this.logger.info(`Generated signed URL for key: ${key}`); | |
| return url; | |
| } catch (error) { | |
| this.logger.error(`Error generating signed URL: ${error}`); | |
| throw error; | |
| } | |
| } | |
| async getObject(key: string) { | |
| const command = new GetObjectCommand({ | |
| Bucket: this.bucketName, | |
| Key: key, | |
| }); | |
| try { | |
| const data = await this.s3Client.send(command); | |
| this.logger.info(`file fetched from s3, key: ${key}`); | |
| // Convert the response stream to a Buffer | |
| const stream = data.Body as Readable; | |
| const chunks: Buffer[] = []; | |
| for await (const chunk of stream) { | |
| chunks.push(chunk); | |
| } | |
| return Buffer.concat(chunks); | |
| } catch (error) { | |
| this.logger.error(error); | |
| throw error; | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment