MinIO Üzerine İnce Bir S3 İstemcisi Sarmalamak
MinIO, S3 protokolüyle uyumlu bir self-hosted object storage. Bu da resmi AWS SDK'sını (@aws-sdk/client-s3) MinIO'ya karşı da kullanabileceğin anlamına geliyor — ama üç ayarı doğru yapman şartıyla.
// apps/api/src/storage/storage.service.ts
this.client = new S3Client({
endpoint: `${protocol}://${hostAndPort}`,
region: 'us-east-1',
forcePathStyle: true,
credentials: {
accessKeyId: this.config.getOrThrow<string>('MINIO_ACCESS_KEY'),
secretAccessKey: this.config.getOrThrow<string>('MINIO_SECRET_KEY'),
},
});
forcePathStyle: true — en kritik satır
AWS S3, varsayılan olarak "virtual-hosted style" URL kullanır (bucket.s3.amazonaws.com). Self-hosted MinIO'da bucket adına göre otomatik subdomain oluşturacak bir DNS/wildcard-certificate altyapısı yok — bu yüzden forcePathStyle: true ile SDK'ya "path style" kullan diyoruz (minio-host/bucket/key). Bu satır unutulursa istekler yanlış bir hostname'e gitmeye çalışır ve sessizce başarısız olur.
region: 'us-east-1' — anlamsız ama zorunlu bir değer
MinIO'nun gerçek bir "AWS region" kavramı yok, ama SDK region parametresi olmadan çalışmayı reddediyor. us-east-1 burada sadece SDK'nın imzalama (signing) algoritmasını tatmin eden bir placeholder — MinIO bu değeri hiç kullanmıyor.
Endpoint'i ortam değişkenlerinden inşa etmek
const host = this.config.getOrThrow<string>('MINIO_ENDPOINT');
const port = this.config.get<string>('MINIO_PORT');
const useSsl = this.config.get<string>('MINIO_USE_SSL') === 'true';
const protocol = useSsl ? 'https' : 'http';
const hostAndPort = port ? `${host}:${port}` : host;
MINIO_PORT bilinçli olarak opsiyonel: yerel geliştirmede MinIO'ya doğrudan bir portla bağlanılıyor, prod'da ise reverse proxy 443/80 arkasında port belirtmeye gerek kalmıyor — aynı kod, iki farklı deployment şeklini env değişkenleriyle karşılıyor.
Servis neden upload/get'ten fazlasını bilmiyor
StorageService sadece iki metod sunuyor: upload(key, body, contentType) ve get(key). Presigned URL üretme, multipart upload, bucket policy yönetimi gibi S3'ün geri kalan yüzeyi bilinçli olarak dışarıda bırakılıyor — bu API'nin gerçek ihtiyacı bu ikisi, SDK'nın tüm yüzeyini sarmalamak (wrap) gereksiz bir soyutlama olurdu.