WHATWG Node Generic Server Adapter
@whatwg-node/server
helps you to create a generic server implementation by using WHATWG Fetch API for Node.js, AWS Lambda, Cloudflare Workers, Deno, Express, Fastify, Koa, Next.js and Sveltekit.
Once you create an adapter with createServerAdapter
, you don't need to install any other platform specific package since the generic adapter will handle it automatically.
How to start
Let's create a basic Hello World server adapter.
import { createServerAdapter } from '@whatwg-node/server'
export default createServerAdapter((request: Request) => {
return new Response(`Hello World!`, { status: 200 })
})
Integrations
You can use your server adapter with the following integrations:
Node.js
Node.js is the most popular server side JavaScript runtime.
import myServerAdapter from './myServerAdapter'
import { createServer } from 'http'
const nodeServer = createServer(myServerAdapter)
nodeServer.listen(4000)
AWS Lambda
AWS Lambda is a serverless computing platform that makes it easy to build applications that run on the AWS cloud. Our adaoter is platform agnostic so they can fit together easily. In order to reduce the boilerplate we prefer to use Serverless Express from Vendia.
import myServerAdapter from './myServerAdapter'
import type { Handler } from '@aws-cdk/aws-lambda'
import { configure } from '@vendia/serverless-express'
export const handler: Handler = configure({
app: myServerAdapter
})
Cloudflare Workers
Cloudflare Workers provides a serverless execution environment that allows you to create entirely new applications or augment existing ones without configuring or maintaining infrastructure. It uses Fetch API already so we can use our adapter as an event listener like below;
import myServerAdapter from './myServerAdapter'
self.addEventListener('fetch', myServerAdapter)
Deno
Deno is a simple, modern and secure runtime for JavaScript and TypeScript that uses V8 and is built in Rust.
You can use our adapter as a Deno request handler like below;
import { serve } from 'https://deno.land/std@0.117.0/http/server.ts'
import myServerAdapter from './myServerAdapter'
serve(myServerAdapter, {
addr: ':4000'
})
Express
Express is the most popular web framework for Node.js. It is a minimalist framework that provides a robust set of features to handle HTTP on Node.js applications.
You can easily integrate your adapter into your Express application with a few lines of code.
import express from 'express'
import myServerAdapter from './myServerAdapter'
const app = express()
app.use('/mypath', myServerAdapter)
app.listen(4000, () => {
console.log('Running the server at http://localhost:4000/mypath')
})
Fastify
Fastify is one of the popular HTTP server frameworks for Node.js.. You can use your adapter easily with Fastify.
So you can benefit from the powerful plugins of Fastify ecosystem.
See the ecosystem
import myServerAdapter from './myServerAdapter'
import fastify, { FastifyRequest, FastifyReply } from 'fastify'
const app = fastify({ logger: true })
app.route({
url: '/mypath',
method: ['GET', 'POST', 'OPTIONS'],
handler: async (req, reply) => {
const response = await myServerAdapter.handleNodeRequest(req, {
req,
reply
})
response.headers.forEach((value, key) => {
reply.header(key, value)
})
reply.status(response.status)
reply.send(response.body)
return reply
}
})
app.listen(4000)
Koa
Koa is another Node.js server framework designed by the team behind Express, which aims to be a smaller, more expressive. You can add your adapter to your Koa application with a few lines of code then benefit middlewares written for Koa.
import Koa from 'koa'
import myServerAdapter from './myServerAdapter'
const app = new Koa()
app.use(async ctx => {
const response = await myServerAdapter.handleNodeRequest(ctx.req)
ctx.status = response.status
response.headers.forEach((value, key) => {
ctx.append(key, value)
})
ctx.body = response.body
})
app.listen(4000, () => {
console.log('Running the server at http://localhost:4000')
})
Next.js
Next.js is a web framework that allows you to build websites very quickly and our new server adapter can be integrated with Next.js easily as an API Route.
import myServerAdapter from './myServerAdapter'
import type { NextApiRequest, NextApiResponse } from 'next'
export const config = {
api: {
bodyParser: false
}
}
export default myServerAdapter
SvelteKit
SvelteKit is the fastest way to build svelte apps. It is very simple, and let you build frontend & backend in a single place
import myServerAdapter from './myServerAdapter'
export { myServerAdapter as get, myServerAdapter as post }
Bun
Bun is a modern JavaScript runtime like Node or Deno, and it supports Fetch API as a first class citizen.
So the configuration is really simple like any other JS runtime;
import myServerAdapter from './myServerAdapter'
Bun.serve(myServerAdapter)
const server = Bun.serve(yoga)
console.info(`Server is running on ${server.hostname}`)
File Uploads / Multipart Requests
Multipart requests are a type of HTTP request that allows you to send blobs together with regular text data which has a mime-type multipart/form-data
.
For example, if you send a multipart request from a browser with FormData
, you can get the same FormData
object in your request handler.
import { createServerAdapter } from '@whatwg-node/server'
const myServerAdapter = createServerAdapter(async request => {
const formData = await request.formData()
const file = formData.get('file')
const fileTextContent = await file.text()
const regularTextData = formData.get('additionalStuff')
return new Response('{ "message": "ok" }', {
status: 200,
headers: {
'Content-Type': 'application/json'
}
})
})
You can learn more about File API on MDN documentation.
Routing and Middlewares
We'd recommend to use itty-router to handle routing and middleware approach. So it is really easy to integrate your router to @whatwg-node/server
.
Basic Routing
import { Router } from 'itty-router'
import { createServerAdapter } from '@whatwg-node/server'
const router = Router()
router.get('/todos', () => new Response('Todos Index!'))
router.get('/todos/:id', ({ params }) => new Response(`Todo #${params.id}`))
router.post('/todos', async request => {
const content = await request.json()
return new Response('Creating Todo: ' + JSON.stringify(content))
})
router.get('/google', () => Response.redirect('http://www.google.com'))
router.all('*', () => new Response('Not Found.', { status: 404 }))
const myServerAdapter = createServerAdapter(router)
import { createServer } from 'http'
const httpServer = createServer(myServer)
httpServer.listen(4000)
Middlewares to handle CORS, cookies and more
There is another package called itty-router-extras that provides some utilities for your platform agnostic server implementation. The following example shows how to get the cookies as an object from the request.
import { withCookies } from 'itty-router-extras'
router.get('/foo', withCookies, ({ cookies }) => {
return new Response(`Cookies: ${JSON.stringify(cookies)}`)
})
You can also setup a CORS middleware to handle preflight CORS requests.
import { withCors } from 'itty-router-extras'
router.all(
'*',
withCors({
origin: 'http://localhost:4000',
methods: 'GET, POST, PATCH, DELETE',
headers: 'authorization, referer, origin, content-type',
credentials: false
})
)