effect-cloudflare-r2-layer
v1.0.30
Published
An effect layer to interact with Cloudware R2 storage service
Downloads
823
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 |
| 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). |
🔶 createBucket
type createBucket = (
input: CreateBucketCommandInput
) => Effect.Effect<
CreateBucketCommandOutput,
FileStorageError | ConfigError.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.ConfigError,
FileStorage
>;
🧿 Example
import { Effect, pipe } from 'effect';
import {
CloudflareR2StorageLayerLive,
FileStorageLayer,
} from 'effect-cloudflare-r2-layer';
import { readFile } from 'fs-extra';
type Buckets = 'assets' | 'config';
const fileName = 'yolo.jpg';
const filePath = './assets/yolo.jpg';
const task = pipe(
Effect.gen(function* () {
const fileData = yield* Effect.tryPromise({
try: () => readFile(filePath),
catch: (e) => new FsError({ cause: e }),
});
yield* FileStorageLayer.uploadFile<Buckets>({
bucketName: 'assets',
documentKey: fileName,
data: fileData,
contentType: 'image/jpeg',
});
// ...
}),
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.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.ConfigError,
FileStorage | Scope | HttpClient<HttpClientError, Scope>
>;
🧿 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<HttpClientError, Scope>
>;
🧿 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<HttpClientError, Scope>
>;
🧿 Example
import { FetchHttpClient } from '@effect/platform';
import { Effect, Layer, pipe } from 'effect';
import {
CloudflareR2StorageLayerLive,
FileStorageLayer,
} from 'effect-cloudflare-r2-layer';
import fs from 'fs-extra';
import { TaggedError } from 'effect/Data';
export class FsError extends TaggedError('FsError')<{
cause?: unknown;
}> {}
type Buckets = 'assets' | 'config';
const task = pipe(
pipe(
Effect.gen(function* () {
const buffer = yield* FileStorageLayer.readAsRawBinary<Buckets>(
'assets',
'yolo.jpg'
);
yield* Effect.tryPromise({
try: () =>
fs.writeFile('./file.jpg', Buffer.from(buffer), {
encoding: 'utf-8',
}),
catch: (e) => new FsError({ cause: e }),
});
}),
Effect.scoped,
Effect.provide(
Layer.mergeAll(CloudflareR2StorageLayerLive, FetchHttpClient.layer)
)
)
);