Writing more adapters β
I sold you the fact it would be easy to write adapters for any framework. How about we verify that instead of just taking my word?
Express β
ts
// src/server-first/8-writing-more-adapters/express-adapter.ts
import express from 'express'
import bodyParser from 'body-parser'
import { ServerAdapter } from '../7-grocery-list-project/server-adapter'
import { IncomingHttpHeaders } from 'http'
import { ReadableStream } from 'node:stream/web'
// See http://expressjs.com/en/5x/api.html
export const createExpressNodeServer: ServerAdapter = ({ handlers, port }) => {
const app = express()
app.use(bodyParser.urlencoded({ extended: true }))
for (const { handle, ...route } of handlers) {
const routerFn = route.method === 'GET' ? app.get : app.post
routerFn.bind(app)(route.path, async (req, res) => {
const response = await handle({
body: req.body,
headers: getHeadersFromIncomingHttpHeaders(req.headers),
params: req.params,
query: req.query,
})
response.headers.forEach((value, name) => res.setHeader(name, value))
response.body
? res.status(response.status).send(await response.text())
: res.sendStatus(response.status)
})
}
return new Promise<void>((resolve) => {
app.listen(port, resolve)
})
}
function getHeadersFromIncomingHttpHeaders(incoming: IncomingHttpHeaders) {
const headers = new Headers()
Object.entries(incoming).forEach(([name, value]) => {
const values = Array.isArray(value) ? value : [value]
values.forEach((value) => {
if (value) headers.append(name, value)
})
})
return headers
}
Hapi.js β
ts
// src/server-first/8-writing-more-adapters/hapi-adapter.ts
import { Server } from '@hapi/hapi'
import { ServerAdapter } from '../7-grocery-list-project/server-adapter'
// See https://hapi.dev/tutorials/
export const createHapiNodeServer: ServerAdapter = ({ port, handlers }) => {
const server = new Server({ port })
for (const { handle, ...route } of handlers) {
server.route({
method: route.method,
path: adaptPath(route.path),
handler: async (request, h) => {
const response = await handle({
body: request.payload as Record<string, any> | undefined,
headers: new Headers(request.headers),
params: request.params,
query: { ...request.query },
})
const hapiRes = h
.response((await response.text()) || undefined)
.code(response.status)
response.headers.forEach((value, name) => hapiRes.header(name, value))
return hapiRes
},
})
}
return server.start()
}
/**
* transforms "/todo/:id" to "/todo/{id}"
*/
function adaptPath(path: string) {
return path.replace(/:([^/]+)/g, '{$1}')
}
Hono β
ts
// src/server-first/8-writing-more-adapters/hono-adapter.ts
import { serve } from '@hono/node-server'
import { Hono } from 'hono'
import { ServerAdapter } from '../7-grocery-list-project/server-adapter'
// See https://hono.dev/docs/getting-started/nodejs
export const createHonoNodeServer: ServerAdapter = ({ handlers, port }) => {
const app = new Hono()
for (const { handle, ...route } of handlers) {
const routerFn = route.method === 'GET' ? app.get : app.post
routerFn.bind(app)(route.path, async (c) => {
return handle({
body: await c.req.parseBody(),
headers: new Headers(c.req.header()),
params: c.req.param(),
query: c.req.query(),
})
})
}
return new Promise<unknown>((resolve) => {
serve({ fetch: app.fetch, port }, resolve)
})
}
Exposing them all β
ts
// src/server-first/8-writing-more-adapters/servers.ts
import { createExpressNodeServer } from './express-adapter'
import { makeHandlers } from './handlers'
import { createHapiNodeServer } from './hapi-adapter'
import { createHonoNodeServer } from './hono-adapter'
async function createServers() {
const handlers = makeHandlers()
await Promise.all([
createHonoNodeServer({ port: 6001, handlers }),
createExpressNodeServer({ port: 6002, handlers }),
createHapiNodeServer({ port: 6003, handlers }),
])
console.info(`Hono server listening on http://localhost:6001`)
console.info(`Express server listening on http://localhost:6002`)
console.info(`Hapi server listening on http://localhost:6003`)
}
createServers().catch(console.error)
sh
$: npx tsx src/server-first/8-writing-more-adapters/servers.ts
Hono server listening on http://localhost:6001
Express server listening on http://localhost:6002
Hapi server listening on http://localhost:6003
Yey, letβs test'hem all β¦Β it took me some (back-ported) adjustments, but here we go. All good β πͺ