@lafken/bucket
v0.16.1
Published
Define S3 buckets using TypeScript decorators - automatic infrastructure generation with Lafken
Downloads
970
Maintainers
Readme
@lafken/bucket
Define and manage Amazon S3 buckets using TypeScript decorators. @lafken/bucket lets you declare bucket configuration — versioning, ACL, lifecycle rules, transfer acceleration, and EventBridge integration — directly on a class. A built-in repository provides type-safe S3 operations at runtime.
Installation
npm install @lafken/bucketGetting Started
Define a bucket class with @Bucket, register it in the BucketResolver, and use createRepository to interact with it:
import { createApp } from '@lafken/main';
import { BucketResolver } from '@lafken/bucket/resolver';
import { Bucket } from '@lafken/bucket/main';
import { createRepository } from '@lafken/bucket/service';
// 1. Define the bucket
@Bucket({ name: 'project-assets', versioned: true })
export class AssetsBucket {}
// 2. Create a repository for runtime operations
export const assetsRepository = createRepository(AssetsBucket);
// 3. Register the bucket in the resolver
createApp({
name: 'my-app',
resolvers: [new BucketResolver([AssetsBucket])],
});Features
Defining a Bucket
Use the @Bucket decorator on a class to declare an S3 bucket. If name is omitted, the class name is used:
import { Bucket } from '@lafken/bucket/main';
@Bucket({
name: 'upload-storage',
versioned: true,
acl: 'private',
forceDestroy: true,
tags: { environment: 'production' },
})
export class UploadBucket {}Bucket Options
| Option | Type | Default | Description |
| --------------------- | --------------------------------------------- | ------------ | --------------------------------------------------------- |
| name | string | class name | S3 bucket name |
| versioned | boolean | false | Enable object versioning |
| acl | 'private' \| 'public-read' \| 'public-read-write' | — | Access control list |
| forceDestroy | boolean | false | Delete all objects when the bucket is destroyed |
| eventBridgeEnabled | boolean | false | Send bucket events to Amazon EventBridge |
| transferAcceleration| boolean | false | Enable CloudFront-based transfer acceleration |
| tracing | boolean | false | Enable AWS X-Ray tracing on repository operations |
| tags | Record<string, string> | — | Tags applied to the bucket resource |
| lifeCycleRules | Record<string, KeyLifeCycleRule> | — | Object lifecycle management rules |
| ref | string | — | Registers the bucket as a named global reference — see Global References |
| outputs | ResourceOutputType<BucketOutputAttributes> | — | Exports bucket attributes to SSM/Terraform — see Outputs |
External Buckets
Set isExternal: true to reference an existing S3 bucket instead of creating one. The framework only reads the bucket by name — it does not manage versioning, ACL, lifecycle rules, or any other configuration:
@Bucket({ name: 'legacy-assets', isExternal: true })
export class LegacyAssetsBucket {}Register it the same way as any other bucket — new BucketResolver([LegacyAssetsBucket]). Options like versioned, lifeCycleRules, eventBridgeEnabled, and outputs don't apply, since the framework doesn't own the resource.
Lifecycle Rules
Define rules to automatically transition or expire objects based on age and size. Each key in lifeCycleRules represents an object prefix filter:
@Bucket({
name: 'log-archive',
lifeCycleRules: {
'logs/': {
condition: {
objectSizeGreaterThan: 1024,
},
expiration: {
days: 90,
},
transitions: [
{ days: 30, storage: 'standard_ia' },
{ days: 60, storage: 'glacier' },
],
},
'tmp/': {
expiration: {
days: 7,
},
},
},
})
export class LogBucket {}Expiration Options
| Option | Type | Description |
| ---------------------------- | --------- | ------------------------------------------------- |
| days | number | Delete objects after this many days |
| date | Date | Delete objects after a specific date |
| expiredObjectDeleteMarker | boolean | Remove expired object delete markers |
[!NOTE] Only one expiration option can be set per rule.
Condition Options
| Option | Type | Description |
| ------------------------ | -------- | --------------------------------------------- |
| objectSizeGreaterThan | number | Apply rule only to objects larger than (bytes) |
| objectSizeLessThan | number | Apply rule only to objects smaller than (bytes)|
Available Storage Classes
| Storage Class | Description |
| ---------------------- | ------------------------------------------------ |
| standard_ia | Infrequent access, lower cost |
| onezone_ia | Single-AZ infrequent access |
| intelligent_tiering | Automatic cost optimization by access patterns |
| glacier | Long-term archive (minutes to hours retrieval) |
| glacier_ir | Instant retrieval archive |
| deep_archive | Lowest cost (12+ hours retrieval) |
EventBridge Integration
Enable eventBridgeEnabled to send bucket events (object created, deleted, etc.) to Amazon EventBridge. Combine with @lafken/event to process these events:
@Bucket({
name: 'document-uploads',
eventBridgeEnabled: true,
})
export class DocumentBucket {}Outputs
Export bucket attributes to SSM Parameter Store or as Terraform outputs via outputs:
@Bucket({
name: 'document-uploads',
outputs: [
{ type: 'ssm', name: '/document-uploads/arn', value: 'arn' },
{ type: 'output', name: 'document_uploads_domain', value: 'bucketDomainName' },
],
})
export class DocumentBucket {}| Attribute | Description |
| --------------------------- | --------------------------------------------------- |
| id | Name of the bucket |
| arn | ARN of the bucket (arn:aws:s3:::bucketname) |
| bucketDomainName | Bucket domain name (bucketname.s3.amazonaws.com) |
| bucketRegionalDomainName | Region-specific bucket domain name |
Global References
Set ref to register the bucket under a name, so other resources can read its attributes (e.g. an ARN dropped into a Lambda's env) without importing the bucket class directly:
@Bucket({ name: 'document-uploads', ref: 'documents' })
export class DocumentBucket {}import { Refs } from '@lafken/common';
lambda: {
env: {
DOCUMENTS_BUCKET_ARN: Refs.resourceValue('bucket::documents', 'arn'),
},
}Repository
createRepository provides a type-safe API for S3 operations at runtime. The bucket name is automatically injected into every command:
import { createRepository } from '@lafken/bucket/service';
export const docsRepository = createRepository(DocumentBucket);Put Object
Upload an object to the bucket:
await docsRepository.putObject({
Key: 'reports/monthly.json',
Body: JSON.stringify({ revenue: 50000 }),
ContentType: 'application/json',
});Get Object
Retrieve an object from the bucket:
const response = await docsRepository.getObject({
Key: 'reports/monthly.json',
});
const body = await response.Body?.transformToString();Delete Object
Remove an object from the bucket:
await docsRepository.deleteObject({
Key: 'reports/old-report.json',
});Copy Object
Copy an object within or across buckets:
await docsRepository.copyObject({
Key: 'archive/monthly.json',
CopySource: 'document-uploads/reports/monthly.json',
});Move Object
Copy an object to a new key and delete the original in a single operation:
await docsRepository.moveObject({
Key: 'processed/monthly.json',
CopySource: 'document-uploads/reports/monthly.json',
});List Objects
List all objects matching a prefix. Pagination is handled automatically:
const result = await docsRepository.listObjects({
Prefix: 'reports/',
});
for (const object of result.Contents) {
console.log(object.Key, object.Size);
}Custom Client
By default every repository shares an S3Client built from the ambient AWS SDK configuration (the region, credentials and endpoint the SDK resolves from the environment). Pass a client to reach a bucket on a different region, account or endpoint, such as a local S3-compatible instance during development:
import { S3Client } from '@aws-sdk/client-s3';
import { createRepository } from '@lafken/bucket/service';
const client = new S3Client({
endpoint: 'http://localhost:9000',
region: 'us-east-1',
forcePathStyle: true,
});
export const docsRepository = createRepository(DocumentBucket, { client });Reuse the same instance across your buckets instead of creating one per repository, so they share a single connection pool.
X-Ray Tracing
Enable tracing in the @Bucket decorator to instrument all repository operations with AWS X-Ray:
@Bucket({
name: 'traced-bucket',
tracing: true,
})
export class TracedBucket {}Global Configuration
The BucketResolver accepts a second argument with global defaults applied to all buckets. Per-bucket options override global ones:
new BucketResolver(
[AssetsBucket, LogBucket],
{
forceDestroy: true,
versioned: true,
tags: { team: 'platform' },
}
);Extending Buckets
Apply advanced CDKTN configuration to a bucket using the extends callback:
new BucketResolver([
{
bucket: AssetsBucket,
extends: ({ bucket, scope }) => {
// Add CORS rules, policies, or any CDKTN construct
},
},
]);