Files
bonpin-adrian-meal-api/server.js
T

63 lines
1.9 KiB
JavaScript

import { Elysia, status } from 'elysia'
import { readFileSync } from 'node:fs'
import { fileURLToPath } from 'node:url'
const PORT = process.env.PORT || 3000
// Resolve index.html next to this file, so it works no matter where you run it from.
const INDEX_HTML = readFileSync(fileURLToPath(new URL('./index.html', import.meta.url)), 'utf8')
const HTML_HEADERS = {
'content-type': 'text/html; charset=utf-8',
'content-length': String(Buffer.byteLength(INDEX_HTML)),
}
// The food menu data.
const menu = [
{ id: 1, food: 'Adobo', price: 75 },
{ id: 2, food: 'Fried Chicken', price: 50 },
{ id: 3, food: 'Yum Burger', price: 70 },
]
// Elysia serves through Bun.serve on Bun, but it needs the Node adapter on Node.
// This one file therefore runs on either runtime: `bun server.js` or `node server.js`.
const adapter =
typeof Bun === 'undefined' ? (await import('@elysiajs/node')).node() : undefined
new Elysia(adapter ? { adapter } : {})
.onRequest(({ request }) => {
if (request.method !== 'GET') {
return status(405, { error: 'Method not allowed' })
}
})
// GET /api/menu -> the whole menu
.get('/api/menu', () => menu)
// GET /api/menu/:id -> one item
.get('/api/menu/:id', ({ params: { id }, status }) => {
const num = Number(id)
if (!Number.isInteger(num) || num < 1) {
return status(400, { error: 'ID must be a whole number, like 1.' })
}
const item = menu.find((food) => food.id === num)
if (!item) {
return status(404, { error: `No menu item with ID ${num}.` })
}
return item
})
// GET / -> the frontend
.get('/', () => new Response(INDEX_HTML, { headers: HTML_HEADERS }))
.get('/index.html', () => new Response(INDEX_HTML, { headers: HTML_HEADERS }))
.get('*', () => status(404, 'Not found'))
.listen(PORT)
const runtime = typeof Bun === 'undefined' ? 'Node.js' : 'Bun'
console.log(`Meal API running at http://localhost:${PORT} (${runtime})`)