Skip to content

Instantly share code, notes, and snippets.

@felinto-dev
Last active June 29, 2024 15:42
Show Gist options
  • Save felinto-dev/0450a3484ddd8aefefc79f42797eeadf to your computer and use it in GitHub Desktop.
Save felinto-dev/0450a3484ddd8aefefc79f42797eeadf to your computer and use it in GitHub Desktop.
After a lot of headaches with plugins that propose to be a wrapper between nestjs and aws-sdk, I decided to configure the dependency injection by myself and the result was not bad. You can use this code as the basis for any cloud storage S3-compatible. I am using @nestjs/config to manage the settings of environment variables. For additional inst…
import { S3 } from 'aws-sdk';
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { scalewayConfig } from '@/app/config/scaleway.config';
import { StorageService } from './services/storage.service';
@Module({
imports: [ConfigModule.forFeature(scalewayConfig)],
providers: [
StorageService,
{
provide: 'S3',
useFactory: async (configService: ConfigService) =>
new S3({
credentials: {
accessKeyId: configService.get('scaleway.ACCESS_KEY_ID'),
secretAccessKey: configService.get('scaleway.SECRET_ACCESS_KEY'),
},
region: configService.get('scaleway.REGION'),
endpoint: configService.get('scaleway.ENDPOINT'),
signatureVersion: 'v4',
}),
inject: [ConfigService],
},
],
exports: [StorageService],
})
export class StorageModule {}
import * as fs from 'fs';
import * as mockFs from 'mock-fs';
import * as faker from 'faker';
import { Console } from 'console';
import { ConfigModule } from '@nestjs/config';
import { Test, TestingModule } from '@nestjs/testing';
import { s3CloudStorageConfig } from '@/app/config/s3-cloud-storage.config';
import { StorageService } from '../../services/storage.service';
describe('StorageService', () => {
let service: StorageService;
beforeEach(async () => {
// REF: WORKAROUND to be able to use console to show files in the directory (https://github.com/tschaub/mock-fs/issues/234)
global.console = new Console(process.stdout, process.stderr);
mockFs();
const module: TestingModule = await Test.createTestingModule({
imports: [ConfigModule.forFeature(s3CloudStorageConfig)],
providers: [
StorageService,
{
provide: 'S3',
useValue: {
upload: jest.fn(() => ({
promise: jest.fn(() =>
Promise.resolve({
ETag: faker.random.uuid(),
Location: faker.internet.url(),
Key: faker.system.fileName(),
Bucket: 'bucket-id',
}),
),
})),
},
},
],
}).compile();
service = module.get<StorageService>(StorageService);
});
afterAll(() => {
mockFs.restore();
});
it('should be defined', () => {
expect(service).toBeDefined();
});
describe('uploadFile()', () => {
it('should upload a valid file', async () => {
mockFs({
'file.txt': 'dummy content',
});
const mockedFile = fs.readFileSync('file.txt');
await service.uploadFile({
dataBuffer: mockedFile,
filename: 'file.txt',
contentType: 'text/plain',
isPrivate: true,
});
});
});
});
import { Inject, Injectable } from '@nestjs/common';
import { ConfigType } from '@nestjs/config';
import { S3 } from 'aws-sdk';
import { scalewayConfig } from '@/app/config/scaleway.config';
import { FileMetadataAwsObject } from '../interface/file-metadata-aws-object';
@Injectable()
export class StorageService {
constructor(
@Inject(scalewayConfig.KEY)
private readonly scaleway: ConfigType<typeof scalewayConfig>,
@Inject('S3')
private readonly s3: S3,
) {}
async uploadFile(file: FileMetadataAwsObject) {
const fileSaved: S3.ManagedUpload.SendData = await this.s3
.upload({
Bucket: this.scaleway.BUCKET,
Body: file.dataBuffer,
Key: file.filename,
ACL: 'public-read',
ContentType: file.contentType,
})
.promise();
return fileSaved;
}
}
@felinto-dev
Copy link
Author

Some interesting S3-compatible cloud storage solutions:

  • Scaleway: Offers an interesting FREE TIER. With 75GB of disk space and 75GB of bandwidth. More than enough for many projects.
  • Backblaze: Currently the most cost-effective cloud storage. It currently offers a FREE TIER of 10GB of disk space and the first 1 GB of data downloaded each day is free.
  • Digital Ocean Spaces Object Storage and Vultr Block Storage: Both are great options. You need to pay a minimum of $5 a month, even if your consumption is negligible. Depending on the business, it is not a good first choice.

Static files (images, downloads):
For static files and that there is not a lot of cache concern, I suggest you take a look at the "Cloudflare" (check the links below). It can represent considerable savings, as well as greater performance:
https://www.cloudflare.com/bandwidth-alliance/
https://support.cloudflare.com/hc/en-us/articles/360023040812-Best-Practice-Caching-Everything-While-Ignoring-Query-Strings
https://support.cloudflare.com/hc/pt-br/articles/218411427-Understanding-and-Configuring-Cloudflare-Page-Rules-Page-Rules-Tutorial-

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment