effect-cloudflare-r2-layer
v1.1.60
Published
An effect layer to interact with Cloudware R2 storage service
Readme
effect-cloudflare-r2-layer
An effect layer to interact with Cloudware R2 storage service.
⚡ Quick start
🔶 Install
npm i effect-cloudflare-r2-layer
# or
pnpm i effect-cloudflare-r2-layer
# or
bun i effect-cloudflare-r2-layer🔶 Use the layer
import { FetchHttpClient } from '@effect/platform';
import { Effect, Layer, pipe } from 'effect';
import {
CloudflareR2StorageLayerLive,
FileStorageLayer,
} from 'effect-cloudflare-r2-layer';
const task = pipe(
FileStorageLayer.readAsText('my-bucket', 'some-file.txt'),
Effect.scoped,
Effect.provide(
Layer.mergeAll(CloudflareR2StorageLayerLive, FetchHttpClient.layer)
)
);
/* task is of type
Effect.Effect<
string,
ConfigError | HttpClientError | FileStorageError,
never
>
*/⚡ Env variables
The layer requires the following env variables:
CLOUDFLARE_ACCOUNT_ID=""
R2_DOCUMENTS_ACCESS_KEY_ID=""
R2_DOCUMENTS_SECRET_ACCESS_KEY=""⚡ API
| function | description |
| -------------------------------------- | ----------------------------------------------------------------------------------------- |
| createBucket | Create a bucket |
| bucketInfos | Get bucket infos |
| uploadFile | Adds a file to the specified bucket |
| deleteFile | Removes a file from the specified bucket |
| getFileUrl | Gets a pre-signed url to fetch a ressource by its filename from the specified bucket. |
| readAsJson | Fetches a file, expecting a content extending Record<string, unknown>. |
| readAsText | Fetches a file as a string. |
| readAsRawBinary | Fetches a file as raw binary (ArrayBuffer). |
| fileExists | Checks if a file exists in a bucket |
🔶 createBucket
type createBucket = (
input: CreateBucketCommandInput
) => Effect.Effect<
CreateBucketCommandOutput,
FileStorageError | ConfigError,
FileStorage
>;🧿 Example
import { Effect, pipe } from 'effect';
import {
CloudflareR2StorageLayerLive,
FileStorageLayer,
} from 'effect-cloudflare-r2-layer';
const task = pipe(
Effect.gen(function* () {
const result = yield* FileStorageLayer.createBucket({
Bucket: 'test',
CreateBucketConfiguration: {
Bucket: {
Type: 'Directory',
DataRedundancy: 'SingleAvailabilityZone',
},
},
});
// ...
}),
Effect.provide(CloudflareR2StorageLayerLive)
);🔶 bucketInfos
type BucketInfosInput<TBucket extends string> = {
Bucket: TBucket;
ExpectedBucketOwner?: string;
};
type BucketInfosResult = {
region?: string;
};
type bucketInfos = <TBucket extends string>(
input: BucketInfosInput<TBucket>
) => Effect.Effect<
BucketInfosResult,
ConfigError | FileStorageError | BucketNotFoundError,
FileStorage
>;🧿 Example
import { Effect, pipe } from 'effect';
import {
CloudflareR2StorageLayerLive,
FileStorageLayer,
} from 'effect-cloudflare-r2-layer';
type Buckets = 'assets' | 'config';
const task = pipe(
Effect.gen(function* () {
const result = yield* FileStorageLayer.bucketInfos<Buckets>({
Bucket: 'assets',
});
// ...
}),
Effect.provide(CloudflareR2StorageLayerLive)
);🔶 uploadFile
Adds a file to the specified bucket.
interface UploadFileInput<TBucket extends string> {
bucketName: TBucket;
key: string;
data: Buffer;
contentType: string | undefined;
}
type uploadFile = <TBucket extends string>(
input: UploadFileInput<TBucket>
) => Effect.Effect<
PutObjectCommandOutput,
FileStorageError | ConfigError,
FileStorage
>;🧿 Example
import { FileSystem } from '@effect/platform/FileSystem';
import { NodeFileSystem } from '@effect/platform-node';
import { Effect, Layer, pipe } from 'effect';
import {
CloudflareR2StorageLayerLive,
FileStorageLayer,
} from 'effect-cloudflare-r2-layer';
type Buckets = 'assets' | 'config';
const fileName = 'yolo.jpg';
const filePath = './assets/yolo.jpg';
const task = pipe(
Effect.gen(function* () {
const fs = yield* FileSystem;
const fileData = yield* fs.readFile(filePath);
yield* FileStorageLayer.uploadFile<Buckets>({
bucketName: 'assets',
documentKey: fileName,
data: Buffer.from(fileData),
contentType: 'image/jpeg',
});
// ...
}),
Effect.provide(
Layer.mergeAll(CloudflareR2StorageLayerLive, NodeFileSystem.layer)
)
);🔶 deleteFile
Removes a file from the specified bucket.
interface DeleteFileInput<TBucket extends string> {
bucketName: TBucket;
key: string;
}
type deleteFile = <TBucket extends string>(
input: DeleteFileInput<TBucket>
) => Effect.Effect<
DeleteObjectCommandOutput,
FileStorageError | ConfigError,
FileStorage
>;🧿 Example
import { Effect, pipe } from 'effect';
import {
CloudflareR2StorageLayerLive,
FileStorageLayer,
} from 'effect-cloudflare-r2-layer';
type Buckets = 'assets' | 'config';
const fileName = 'yolo.jpg';
const filePath = './assets/yolo.jpg';
const task = pipe(
Effect.gen(function* () {
yield* FileStorageLayer.deleteFile<Buckets>({
bucketName: 'assets',
documentKey: fileName,
});
// ...
}),
Effect.provide(CloudflareR2StorageLayerLive);
);🔶 getFileUrl
Gets a pre-signed url to fetch a ressource by its filename from the specified bucket.
type getFileUrl = <TBucket extends string>(
bucket: TBucket
fileName: string,
) => Effect.Effect<
string,
FileStorageError | ConfigError,
FileStorage
>;🧿 Example
import { Effect, pipe } from 'effect';
import {
CloudflareR2StorageLayerLive,
FileStorageLayer,
} from 'effect-cloudflare-r2-layer';
type Buckets = 'assets' | 'config';
const filename = 'yolo.jpg';
const task = pipe(
Effect.gen(function* () {
const url = yield* FileStorageLayer.getFileUrl<Buckets>('assets', filename);
// ...
}),
Effect.provide(CloudflareR2StorageLayerLive);
);🔶 readAsJson
Fetches a file, expecting a content extending Record<string, unknown>.
type readAsJson = <
TBucket extends string,
TShape extends Record<string, unknown>
>(
bucket: TBucket,
fileName: string
) => Effect.Effect<
TShape,
HttpClientError | FileStorageError | ConfigError,
FileStorage | Scope | HttpClient>
>;🧿 Example
import { FetchHttpClient } from '@effect/platform';
import { Effect, Layer, pipe } from 'effect';
import {
CloudflareR2StorageLayerLive,
FileStorageLayer,
} from 'effect-cloudflare-r2-layer';
type Buckets = 'assets' | 'config';
type JsonData = {
cool: boolean;
yolo: string;
};
const task = pipe(
pipe(
Effect.gen(function* () {
const json = yield* FileStorageLayer.readAsJson<Buckets, JsonData>(
'config',
'app-config.json'
);
// json is of type JsonData ...
}),
Effect.scoped,
Effect.provide(
Layer.mergeAll(CloudflareR2StorageLayerLive, FetchHttpClient.layer)
)
)
);🔶 readAsText
Fetches a file as a string.
readAsText: <TBucket extends string>(
bucketName: TBucket,
documentKey: string
) =>
Effect.Effect<
string,
ConfigError | HttpClientError | FileStorageError,
FileStorage | Scope | HttpClient
>;🧿 Example
import { FetchHttpClient } from '@effect/platform';
import { Effect, Layer, pipe } from 'effect';
import {
CloudflareR2StorageLayerLive,
FileStorageLayer,
} from 'effect-cloudflare-r2-layer';
type Buckets = 'assets' | 'config';
const task = pipe(
pipe(
Effect.gen(function* () {
const text = yield* FileStorageLayer.readAsText<Buckets>(
'assets',
'content.txt'
);
// ...
}),
Effect.scoped,
Effect.provide(
Layer.mergeAll(CloudflareR2StorageLayerLive, FetchHttpClient.layer)
)
)
);🔶 readAsRawBinary
Fetches a file as raw binary.
readAsRawBinary: <TBucket extends string>(
bucketName: TBucket,
documentKey: string
) =>
Effect.Effect<
ArrayBuffer,
ConfigError | HttpClientError | FileStorageError,
FileStorage | Scope | HttpClient
>;🧿 Example
import { FetchHttpClient } from '@effect/platform';
import { FileSystem } from '@effect/platform/FileSystem';
import { Effect, Layer, pipe } from 'effect';
import {
CloudflareR2StorageLayerLive,
FileStorageLayer,
} from 'effect-cloudflare-r2-layer';
type Buckets = 'assets' | 'config';
const task = pipe(
pipe(
Effect.gen(function* () {
const buffer = yield* FileStorageLayer.readAsRawBinary<Buckets>(
'assets',
'yolo.jpg'
);
const fs = yield* FileSystem;
const buffer = Buffer.from(data);
yield* fs.writeFile('./file.jpg', buffer);
}),
Effect.scoped,
Effect.provide(
Layer.mergeAll(CloudflareR2StorageLayerLive, FetchHttpClient.layer)
)
)
);🔶 fileExists
type fileExists = <TBucket extends string>(
bucket: TBucket,
fileName: string
) => Effect.Effect<boolean, ConfigError | FileStorageError, FileStorage>;🧿 Example
import { Effect, pipe } from 'effect';
import {
CloudflareR2StorageLayerLive,
FileStorageLayer,
} from 'effect-cloudflare-r2-layer';
type Bucket = 'assets' | 'config';
const filePath = 'my-app/config.json';
const task = pipe(
Effect.gen(function* () {
const exists = yield* FileStorageLayer.fileExists<Bucket>(
'config',
filePath
);
// ...
}),
Effect.provide(CloudflareR2StorageLayerLive)
);