Skip to main content

AWS SDK для S3

Как устроена работа с объектным хранилищем S3 в @lowcode/api и как AWS SDK встроена в архитектуру платформы.

🎯 Зачем эта страница

Задача этой страницы — объяснить:

  • что такое AWS SDK и какие задачи она решает;
  • как настроить подключение к S3 или S3-совместимым хранилищам;
  • как AWS SDK интегрирована в NestJS-модуль @lowcode/api;
  • как правильно использовать StorageService для работы с файлами.

1. Что такое AWS SDK

AWS SDK для JavaScript — это официальная библиотека для работы с сервисами Amazon Web Services из Node.js-приложений.

В проекте используются два пакета:

  1. @aws-sdk/client-s3 — клиент для работы с S3 (загрузка, скачивание, удаление файлов).
  2. @aws-sdk/s3-request-presigner — генерация presigned URL для прямой загрузки/скачивания файлов клиентом.

Основные преимущества:

  • модульная архитектура: подключаются только нужные сервисы (меньший размер бандла);
  • строгая типизация: полная поддержка TypeScript;
  • совместимость с S3-клонами: поддержка MinIO, Yandex Object Storage, и других S3-совместимых хранилищ через кастомный endpoint.

2. Минимальный пример AWS SDK в отрыве от проекта

2.1. Создание S3 клиента

import { S3Client } from '@aws-sdk/client-s3';

const s3Client = new S3Client({
region: 'us-east-1',
credentials: {
accessKeyId: 'YOUR_ACCESS_KEY',
secretAccessKey: 'YOUR_SECRET_KEY',
},
});

2.2. Генерация presigned URL для загрузки

import { PutObjectCommand } from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';

const command = new PutObjectCommand({
Bucket: 'my-bucket',
Key: 'path/to/file.png',
ContentType: 'image/png',
});

const uploadUrl = await getSignedUrl(s3Client, command, { expiresIn: 3600 });
console.log('Upload URL:', uploadUrl);

2.3. Генерация presigned URL для скачивания

import { GetObjectCommand } from '@aws-sdk/client-s3';

const command = new GetObjectCommand({
Bucket: 'my-bucket',
Key: 'path/to/file.png',
});

const downloadUrl = await getSignedUrl(s3Client, command, { expiresIn: 3600 });
console.log('Download URL:', downloadUrl);

2.4. Удаление файла

import { DeleteObjectCommand } from '@aws-sdk/client-s3';

const command = new DeleteObjectCommand({
Bucket: 'my-bucket',
Key: 'path/to/file.png',
});

await s3Client.send(command);
console.log('File deleted');

3. Где AWS SDK используется в @lowcode/api

В lowcode-платформе AWS SDK отвечает за работу с объектным хранилищем для модуля attachments.

Ключевые места:

  • конфигурация: переменные окружения в apps/api/.env;
  • сервис-обёртка: StorageService в apps/api/src/modules/storage/storage.service.ts;
  • использование в сервисах: AttachmentsService для управления файлами.

Архитектурно это выглядит так:

Контроллер → AttachmentsService → StorageService → S3 (через AWS SDK)

Контроллеры не работают с S3 напрямую; они обращаются к AttachmentsService, который использует StorageService для работы с хранилищем.


4. Интеграция AWS SDK с NestJS

4.1. StorageService

В проекте используется обёртка над S3Client для удобной работы с хранилищем.

Полный пример:

// apps/api/src/modules/storage/storage.service.ts
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { S3Client, DeleteObjectCommand } from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
import { PutObjectCommand, GetObjectCommand } from '@aws-sdk/client-s3';

@Injectable()
export class StorageService {
private readonly s3Client: S3Client;
private readonly bucket: string;
private readonly urlExpiration: number;

constructor(private readonly config: ConfigService) {
const region = this.config.get<string>('S3_REGION', 'us-east-1');
const endpoint = this.config.get<string>('S3_ENDPOINT');
const accessKeyId = this.config.get<string>('S3_ACCESS_KEY_ID');
const secretAccessKey = this.config.get<string>('S3_SECRET_ACCESS_KEY');

this.bucket = this.config.get<string>('S3_BUCKET', 'lowcode-attachments');
this.urlExpiration = this.config.get<number>('S3_PRESIGNED_URL_EXPIRATION', 3600);

this.s3Client = new S3Client({
region,
...(endpoint && { endpoint }), // для совместимых с S3 сервисов
credentials: accessKeyId && secretAccessKey
? { accessKeyId, secretAccessKey }
: undefined,
});
}

async generateUploadUrl(key: string, contentType: string): Promise<string> {
const command = new PutObjectCommand({
Bucket: this.bucket,
Key: key,
ContentType: contentType,
});
return getSignedUrl(this.s3Client, command, { expiresIn: this.urlExpiration });
}

async generateDownloadUrl(key: string): Promise<string> {
const command = new GetObjectCommand({
Bucket: this.bucket,
Key: key,
});
return getSignedUrl(this.s3Client, command, { expiresIn: this.urlExpiration });
}

async deleteFile(key: string): Promise<void> {
const command = new DeleteObjectCommand({
Bucket: this.bucket,
Key: key,
});
await this.s3Client.send(command);
}
}

Этот сервис:

  • инициализирует S3Client с настройками из конфигурации;
  • поддерживает кастомный endpoint для S3-совместимых хранилищ;
  • предоставляет методы для генерации presigned URL и удаления файлов.

4.2. StorageModule

Создаётся отдельный модуль, который экспортирует StorageService:

// apps/api/src/modules/storage/storage.module.ts
import { Global, Module } from '@nestjs/common';
import { StorageService } from './storage.service';

@Global()
@Module({
providers: [StorageService],
exports: [StorageService],
})
export class StorageModule {}

@Global() делает модуль доступным во всём приложении без явного импорта в каждый модуль.

4.3. Подключение к AppModule

В apps/api/src/app.module.ts модуль хранилища импортируется один раз:

@Module({
imports: [
StorageModule,
/* другие модули */
],
})
export class AppModule {}

После этого любой сервис может получать StorageService через DI.


5. Как сервисы используют StorageService

Пример использования в AttachmentsService:

// apps/api/src/modules/attachments/attachments.service.ts
import { Injectable } from '@nestjs/common';
import { StorageService } from '../storage/storage.service';

@Injectable()
export class AttachmentsService {
constructor(private readonly storage: StorageService) {}

async generateUploadUrl(clientId: string, projectId: string, filename: string, contentType: string) {
const storageKey = this.storage.generateStorageKey(clientId, projectId, filename);
const uploadUrl = await this.storage.generateUploadUrl(storageKey, contentType);
return { uploadUrl, storageKey };
}

async createS3(clientId: string, dto: CreateS3AttachmentDto) {
const url = await this.storage.generateDownloadUrl(dto.storageKey);
// ... создание записи в БД с url
}

async remove(clientId: string, id: string) {
const attachment = await this.ensureAttachment(clientId, id);
if (attachment.kind === 's3' && attachment.storageKey) {
await this.storage.deleteFile(attachment.storageKey);
}
// ... удаление записи из БД
}
}

Особенности подхода:

  • Сервис получает StorageService через конструктор.
  • Вся работа с S3 инкапсулирована в StorageService.
  • AttachmentsService работает только с высокоуровневыми операциями.

6. Конфигурация окружения

6.1. Переменные окружения

Все настройки S3 задаются через .env файл:

# S3 / Object Storage настройки
S3_REGION=us-east-1
S3_ENDPOINT= # для S3-совместимых сервисов (MinIO, Yandex Object Storage)
S3_BUCKET=lowcode-attachments
S3_ACCESS_KEY_ID=your-access-key
S3_SECRET_ACCESS_KEY=your-secret-key
S3_PRESIGNED_URL_EXPIRATION=3600 # время жизни presigned URL в секундах

6.2. Работа с разными провайдерами

AWS S3:

S3_REGION=us-east-1
S3_ENDPOINT= # оставить пустым для AWS
S3_BUCKET=my-bucket
S3_ACCESS_KEY_ID=AKIA...
S3_SECRET_ACCESS_KEY=...

MinIO (локальная разработка):

S3_REGION=us-east-1
S3_ENDPOINT=http://localhost:9000
S3_BUCKET=lowcode-attachments
S3_ACCESS_KEY_ID=minioadmin
S3_SECRET_ACCESS_KEY=minioadmin

Yandex Object Storage:

S3_REGION=ru-central1
S3_ENDPOINT=https://storage.yandexcloud.net
S3_BUCKET=my-bucket
S3_ACCESS_KEY_ID=...
S3_SECRET_ACCESS_KEY=...

7. Где искать код, связанный с AWS SDK

КомпонентПутьОписание
StorageServiceapps/api/src/modules/storage/storage.service.tsОбёртка над S3Client
StorageModuleapps/api/src/modules/storage/storage.module.tsГлобальный модуль с StorageService
Конфигурацияapps/api/.envПеременные окружения для S3
Использование в APIapps/api/src/modules/attachments/attachments.service.tsРабота с файлами через StorageService

8. Что нужно знать разработчику

Минимальный набор знаний, чтобы уверенно работать с AWS SDK в проекте:

  1. Где хранятся настройки S3 и как их конфигурировать для разных провайдеров.
  2. Как устроен StorageService и какие методы он предоставляет.
  3. Как генерировать presigned URL для загрузки/скачивания файлов.
  4. Как работает flow загрузки файла:
    • Клиент запрашивает presigned URL через POST /attachments/upload-url
    • Клиент загружает файл напрямую в S3 через PUT-запрос по полученному URL
    • Клиент создаёт запись в БД через POST /attachments/s3 с storageKey

Этого достаточно, чтобы:

  • добавлять новую функциональность работы с файлами;
  • переключаться между разными S3-провайдерами;
  • понимать, как файлы проходят путь от клиента до хранилища и обратно.