@kilnjs/storage-s3
v0.1.3
Published
An S3 compatible storage provider for KilnJS: AWS, DigitalOcean Spaces, MinIO or R2
Readme
@kilnjs/storage-s3
An S3 compatible storage provider for KilnJS. The endpoint decides which store, so AWS, DigitalOcean Spaces, MinIO and R2 all work through it.
Install
yarn add @kilnjs/storage @kilnjs/storage-s3// app.config.ts
import { storage } from "@kilnjs/storage";
import { s3 } from "@kilnjs/storage-s3";
export default {
modules: [/* … */ storage({ drivers: [s3()] })],
} satisfies AppConfig;import { Storage } from "@kilnjs/storage";
await Storage.s3.put(body, "avatars/1.png");s3() hands the driver over; storage() installs it. Importing this package
is inert, and naming its type is what merges this provider into
StorageDrivers and its settings into StorageCredentials, so both the name
and what it takes become typed.
It used to register itself on import, so a configuration carried a bare
import "@kilnjs/storage-s3" whose only job was the side effect. That made
the set of drivers a server has depend on what happened to be imported, with
nothing declaring it anywhere.
Name it as the default in app.config.ts and the bare actions use it too:
export default {
storage: {
driver: "s3" satisfies Driver,
credentials: { s3: { bucket: "uploads", region: "eu-central-1" } },
},
} satisfies AppConfig;await Storage.put(body, "avatars/1.png");Settings
Its own, not declared in core's configuration, because core knows nothing about object storage and a server that does not install this package should not be asked for them.
Two ways in, and both work: storage.credentials.s3 in app.config.ts, and
the environment. What the configuration sets wins, field by field, so the
bucket can be declared and the keys left in the environment.
| | | |
| --- | --- | --- |
| accessKey | S3_ACCESS_KEY | required |
| secretKey | S3_SECRET_KEY | required |
| bucket | S3_BUCKET | required |
| endpoint | S3_ENDPOINT | the regional host, with or without a scheme. Unset means AWS, resolved from the region |
| region | S3_REGION | us-east-1 |
| prefix | S3_PREFIX | uploads - the key prefix every object sits under. Applied by every operation, and never part of the path put answers with |
| downloadUrlTtl | S3_DOWNLOAD_URL_TTL | 3600 - seconds a presigned URL stays valid |
| forcePathStyle | S3_FORCE_PATH_STYLE | false. MinIO, and any bucket whose name is not a legal subdomain |
| acl | S3_ACL | private. What every object this writes is written with |
| storageClass | S3_STORAGE_CLASS | the SDK's own list: STANDARD_IA, GLACIER_IR and the rest |
| encryption | S3_ENCRYPTION | AES256 or aws:kms, server side |
| kmsKeyId | S3_KMS_KEY_ID | required with aws:kms, refused without it |
| timeout | S3_TIMEOUT_MS | 10000 per request |
A host with no scheme is read as https://, and one with a scheme is used
as it stands, which is what a MinIO on a development machine needs:
http://localhost:9000 with forcePathStyle: true.
The encryption pair is checked against itself, because S3 does not:
aws:kms with no key is refused on the first upload, days after the deploy,
and a key with AES256 is a key nothing reads. Both are a boot error here.
They are read when the driver is first built, so installing the package costs nothing until something reaches for it. Whatever is still missing names itself:
Object storage is not configured: ✖ Invalid input: expected string → at bucketBeyond the contract
testConnection() lists one object to prove the credentials and the bucket
work, for a health check. It is reachable as Storage.s3.testConnection()
like anything else: the registry reads this provider's methods off the class
it was registered with, so its own are not a special case.
Everything else is the StorageProvider contract.
Development
yarn verify # ts:check, lint, build, exports:check, testThe tests reach no bucket: they cover how the settings are read, and that importing the package registers the driver.
