Hono - REST

REST is a set of rules and recommendations for designing a web API.

Introduction

A web server can be configured as a resource server accessed through a set of “external” functions or endpoints (API).

REST is a fairly common way of interacting between client applications and services using HTTP.

An HTTP request consists of a method and a “path”.

By default, browsers use the GET method, and when you navigate to a website you do so with a GET request and only need to specify the “path”.

But a web API uses other methods, and clients use these methods to perform actions other than obtaining resources.

Work Environment

Create an application (with the cloudflare-workers template):

Terminal window
deno init --npm hono hono-rest --template cloudflare-workers

Run the server in development mode:

Terminal window
deno task dev

Modify the index.ts file to return a JSON document at the path /api/hello.

Add this code:

app.get('/api/hello', (c) => {
return c.json({
ok: true,
message: 'Hello Hono!',
})
})

Install curlie:

Terminal window
scoop install curlie

Make a GET request:

Terminal window
curlie localhost:8787/api/hello

You can see that the server returns the JSON response you programmed:

Terminal window
HTTP/1.1 200 OK
Content-Length: 35
Content-Type: application/json
{
"ok": true,
"message": "Hello Hono!"
}

Routes

A web API consists of different “external” functions defined through a “path”.

Parameters

Most functions, to be useful, must be parameterized: they must be able to receive parameters and produce a different result based on those parameters.

You can use part of the path to identify the function and the other part of the path to define the input parameters.

For example, if you have a function that shows the profile of each employee, you can use the path /employee/:id, where /employee is the external name of the function and /:id is the function argument.

index.ts
const employees = [{name: "David"}, {name: "Dora"}]
app.get('employee/:id', (c) => {
const {id} = c.req.param()
const index = parseInt(id)
const employee = employees[index]
if (!employee) {
return c.json('Employee not found', 404)
} else {
return c.json(employee)
}
})

Parameters

app.get('/student/:username', (c) => {
const {username} = c.req.param()
return c.json({"student": username})
})
Terminal window
> curl http://127.0.0.1:8787/student/eva

Getting a path parameter, URL query value, and appending a Response header is written as follows:

app.get('/post/:id', (c) => {
const page = c.req.query('page')
const id = c.req.param('id')
c.header('X-Message', 'Hi!')
return c.text(`You want to see ${page} of ${id}`)
})

We can easily handle POST, PUT, and DELETE not only GET.

app.post('/post/', (c) => c.text('Created!', 201))
app.delete('/post/:id', (c) =>
c.text(`${c.req.param('id')} is deleted!`)
)

MongoDB

Add a dependency with mongodb:

Terminal window
deno add npm:mongodb

Node.js compatibility is required for database drivers, including Postgres.js, and needs to be configured for your Workers project.

Modify the wrangler.jsonc file:

// wrangler.jsonc
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "hono",
"main": "src/index.ts",
"compatibility_date": "2025-04-10",
"compatibility_flags": [
"nodejs_compat"
]
}

Connection String

Create the .dev.vars file for local development:

.dev.vars
DB_CONN_STRING="mongodb+srv://<username>:<password>@cluster.2hr0xbm.mongodb.net"
Caution
Add this file to .gitignore

To get the value of this variable, use Hono’s Adapter Helper.

The env() function facilitates retrieving environment variables across different runtimes:

import {Hono} from 'hono'
import {env} from 'hono/adapter'
const app = new Hono()
app.get('/api/env', (c) => {
const {DB_CONN_STRING} = env<{ DB_CONN_STRING: string }>(c)
return c.json({DB_CONN_STRING: DB_CONN_STRING})
})

Run curlie at the endpoint:

Terminal window
curlie localhost:8787/api/env

You can see that it responds with the connection string configured in the .dev.vars file:

HTTP/1.1 200 OK
Content-Length: 133
Content-Type: application/json
{
"DB_CONN_STRING": "mongodb+srv://<user>:<password>@cluster.ee8smuy.mongodb.net/?retryWrites=true&w=majority&appName=cluster"
}
Tip
You can configure the .dev.vars file to work with a local database or another database that is not the production one

Let’s Go!

Implement an “endpoint” to insert birds:

index.ts
import { MongoClient } from 'mongodb'
app.post('/api/bird/', async (c) => {
const bird = await c.req.json()
const {DB_CONN_STRING} = env<{ DB_CONN_STRING: string }>(c)
const client = new MongoClient(DB_CONN_STRING)
const result = await client.db('earth').collection('birds').insertOne(bird)
return c.json({id: result.insertedId})
})

From the terminal, send a POST request to the /api/bird/ endpoint:

Terminal window
curlie POST localhost:8787/api/bird/ name=swallow

Verify that the database has a swallow in the birds collection.

Next, implement an “endpoint” to search for birds by name:

index.ts
app.get('/api/bird/:name', async (c) => {
const {name} = c.req.param()
const {DB_CONN_STRING} = env<{ DB_CONN_STRING: string }>(c)
const result = await new MongoClient(DB_CONN_STRING).db('earth').collection('birds').findOne({ name })
if (result == null){
c.notFound()
} else {
return c.json(result)
}
})

You can search for a bird by name:

Terminal window
curlie localhost:8787/api/bird/swallow
HTTP/1.1 200 OK
Content-Length: 51
Content-Type: application/json
{
"_id": "68079db611c9e476456f5cbb",
"name": "swallow"
}

And if it doesn’t find it, the endpoint returns an error:

Terminal window
curlie localhost:8787/api/bird/turtle
404 Not Found

Cloudflare

Next, deploy the project to Cloud - Cloudflare.

First, you need to add the DB_CONN_STRING variable to your production environment:

Terminal window
deno run -A npm:wrangler secret put DB_CONN_STRING
× Enter a secret value: mongodb+srv://<user>:<password>@cluster.ee8smuy.mongodb.net

Deploy the code to Cloudflare:

Terminal window
deno task deploy
...
Deployed hono triggers (2.64 sec)
https://hono.optersoft.workers.dev
Current Version ID: bbaafdcb-5836-4ab8-a360-bc65832aabd2

Go to the URL indicated to you (it will be different in your case): https://hono.optersoft.workers.dev

You can see that the application has been deployed to Cloudflare Workers.

Note
Every time you want to redeploy a new version, run deno task deploy

Secrets are a type of binding that allow you to attach encrypted text values to your Worker.

TODO