REST

REST és un conjunt de regles i recomanacions per dissenyar una API web.

Introducció

Un servidor web es pot configurar com un servidor de recursos que s’hi accedeix mitjançant un conjunt de funcions “externes” o endpoints (API)

REST (Representational State Transfer) és una forma força habitual d’interacció entre aplicacions i serveis de client mitjançant HTTP.

Una sol·licitud HTTP es compon d’un mètode i un “path”.

Per defecte, els navegadors utilitzen el mètode GET, i quan navegues a un lloc web ho fas amb una petició GET i només has d’indicar el “path”.

Però una API web utilitza altres mètodes, i els clients utilitzen aquests mètodes per fer altres accions que no són obtenir recursos.

Entorn de treball

Crea una aplicació (amb el template cloudflare-workers)

bun create hono@latest hono
...
 Which template do you want to use? cloudflare-workers

Executa el servidor en mode desenvolupament:

bun run dev
...
 Starting local server...
[wrangler:inf] Ready on http://127.0.0.1:8787
...

Modifica el fitxer index.ts perquè torni un document JSON al path /api/hello:

import { Hono } from 'hono'

const app = new Hono()

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

export default app

Instal·la curlie

scoop install curlie

Fes una petició GET:

curlie localhost:8787/api/hello

Pots veure que el servidor torna la resposta JSON que has programat:

HTTP/1.1 200 OK
Content-Length: 35
Content-Type: application/json

{
    "ok": true,
    "message": "Hello Hono!"
}

Rutes

Una API web es compon de diferents funcions “externes” definides mitjançant un “path”.

Paràmetres

La majoria de les funcions, per ser útils, han d’estar parametritzades: han de poder rebre uns paràmetres i produir un resultat diferent segons aquests paràmetres.

Pots utilitzar una part del path per identificar la funció i l’altra part del path la pots utilitzar per definir els paràmetres d’entrada.

Por ejemplo, si tienes una función que muestra el perfil de cada empleado puedes utilizar el path /employee/:id, donde /employee es el nombre externo de la función y /:id es el argumento de la función.

Per exemple, si tens una funció que mostra el perfil de cada empleat pots utilitzar el path /employee/:id, on /employee és el nom extern de la funció i /:id és l’argument de la funció.

// 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('Empleat no trobat', 404)
    } else {
      return c.json(employee)
    }
})

Moure documentació de Rest y FastApi

Parámetros

app.get('/student/:username', (c) => {
    const {username} = c.req.param()
    return c.json({"student": username})
})
> 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

Afegeix una dependència amb mongodb:

bun add mongodb

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

Modifica el fitxer wrangler.jsonc:

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

String de connexió

Crea el fitxer .dev.vars per a desenvolupament local:

// .dev.vars
DB_CONN_STRING="mongodb+srv://<username>:<password>@cluster.2hr0xbm.mongodb.net"

Per obtenir el valor d’aquesta variable utilitza l’Adapter Helper de Hono.

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})
})

Executa curlie a l’endpoint

curlie localhost:8787/api/env

Pots veure que respon amb el string de connexió configurat amb el fitxer .dev.vars

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"
}

Anem!

Implementa un “endpoint” per inserir ocells:

// index.ts
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})
})

Des del terminal, envia una petició POST a l’endpoint /api/bird/

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

Verifica que la base de dades té una oreneta a la col·lecció birds.

A continuació implementa un “endpoint” per cercar ocells per nom:

// 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)
    }
})

Pots cercar un ocell per nom:

curlie localhost:8787/api/bird/swallow

HTTP/1.1 200 OK
Content-Length: 51
Content-Type: application/json

{
    "_id": "68079db611c9e476456f5cbb",
    "name": "swallow"
}

I si no el troba l’endpoint retorna un error:

curlie localhost:8787/api/bird/turtle

404 Not Found

Cloudflare

A continuació, desplega el projecte a Cloudflare.

Primer has d’afegir la variable DB_CONN_STRING al teu entorn de producció:

bunx wrangler secret put DB_CONN_STRING

× Enter a secret value: mongodb+srv://<user>:<password>@cluster.ee8smuy.mongodb.net

Desplega el codi a Cloudflare:

> bun run deploy
...
Deployed hono triggers (2.64 sec)
  https://hono.optersoft.workers.dev
Current Version ID: bbaafdcb-5836-4ab8-a360-bc65832aabd2

Ves a la URL que se t’indica (en el teu cas serà diferent): https://hono.optersoft.workers.dev

Pots veure que l’aplicació s’ha desplegat a Cloudflare Workers.

TODO