Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/jpb06/effect-cloudflare-r2-layer
An effect layer to interact with Cloudflare R2 storage service
https://github.com/jpb06/effect-cloudflare-r2-layer
cloudflare cloudflare-r2 effect-ts
Last synced: 19 days ago
JSON representation
An effect layer to interact with Cloudflare R2 storage service
- Host: GitHub
- URL: https://github.com/jpb06/effect-cloudflare-r2-layer
- Owner: jpb06
- License: mit
- Created: 2024-09-25T18:23:00.000Z (3 months ago)
- Default Branch: main
- Last Pushed: 2024-10-23T18:57:42.000Z (2 months ago)
- Last Synced: 2024-10-24T04:57:07.031Z (2 months ago)
- Topics: cloudflare, cloudflare-r2, effect-ts
- Language: TypeScript
- Homepage:
- Size: 396 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# effect-cloudflare-r2-layer
An effect layer to interact with Cloudware R2 storage service.
## ⚡ Quick start
### 🔶 Install
```bash
npm i effect-cloudflare-r2-layer
# or
pnpm i effect-cloudflare-r2-layer
# or
bun i effect-cloudflare-r2-layer
```### 🔶 Use the layer
```typescript
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:
```env
CLOUDFLARE_ACCOUNT_ID=""
R2_DOCUMENTS_ACCESS_KEY_ID=""
R2_DOCUMENTS_SECRET_ACCESS_KEY=""
```## ⚡ API
| function | description |
| -------------------------------------- | ----------------------------------------------------------------------------------------- |
| [`createBucket`](#-createbucket) | Create a bucket |
| [`bucketInfos`](#-bucketinfos) | Get bucket infos |
| [`uploadFile`](#-uploadfile) | Adds a file to the specified bucket |
| [`deleteFile`](#-deleteFile) | Removes a file from the specified bucket |
| [`getFileUrl`](#-getfileurl) | Gets a pre-signed url to fetch a ressource by its `filename` from the specified `bucket`. |
| [`readAsJson`](#-readasjson) | Fetches a file, expecting a content extending `Record`. |
| [`readAsText`](#-readastext) | Fetches a file as a string. |
| [`readAsRawBinary`](#-readasrawbinary) | Fetches a file as raw binary (ArrayBuffer). |### 🔶 `createBucket`
```typescript
type createBucket = (
input: CreateBucketCommandInput
) => Effect.Effect<
CreateBucketCommandOutput,
FileStorageError | ConfigError.ConfigError,
FileStorage
>;
```#### 🧿 Example
```typescript
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`
```typescript
type BucketInfosInput = {
Bucket: TBucket;
ExpectedBucketOwner?: string;
};type BucketInfosResult = {
region?: string;
};type bucketInfos = (
input: BucketInfosInput
) => Effect.Effect<
BucketInfosResult,
ConfigError | FileStorageError | BucketNotFoundError,
FileStorage
>;
```#### 🧿 Example
```typescript
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({
Bucket: 'assets',
});// ...
}),
Effect.provide(CloudflareR2StorageLayerLive)
);
```### 🔶 `uploadFile`
Adds a file to the specified bucket.
```typescript
interface UploadFileInput {
bucketName: TBucket;
key: string;
data: Buffer;
contentType: string | undefined;
}type uploadFile = (
input: UploadFileInput
) => Effect.Effect<
PutObjectCommandOutput,
FileStorageError | ConfigError.ConfigError,
FileStorage
>;
```#### 🧿 Example
```typescript
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({
bucketName: 'assets',
documentKey: fileName,
data: fileData,
contentType: 'image/jpeg',
});// ...
}),
Effect.provide(CloudflareR2StorageLayerLive);
);
```### 🔶 `deleteFile`
Removes a file from the specified bucket.
```typescript
interface DeleteFileInput {
bucketName: TBucket;
key: string;
}type deleteFile = (
input: DeleteFileInput
) => Effect.Effect<
DeleteObjectCommandOutput,
FileStorageError | ConfigError.ConfigError,
FileStorage
>;
```#### 🧿 Example
```typescript
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({
bucketName: 'assets',
documentKey: fileName,
});// ...
}),
Effect.provide(CloudflareR2StorageLayerLive);
);
```### 🔶 `getFileUrl`
Gets a pre-signed url to fetch a ressource by its `filename` from the specified `bucket`.
```typescript
type getFileUrl = (
bucket: TBucket
fileName: string,
) => Effect.Effect<
string,
FileStorageError | ConfigError.ConfigError,
FileStorage
>;
```#### 🧿 Example
```typescript
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('assets', filename);// ...
}),
Effect.provide(CloudflareR2StorageLayerLive);
);
```### 🔶 `readAsJson`
Fetches a file, expecting a content extending `Record`.
```typescript
type readAsJson = <
TBucket extends string,
TShape extends Record
>(
bucket: TBucket,
fileName: string
) => Effect.Effect<
TShape,
HttpClientError | FileStorageError | ConfigError.ConfigError,
FileStorage | Scope | HttpClient
>;
```#### 🧿 Example
```typescript
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(
'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.
```typescript
readAsText: (
bucketName: TBucket,
documentKey: string
) =>
Effect.Effect<
string,
ConfigError | HttpClientError | FileStorageError,
FileStorage | Scope | HttpClient
>;
```#### 🧿 Example
```typescript
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(
'assets',
'content.txt'
);// ...
}),
Effect.scoped,
Effect.provide(
Layer.mergeAll(CloudflareR2StorageLayerLive, FetchHttpClient.layer)
)
)
);
```### 🔶 `readAsRawBinary`
Fetches a file as raw binary.
```typescript
readAsRawBinary: (
bucketName: TBucket,
documentKey: string
) =>
Effect.Effect<
ArrayBuffer,
ConfigError | HttpClientError | FileStorageError,
FileStorage | Scope | HttpClient
>;
```#### 🧿 Example
```typescript
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(
'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)
)
)
);
```