Cloudflare Workers
Cloudflare Workers is a JavaScript edge runtime on Cloudflare CDN.
You can develop the application locally and publish it with a few commands using Wrangler. Wrangler includes trans compiler, so we can write the code with TypeScript.
Let’s make your first application for Cloudflare Workers with Hono.
1. Setup
A starter for Cloudflare Workers is available. Start your project with "create-hono" command.
npm create hono@latest my-appnpm create hono@latest my-appyarn create hono my-appyarn create hono my-apppnpm create hono my-apppnpm create hono my-appbunx create-hono my-appbunx create-hono my-appdeno run -A npm:create-hono my-appdeno run -A npm:create-hono my-appMove to my-app and install the dependencies.
cd my-app
npm icd my-app
npm icd my-app
yarncd my-app
yarncd my-app
pnpm icd my-app
pnpm icd my-app
bun icd my-app
bun i2. Hello World
Edit src/index.ts like below.
import { Hono } from 'hono'
const app = new Hono()
app.get('/', (c) => c.text('Hello Cloudflare Workers!'))
export default appimport { Hono } from 'hono'
const app = new Hono()
app.get('/', (c) => c.text('Hello Cloudflare Workers!'))
export default app3. Run
Run the development server locally. Then, access http://localhost:8787 in your web browser.
npm run devnpm run devyarn devyarn devpnpm devpnpm devbun run devbun run dev4. Deploy
If you have a Cloudflare account, you can deploy to Cloudflare. In package.json, $npm_execpath needs to be changed to your package manager of choice.
npm run deploynpm run deployyarn deployyarn deploypnpm deploypnpm deploybun run deploybun run deployThat's all!
Service Worker mode or Module Worker mode
There are two syntaxes for writing the Cloudflare Workers. Service Worker mode and Module Worker mode. Using Hono, you can write with both syntax:
// Service Worker
app.fire()// Service Worker
app.fire()// Module Worker
export default app// Module Worker
export default appBut now, we recommend using Module Worker mode because such as that the binding variables are localized.
Serve static files
You need to set it up to serve static files. Static files are distributed by using Workers Sites. To enable this feature, edit wrangler.toml and specify the directory where the static files will be placed.
[site]
bucket = "./assets"[site]
bucket = "./assets"Then create the assets directory and place the files there.
./
├── assets
│ ├── favicon.ico
│ └── static
│ ├── demo
│ │ └── index.html
│ ├── fallback.txt
│ └── images
│ └── dinotocat.png
├── package.json
├── src
│ └── index.ts
└── wrangler.toml./
├── assets
│ ├── favicon.ico
│ └── static
│ ├── demo
│ │ └── index.html
│ ├── fallback.txt
│ └── images
│ └── dinotocat.png
├── package.json
├── src
│ └── index.ts
└── wrangler.tomlThen use "Adapter".
import { Hono } from 'hono'
import { serveStatic } from 'hono/cloudflare-workers'
const app = new Hono()
app.get('/static/*', serveStatic({ root: './' }))
app.get('/favicon.ico', serveStatic({ path: './favicon.ico' }))import { Hono } from 'hono'
import { serveStatic } from 'hono/cloudflare-workers'
const app = new Hono()
app.get('/static/*', serveStatic({ root: './' }))
app.get('/favicon.ico', serveStatic({ path: './favicon.ico' }))See Example.
rewriteRequestPath
If you want to map http://localhost:8787/static/* to ./assets/statics, you can use the rewriteRequestPath option:
app.get(
'/static/*',
serveStatic({
root: './',
rewriteRequestPath: (path) => path.replace(/^\/static/, '/statics'),
})
)app.get(
'/static/*',
serveStatic({
root: './',
rewriteRequestPath: (path) => path.replace(/^\/static/, '/statics'),
})
)onNotFound
You can specify handling when the requested file is not found with notFoundOption:
app.get(
'/static/*',
serveStatic({
onNotFound: (path, c) => {
console.log(`${path} is not found, you access ${c.req.path}`)
},
})
)app.get(
'/static/*',
serveStatic({
onNotFound: (path, c) => {
console.log(`${path} is not found, you access ${c.req.path}`)
},
})
)Types
You have to install @cloudflare/workers-types if you want to have workers types.
npm i --save-dev @cloudflare/workers-typesnpm i --save-dev @cloudflare/workers-typesyarn add -D @cloudflare/workers-typesyarn add -D @cloudflare/workers-typespnpm add -D @cloudflare/workers-typespnpm add -D @cloudflare/workers-typesbun add --dev @cloudflare/workers-typesbun add --dev @cloudflare/workers-typesTesting
For testing, we recommend using jest-environment-miniflare. Refer to examples for setting it up.
If there is the application below.
import { Hono } from 'hono'
const app = new Hono()
app.get('/', (c) => c.text('Please test me!'))import { Hono } from 'hono'
const app = new Hono()
app.get('/', (c) => c.text('Please test me!'))We can test if it returns "200 OK" Response with this code.
describe('Test the application', () => {
it('Should return 200 response', async () => {
const res = await app.request('http://localhost/')
expect(res.status).toBe(200)
})
})describe('Test the application', () => {
it('Should return 200 response', async () => {
const res = await app.request('http://localhost/')
expect(res.status).toBe(200)
})
})Bindings
In the Cloudflare Workers, we can bind the environment values, KV namespace, R2 bucket, or Durable Object. You can access them in c.env. It will have the types if you pass the "type struct" for the bindings to the Hono as generics.
type Bindings = {
MY_BUCKET: R2Bucket
USERNAME: string
PASSWORD: string
}
const app = new Hono<{ Bindings: Bindings }>()
// Access to environment values
app.put('/upload/:key', async (c, next) => {
const key = c.req.param('key')
await c.env.MY_BUCKET.put(key, c.req.body)
return c.text(`Put ${key} successfully!`)
})type Bindings = {
MY_BUCKET: R2Bucket
USERNAME: string
PASSWORD: string
}
const app = new Hono<{ Bindings: Bindings }>()
// Access to environment values
app.put('/upload/:key', async (c, next) => {
const key = c.req.param('key')
await c.env.MY_BUCKET.put(key, c.req.body)
return c.text(`Put ${key} successfully!`)
})Using Variables in Middleware
This is the only case for Module Worker mode. If you want to use Variables or Secret Variables in Middleware, for example, "username" or "password" in Basic Authentication Middleware, you need to write like the following.
import { basicAuth } from 'hono/basic-auth'
//...
app.use('/auth/*', async (c, next) => {
const auth = basicAuth({
username: c.env.USERNAME,
password: c.env.PASSWORD,
})
return auth(c, next)
})import { basicAuth } from 'hono/basic-auth'
//...
app.use('/auth/*', async (c, next) => {
const auth = basicAuth({
username: c.env.USERNAME,
password: c.env.PASSWORD,
})
return auth(c, next)
})The same is applied to Bearer Authentication Middleware, JWT Authentication, or others.
Hono