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