npm package: @mest-fe/cloudflare-workers-helper
Description: Helper functions for Cloudflare Workers
Author: MEST Team
Latest version: 0.8.1

## Cloudflare Workers Helpers

In Mest, we use Workers to handle the public endpoints of API services,
which involves some standardized Response and common operations,
and Helpers has summarized utils to reduce the amount of repetitive code in daily development.

To keep the script running fast, Helpers will only add simple functions or individual functions,
and will only work with HTTP Workers.

### Usage

1. Install: `yarn add @mest-fe/cloudflare-workers-helpers`
2. Import: `import { utils } from '@mest-fe/cloudflare-workers-helpers'`

## Documentation

### Utils

#### `utils.textToHashHex`

Convert text to hash hex string.

```ts
const hash = await utils.textToHashHex('hello world')
```

#### `utils.standardizedUrl`

Disassemble and extract the parts of the url.

```ts
const { pathname, query, url, relative } = utils.standardizedUrl(request)
```

#### `utils.logger`

Output logs, which are not parsed unless explicitly requested.

- WORKERS_LOG: `debug` / `info` / undefined

Set the logging level using environment variables or global configuration:

```ts
// Set the logging level to `debug` in the global configuration
;(globalThis as any).WORKERS_LOG =
  // set the logging level to `debug` in the environment variables
  // wrangler.toml
  'debug'[vars]
WORKERS_LOG = 'debug'
```

### Alias

Return the preset response as a shorthand named, option to cache requests or not.

> Caching will only work on custom domains and each data center is isolated from each other.
>
> [Cache on Cloudflare](https://developers.cloudflare.com/workers/runtime-apis/cache/)

#### Basic

```ts
const handleRequest = (request: Request, event: FetchEvent) => {
  const alias = new Alias(request)
  return alias.noContent()
}

addEventListener('fetch', event => {
  event.respondWith(handleRequest(event.request, event))
})
```

#### Cache response

Only a response can enter the cache after it has been consumed, and set cache does not block the http.

```ts
const handleRequest = (request: Request, event: FetchEvent) => {
  const alias = new Alias(request, {
    cache: { scope: 'my-scope', event },
  })
  // Return the cache directly if it exists
  const cache = alias.getCache()
  if (cache) return cache

  // ... some code

  // Cache 404 for 1 hour
  return alias.withCache(60 * 60).notFound()
}
```

### Upstream

Forwarding requests upstream synchronously, with the ability to cache request returns in KV.

```ts
// -> http://localhost?q=hello
// <- https://www.google.com?q=hello

const upstream = new Upstream(request, {
  host: 'https://www.google.com',
})
return upstream.handler()
```

#### Modify content before request to upstream

You can usually modify the headers or endpoints before the request is initiated。

```ts
const upstream = new Upstream(request, {
  host: 'https://www.google.com',
  beforeRequest: ({ req, url }) => {
    const myUrl = url.replace('q=hello', 'q=world')
    const myReq = req.clone()
    myReq.headers.set('x-proxy-host', '...')
    return { req: myReq, url: myUrl }
  },
})
return upstream.handler()
```

If necessary, we can choose not to initiate this request and return a custom message.

```ts
new Upstream(request, {
  host: 'https://www.google.com',
  beforeRequest: ({ req, url }) => new Response('filtered'),
})
```

#### Cache to KV

Unlike the `Alias` that uses caches, `Upstream` can add [KV](https://developers.cloudflare.com/workers/runtime-apis/kv/) as a persistent cache,
which means that once a request is cached,
it can get the same result in all data centers unless it expires or the version is manually updated.

The persistent cache will store the following sections of the response:

- Response Body (with `Content-Type` and `Content-Encoding`)
- Response headers (without `cf-*` and caches control headers)
- Response status code

The persistent cache will set the ttl with the upstream header:

```js
// upstream headers
headers = {
  'cache-control': 'max-age=3600',
}
```

```ts
const upstream = new Upstream(request, {
  host: 'https://www.google.com',
  KVCache: {
    KVNamespace: MY_KV_NAMESPACE,
    event,
  },
})

const cached = await upstream.getCache()
if (cached && !cached.expired) return cached.getResponse()

return upstream.withKVCache().handler()
```

If you want to track whether the request came from a cache, just add a custom header.

### Images

- [Create Images api token](https://dash.cloudflare.com/profile/api-tokens)
- [Find your accountId](https://developers.cloudflare.com/fundamentals/get-started/basic-tasks/find-account-and-zone-ids/)

#### Upload image

In general, it is recommended that you always manually specify the `id` of the image with the `filename` and keep it consistent,
this will help you to distinguish the purpose of each image in the Cloudflare panel.
Secondly, Cloudflare Images does not support updating images directly,
so it would be helpful if we could create images with a fixed id to help us delete them.

```ts
const image = new Images({ apiToken, accountId })
const created = await image.uploadFromBlob(blob, {
  id: imageId,
  filename: imageId,
})
```

#### Delete image

```ts
const image = new Images({ apiToken, accountId })
const res = await image.removeImage(imageId)
```

#### Check image

Usually we want to verify the existence of the image through the api before deleting or uploading it.

```ts
const image = new Images({ apiToken, accountId })
const result = await image.hasImage(imageId)
```

## License

[MIT](LICENSE)