HTTP Server
Join an existing WiFi network and serve a small HTTP API: a JSON status endpoint, a route with a path param, and a server-sent events stream. See the http/server API reference for the full type surface.
Hardware
- Any ESP32 board with WiFi
- USB cable
- A WiFi network you can join, plus a phone or laptop on the same network
Code
import {env} from 'mikro/env'
import {createServer, type ServerResponse} from 'mikro/http/server'
import {sleep} from 'mikro/sleep'
import {memoryUsage, uptime} from 'mikro/sys'
import {wifi} from 'mikro/wifi'
const ssid = env.require('WIFI_SSID')
const passphrase = env.require('WIFI_PASSPHRASE')
console.log('Connecting to %s...', ssid)
const connected = await wifi.connect({ssid, passphrase})
if (!connected.ok) {
console.error('WiFi connect failed:', connected.error)
} else {
console.log('Connected. Open http://%s/', connected.value.ip)
const json = (status: number, value: unknown): ServerResponse => ({
status,
headers: {'content-type': 'application/json'},
body: JSON.stringify(value),
})
// A short, finite stream. The server handles one request at a time, so a
// long-lived stream holds the connection until it ends.
async function* countdown() {
for (let i = 5; i > 0; i--) {
yield `data: ${i}\n\n`
await sleep(1000)
}
yield 'data: liftoff\n\n'
}
// Routing is left to the app: branch on req.method and the path.
const server = createServer((req): ServerResponse => {
if (req.method !== 'GET') return json(405, {error: 'method not allowed'})
const path = req.url.split('?')[0]
if (path === '/') {
return {
status: 200,
headers: {'content-type': 'text/html; charset=utf-8'},
body: `<h1>Mikro.js HTTP server</h1>
<ul>
<li><a href="/api/status">/api/status</a>: JSON</li>
<li><a href="/api/echo/hello">/api/echo/<msg></a>: path param</li>
<li><a href="/events">/events</a>: server-sent events</li>
</ul>`,
}
}
if (path === '/api/status') {
const mem = memoryUsage()
return json(200, {uptime: uptime(), freeHeapBytes: mem.heapTotal - mem.heapUsed})
}
if (path?.startsWith('/api/echo/')) {
return json(200, {echo: decodeURIComponent(path.slice('/api/echo/'.length))})
}
if (path === '/events') {
return {status: 200, headers: {'content-type': 'text/event-stream'}, body: countdown()}
}
return json(404, {error: 'not found'})
})
server.listen({port: 80}).orPanic('Failed to start HTTP server')
console.log('Listening on port 80')
}Walkthrough
Credentials.
env.require()readsWIFI_SSIDandWIFI_PASSPHRASEfrom.env, throwing a clear error if they're missing.Connect, then serve.
wifi.connect()joins the network and returns the device's IP. The server is created only after a successful connection.Routing is yours. There is no built-in router. Strip the query string from
req.urland branch onreq.methodand the path. The/api/echo/<msg>route reads a path param by slicing the prefix.JSON responses. A small
json()helper setscontent-typeand stringifies the body. Returning a plain{status, body}object is all a handler does.Streaming. Returning an async generator as the
bodystreams a response chunk-by-chunk (here, a short countdown as server-sent events) instead of buffering it.
Create project
pnpm create mikro --template http-servernpm create mikro -- --template http-serveryarn create mikro --template http-serverbun create mikro --template http-serverRun it
Set your network credentials in .env:
WIFI_SSID=your-network
WIFI_PASSPHRASE=your-passwordpnpm install
pnpm mikro flash # only needed once per board
pnpm mikro devnpm install
npx mikro flash # only needed once per board
npx mikro devyarn install
yarn mikro flash # only needed once per board
yarn mikro devbun install
bunx mikro flash # only needed once per board
bunx mikro devThe device prints its IP on boot. Open http://<that-ip>/api/status from a browser or curl on the same network.