effect-cloudflare-r2-layer v1.1.2
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 { 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);
);
🔶 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';
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.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 { 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)
)
)
);
🔶 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)
);
4 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
7 months ago
7 months ago
7 months ago
6 months ago
7 months ago
7 months ago
7 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago