bun-types 1.2.3-canary.20250216T140609 → 1.2.3-canary.20250218T140705

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/docs/api/http.md CHANGED
@@ -8,130 +8,158 @@ To start a high-performance HTTP server with a clean API, the recommended approa
8
8
 
9
9
  ## `Bun.serve()`
10
10
 
11
- Start an HTTP server in Bun with `Bun.serve`.
11
+ Use `Bun.serve` to start an HTTP server in Bun.
12
12
 
13
13
  ```ts
14
14
  Bun.serve({
15
- fetch(req) {
16
- return new Response("Bun!");
17
- },
18
- });
19
- ```
15
+ // `routes` requires Bun v1.2.3+
16
+ routes: {
17
+ // Static routes
18
+ "/api/status": new Response("OK"),
19
+
20
+ // Dynamic routes
21
+ "/users/:id": req => {
22
+ return new Response(`Hello User ${req.params.id}!`);
23
+ },
20
24
 
21
- ### `fetch` request handler
25
+ // Per-HTTP method handlers
26
+ "/api/posts": {
27
+ GET: () => new Response("List posts"),
28
+ POST: async req => {
29
+ const body = await req.json();
30
+ return Response.json({ created: true, ...body });
31
+ },
32
+ },
22
33
 
23
- The `fetch` handler handles incoming requests. It receives a [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) object and returns a [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) or `Promise<Response>`.
34
+ // Wildcard route for all routes that start with "/api/" and aren't otherwise matched
35
+ "/api/*": Response.json({ message: "Not found" }, { status: 404 }),
24
36
 
25
- ```ts
26
- Bun.serve({
37
+ // Redirect from /blog/hello to /blog/hello/world
38
+ "/blog/hello": Response.redirect("/blog/hello/world"),
39
+
40
+ // Serve a file by buffering it in memory
41
+ "/favicon.ico": new Response(await Bun.file("./favicon.ico").bytes(), {
42
+ headers: {
43
+ "Content-Type": "image/x-icon",
44
+ },
45
+ }),
46
+ },
47
+
48
+ // (optional) fallback for unmatched routes:
49
+ // Required if Bun's version < 1.2.3
27
50
  fetch(req) {
28
- const url = new URL(req.url);
29
- if (url.pathname === "/") return new Response("Home page!");
30
- if (url.pathname === "/blog") return new Response("Blog!");
31
- return new Response("404!");
51
+ return new Response("Not Found", { status: 404 });
32
52
  },
33
53
  });
34
54
  ```
35
55
 
36
- The `fetch` handler supports async/await:
56
+ ### Routing
57
+
58
+ Routes in `Bun.serve()` receive a `BunRequest` (which extends [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request)) and return a [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) or `Promise<Response>`. This makes it easier to use the same code for both sending & receiving HTTP requests.
37
59
 
38
60
  ```ts
39
- import { sleep, serve } from "bun";
40
- serve({
41
- async fetch(req) {
42
- const start = performance.now();
43
- await sleep(10);
44
- const end = performance.now();
45
- return new Response(`Slept for ${end - start}ms`);
46
- },
47
- });
61
+ // Simplified for brevity
62
+ interface BunRequest<T extends string> extends Request {
63
+ params: Record<T, string>;
64
+ }
48
65
  ```
49
66
 
50
- Promise-based responses are also supported:
67
+ #### Async/await in routes
68
+
69
+ You can use async/await in route handlers to return a `Promise<Response>`.
51
70
 
52
71
  ```ts
53
- Bun.serve({
54
- fetch(req) {
55
- // Forward the request to another server.
56
- return fetch("https://example.com");
72
+ import { sql, serve } from "bun";
73
+
74
+ serve({
75
+ port: 3001,
76
+ routes: {
77
+ "/api/version": async () => {
78
+ const [version] = await sql`SELECT version()`;
79
+ return Response.json(version);
80
+ },
57
81
  },
58
82
  });
59
83
  ```
60
84
 
61
- You can also access the `Server` object from the `fetch` handler. It's the second argument passed to the `fetch` function.
85
+ #### Promise in routes
86
+
87
+ You can also return a `Promise<Response>` from a route handler.
62
88
 
63
89
  ```ts
64
- // `server` is passed in as the second argument to `fetch`.
65
- const server = Bun.serve({
66
- fetch(req, server) {
67
- const ip = server.requestIP(req);
68
- return new Response(`Your IP is ${ip}`);
90
+ import { sql, serve } from "bun";
91
+
92
+ serve({
93
+ routes: {
94
+ "/api/version": () => {
95
+ return new Promise(resolve => {
96
+ setTimeout(async () => {
97
+ const [version] = await sql`SELECT version()`;
98
+ resolve(Response.json(version));
99
+ }, 100);
100
+ });
101
+ },
69
102
  },
70
103
  });
71
104
  ```
72
105
 
73
- ### Static routes
106
+ #### Type-safe route parameters
74
107
 
75
- Use the `static` option to serve static `Response` objects by route.
108
+ TypeScript parses route parameters when passed as a string literal, so that your editor will show autocomplete when accessing `request.params`.
76
109
 
77
110
  ```ts
78
- // Bun v1.1.27+ required
79
- Bun.serve({
80
- static: {
81
- // health-check endpoint
82
- "/api/health-check": new Response("All good!"),
111
+ import type { BunRequest } from "bun";
83
112
 
84
- // redirect from /old-link to /new-link
85
- "/old-link": Response.redirect("/new-link", 301),
86
-
87
- // serve static text
88
- "/": new Response("Hello World"),
89
-
90
- // serve a file by buffering it in memory
91
- "/index.html": new Response(await Bun.file("./index.html").bytes(), {
92
- headers: {
93
- "Content-Type": "text/html",
94
- },
95
- }),
96
- "/favicon.ico": new Response(await Bun.file("./favicon.ico").bytes(), {
97
- headers: {
98
- "Content-Type": "image/x-icon",
99
- },
100
- }),
101
-
102
- // serve JSON
103
- "/api/version.json": Response.json({ version: "1.0.0" }),
104
- },
113
+ Bun.serve({
114
+ routes: {
115
+ // TypeScript knows the shape of params when passed as a string literal
116
+ "/orgs/:orgId/repos/:repoId": req => {
117
+ const { orgId, repoId } = req.params;
118
+ return Response.json({ orgId, repoId });
119
+ },
105
120
 
106
- fetch(req) {
107
- return new Response("404!");
121
+ "/orgs/:orgId/repos/:repoId/settings": (
122
+ // optional: you can explicitly pass a type to BunRequest:
123
+ req: BunRequest<"/orgs/:orgId/repos/:repoId/settings">,
124
+ ) => {
125
+ const { orgId, repoId } = req.params;
126
+ return Response.json({ orgId, repoId });
127
+ },
108
128
  },
109
129
  });
110
130
  ```
111
131
 
112
- Static routes support headers, status code, and other `Response` options.
132
+ Percent-encoded route parameter values are automatically decoded. Unicode characters are supported. Invalid unicode is replaced with the unicode replacement character `&0xFFFD;`.
133
+
134
+ ### Static responses
135
+
136
+ Routes can also be `Response` objects (without the handler function). Bun.serve() optimizes it for zero-allocation dispatch - perfect for health checks, redirects, and fixed content:
113
137
 
114
138
  ```ts
115
139
  Bun.serve({
116
- static: {
117
- "/api/time": new Response(new Date().toISOString(), {
140
+ routes: {
141
+ // Health checks
142
+ "/health": new Response("OK"),
143
+ "/ready": new Response("Ready", {
118
144
  headers: {
119
- "X-Custom-Header": "Bun!",
145
+ // Pass custom headers
146
+ "X-Ready": "1",
120
147
  },
121
148
  }),
122
- },
123
149
 
124
- fetch(req) {
125
- return new Response("404!");
150
+ // Redirects
151
+ "/blog": Response.redirect("https://bun.sh/blog"),
152
+
153
+ // API responses
154
+ "/api/config": Response.json({
155
+ version: "1.0.0",
156
+ env: "production",
157
+ }),
126
158
  },
127
159
  });
128
160
  ```
129
161
 
130
- Static routes can serve Response bodies faster than `fetch` handlers because they don't create `Request` objects, they don't create `AbortSignal`, they don't create additional `Response` objects. The only per-request memory allocation is the TCP/TLS socket data needed for each request.
131
-
132
- {% note %}
133
- `static` is experimental
134
- {% /note %}
162
+ Static responses do not allocate additional memory after initialization. You can generally expect at least a 15% performance improvement over manually returning a `Response` object.
135
163
 
136
164
  Static route responses are cached for the lifetime of the server object. To reload static routes, call `server.reload(options)`.
137
165
 
@@ -160,7 +188,7 @@ setInterval(() => {
160
188
  }, 1000);
161
189
  ```
162
190
 
163
- Reloading static routes only impact the next request. In-flight requests continue to use the old static routes. After in-flight requests to old static routes are finished, the old static routes are freed from memory.
191
+ Reloading routes only impact the next request. In-flight requests continue to use the old routes. After in-flight requests to old routes are finished, the old routes are freed from memory.
164
192
 
165
193
  To simplify error handling, static routes do not support streaming response bodies from `ReadableStream` or an `AsyncIterator`. Fortunately, you can still buffer the response in memory first:
166
194
 
@@ -180,6 +208,270 @@ const server = Bun.serve({
180
208
  });
181
209
  ```
182
210
 
211
+ ### Route precedence
212
+
213
+ Routes are matched in order of specificity:
214
+
215
+ 1. Exact routes (`/users/all`)
216
+ 2. Parameter routes (`/users/:id`)
217
+ 3. Wildcard routes (`/users/*`)
218
+ 4. Global catch-all (`/*`)
219
+
220
+ ```ts
221
+ Bun.serve({
222
+ routes: {
223
+ // Most specific first
224
+ "/api/users/me": () => new Response("Current user"),
225
+ "/api/users/:id": req => new Response(`User ${req.params.id}`),
226
+ "/api/*": () => new Response("API catch-all"),
227
+ "/*": () => new Response("Global catch-all"),
228
+ },
229
+ });
230
+ ```
231
+
232
+ ### Per-HTTP Method Routes
233
+
234
+ Route handlers can be specialized by HTTP method:
235
+
236
+ ```ts
237
+ Bun.serve({
238
+ routes: {
239
+ "/api/posts": {
240
+ // Different handlers per method
241
+ GET: () => new Response("List posts"),
242
+ POST: async req => {
243
+ const post = await req.json();
244
+ return Response.json({ id: crypto.randomUUID(), ...post });
245
+ },
246
+ PUT: async req => {
247
+ const updates = await req.json();
248
+ return Response.json({ updated: true, ...updates });
249
+ },
250
+ DELETE: () => new Response(null, { status: 204 }),
251
+ },
252
+ },
253
+ });
254
+ ```
255
+
256
+ You can pass any of the following methods:
257
+
258
+ | Method | Usecase example |
259
+ | --------- | ------------------------------- |
260
+ | `GET` | Fetch a resource |
261
+ | `HEAD` | Check if a resource exists |
262
+ | `OPTIONS` | Get allowed HTTP methods (CORS) |
263
+ | `DELETE` | Delete a resource |
264
+ | `PATCH` | Update a resource |
265
+ | `POST` | Create a resource |
266
+ | `PUT` | Update a resource |
267
+
268
+ When passing a function instead of an object, all methods will be handled by that function:
269
+
270
+ ```ts
271
+ const server = Bun.serve({
272
+ routes: {
273
+ "/api/version": () => Response.json({ version: "1.0.0" }),
274
+ },
275
+ });
276
+
277
+ await fetch(new URL("/api/version", server.url));
278
+ await fetch(new URL("/api/version", server.url), { method: "PUT" });
279
+ // ... etc
280
+ ```
281
+
282
+ ### Hot Route Reloading
283
+
284
+ Update routes without server restarts using `server.reload()`:
285
+
286
+ ```ts
287
+ const server = Bun.serve({
288
+ routes: {
289
+ "/api/version": () => Response.json({ version: "1.0.0" }),
290
+ },
291
+ });
292
+
293
+ // Deploy new routes without downtime
294
+ server.reload({
295
+ routes: {
296
+ "/api/version": () => Response.json({ version: "2.0.0" }),
297
+ },
298
+ });
299
+ ```
300
+
301
+ ### Error Handling
302
+
303
+ Bun provides structured error handling for routes:
304
+
305
+ ```ts
306
+ Bun.serve({
307
+ routes: {
308
+ // Errors are caught automatically
309
+ "/api/risky": () => {
310
+ throw new Error("Something went wrong");
311
+ },
312
+ },
313
+ // Global error handler
314
+ error(error) {
315
+ console.error(error);
316
+ return new Response(`Internal Error: ${error.message}`, {
317
+ status: 500,
318
+ headers: {
319
+ "Content-Type": "text/plain",
320
+ },
321
+ });
322
+ },
323
+ });
324
+ ```
325
+
326
+ ### HTML imports
327
+
328
+ To add a client-side single-page app, you can use an HTML import:
329
+
330
+ ```ts
331
+ import myReactSinglePageApp from "./index.html";
332
+
333
+ Bun.serve({
334
+ routes: {
335
+ "/": myReactSinglePageApp,
336
+ },
337
+ });
338
+ ```
339
+
340
+ HTML imports don't just serve HTML. It's a full-featured frontend bundler, transpiler, and toolkit built using Bun's [bundler](https://bun.sh/docs/bundler), JavaScript transpiler and CSS parser.
341
+
342
+ You can use this to build a full-featured frontend with React, TypeScript, Tailwind CSS, and more. Check out [/docs/bundler/fullstack](https://bun.sh/docs/bundler/fullstack) to learn more.
343
+
344
+ ### Practical example: REST API
345
+
346
+ Here's a basic database-backed REST API using Bun's router with zero dependencies:
347
+
348
+ {% codetabs %}
349
+
350
+ ```ts#server.ts
351
+ import type { Post } from "./types.ts";
352
+ import { Database } from "bun:sqlite";
353
+
354
+ const db = new Database("posts.db");
355
+ db.exec(`
356
+ CREATE TABLE IF NOT EXISTS posts (
357
+ id TEXT PRIMARY KEY,
358
+ title TEXT NOT NULL,
359
+ content TEXT NOT NULL,
360
+ created_at TEXT NOT NULL
361
+ )
362
+ `);
363
+
364
+ Bun.serve({
365
+ routes: {
366
+ // List posts
367
+ "/api/posts": {
368
+ GET: () => {
369
+ const posts = db.query("SELECT * FROM posts").all();
370
+ return Response.json(posts);
371
+ },
372
+
373
+ // Create post
374
+ POST: async req => {
375
+ const post: Omit<Post, "id" | "created_at"> = await req.json();
376
+ const id = crypto.randomUUID();
377
+
378
+ db.query(
379
+ `INSERT INTO posts (id, title, content, created_at)
380
+ VALUES (?, ?, ?, ?)`,
381
+ ).run(id, post.title, post.content, new Date().toISOString());
382
+
383
+ return Response.json({ id, ...post }, { status: 201 });
384
+ },
385
+ },
386
+
387
+ // Get post by ID
388
+ "/api/posts/:id": req => {
389
+ const post = db
390
+ .query("SELECT * FROM posts WHERE id = ?")
391
+ .get(req.params.id);
392
+
393
+ if (!post) {
394
+ return new Response("Not Found", { status: 404 });
395
+ }
396
+
397
+ return Response.json(post);
398
+ },
399
+ },
400
+
401
+ error(error) {
402
+ console.error(error);
403
+ return new Response("Internal Server Error", { status: 500 });
404
+ },
405
+ });
406
+ ```
407
+
408
+ ```ts#types.ts
409
+ export interface Post {
410
+ id: string;
411
+ title: string;
412
+ content: string;
413
+ created_at: string;
414
+ }
415
+ ```
416
+
417
+ {% /codetabs %}
418
+
419
+ ### Routing performance
420
+
421
+ `Bun.serve()`'s router builds on top uWebSocket's [tree-based approach](https://github.com/oven-sh/bun/blob/0d1a00fa0f7830f8ecd99c027fce8096c9d459b6/packages/bun-uws/src/HttpRouter.h#L57-L64) to add [SIMD-accelerated route parameter decoding](https://github.com/oven-sh/bun/blob/jarred/optional-fetch/src/bun.js/bindings/decodeURIComponentSIMD.cpp#L21-L271) and [JavaScriptCore structure caching](https://github.com/oven-sh/bun/blob/jarred/optional-fetch/src/bun.js/bindings/ServerRouteList.cpp#L100-L101) to push the performance limits of what modern hardware allows.
422
+
423
+ ### `fetch` request handler
424
+
425
+ The `fetch` handler handles incoming requests that weren't matched by any route. It receives a [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) object and returns a [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) or [`Promise<Response>`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
426
+
427
+ ```ts
428
+ Bun.serve({
429
+ fetch(req) {
430
+ const url = new URL(req.url);
431
+ if (url.pathname === "/") return new Response("Home page!");
432
+ if (url.pathname === "/blog") return new Response("Blog!");
433
+ return new Response("404!");
434
+ },
435
+ });
436
+ ```
437
+
438
+ The `fetch` handler supports async/await:
439
+
440
+ ```ts
441
+ import { sleep, serve } from "bun";
442
+ serve({
443
+ async fetch(req) {
444
+ const start = performance.now();
445
+ await sleep(10);
446
+ const end = performance.now();
447
+ return new Response(`Slept for ${end - start}ms`);
448
+ },
449
+ });
450
+ ```
451
+
452
+ Promise-based responses are also supported:
453
+
454
+ ```ts
455
+ Bun.serve({
456
+ fetch(req) {
457
+ // Forward the request to another server.
458
+ return fetch("https://example.com");
459
+ },
460
+ });
461
+ ```
462
+
463
+ You can also access the `Server` object from the `fetch` handler. It's the second argument passed to the `fetch` function.
464
+
465
+ ```ts
466
+ // `server` is passed in as the second argument to `fetch`.
467
+ const server = Bun.serve({
468
+ fetch(req, server) {
469
+ const ip = server.requestIP(req);
470
+ return new Response(`Your IP is ${ip}`);
471
+ },
472
+ });
473
+ ```
474
+
183
475
  ### Changing the `port` and `hostname`
184
476
 
185
477
  To configure which port and hostname the server will listen on, set `port` and `hostname` in the options object.
@@ -553,7 +845,7 @@ Update the server's handlers without restarting:
553
845
 
554
846
  ```ts
555
847
  const server = Bun.serve({
556
- static: {
848
+ routes: {
557
849
  "/api/version": Response.json({ version: "v1" }),
558
850
  },
559
851
  fetch(req) {
@@ -563,7 +855,7 @@ const server = Bun.serve({
563
855
 
564
856
  // Update to new handler
565
857
  server.reload({
566
- static: {
858
+ routes: {
567
859
  "/api/version": Response.json({ version: "v2" }),
568
860
  },
569
861
  fetch(req) {
@@ -572,7 +864,7 @@ server.reload({
572
864
  });
573
865
  ```
574
866
 
575
- This is useful for development and hot reloading. Only `fetch`, `error`, and `static` handlers can be updated.
867
+ This is useful for development and hot reloading. Only `fetch`, `error`, and `routes` can be updated.
576
868
 
577
869
  ## Per-Request Controls
578
870
 
package/docs/api/spawn.md CHANGED
@@ -110,7 +110,7 @@ You can read results from the subprocess via the `stdout` and `stderr` propertie
110
110
  ```ts
111
111
  const proc = Bun.spawn(["bun", "--version"]);
112
112
  const text = await new Response(proc.stdout).text();
113
- console.log(text); // => "1.2.3-canary.20250216T140609"
113
+ console.log(text); // => "1.2.3-canary.20250218T140705"
114
114
  ```
115
115
 
116
116
  Configure the output stream by passing one of the following values to `stdout/stderr`:
package/docs/api/sql.md CHANGED
@@ -185,11 +185,19 @@ Note that simple queries cannot use parameters (`${value}`). If you need paramet
185
185
 
186
186
  ### Unsafe Queries
187
187
 
188
- You can use the `sql.unsafe` function to execute raw SQL strings. Use this with caution, as it will not escape user input.
188
+ You can use the `sql.unsafe` function to execute raw SQL strings. Use this with caution, as it will not escape user input. Executing more than one command per query is allowed if no parameters are used.
189
189
 
190
190
  ```ts
191
+ // Multiple commands without parameters
192
+ const result = await sql.unsafe(`
193
+ SELECT ${userColumns} FROM users;
194
+ SELECT ${accountColumns} FROM accounts;
195
+ `);
196
+
197
+ // Using parameters (only one command is allowed)
191
198
  const result = await sql.unsafe(
192
- "SELECT " + columns + " FROM users WHERE id = " + id,
199
+ "SELECT " + dangerous + " FROM users WHERE id = $1",
200
+ [id],
193
201
  );
194
202
  ```
195
203
 
@@ -451,28 +459,29 @@ try {
451
459
 
452
460
  ## Prepared Statements
453
461
 
454
- By default, Bun's SQL client automatically creates prepared statements for queries where it can be inferred that the query is static. This provides better performance and security. However, you can disable prepared statements by setting `prepare: false` in the connection options:
462
+ By default, Bun's SQL client automatically creates named prepared statements for queries where it can be inferred that the query is static. This provides better performance. However, you can change this behavior by setting `prepare: false` in the connection options:
455
463
 
456
464
  ```ts
457
465
  const sql = new SQL({
458
466
  // ... other options ...
459
- prepare: false, // Disable prepared statements
467
+ prepare: false, // Disable persisting named prepared statements on the server
460
468
  });
461
469
  ```
462
470
 
463
- When prepared statements are disabled:
471
+ When `prepare: false` is set:
472
+
473
+ Queries are still executed using the "extended" protocol, but they are executed using [unnamed prepared statements](https://www.postgresql.org/docs/current/protocol-flow.html#PROTOCOL-FLOW-EXT-QUERY), an unnamed prepared statement lasts only until the next Parse statement specifying the unnamed statement as destination is issued.
464
474
 
465
- - Queries are executed using simple query protocol
466
- - Each query is sent to the server as a raw SQL string
467
- - Multiple statements can be executed in a single query (using `sql``.simple()`)
468
- - Parameter binding is still safe against SQL injection, but simple queries cannot include parameters
475
+ - Parameter binding is still safe against SQL injection
469
476
  - Each query is parsed and planned from scratch by the server
477
+ - Queries will not be [pipelined](https://www.postgresql.org/docs/current/protocol-flow.html#PROTOCOL-FLOW-PIPELINING)
470
478
 
471
- You might want to disable prepared statements when:
479
+ You might want to use `prepare: false` when:
472
480
 
473
481
  - Using PGBouncer in transaction mode (though since PGBouncer 1.21.0, protocol-level named prepared statements are supported when configured properly)
474
482
  - Debugging query execution plans
475
483
  - Working with dynamic SQL where query plans need to be regenerated frequently
484
+ - More than one command per query will not be supported (unless you use `sql``.simple()`)
476
485
 
477
486
  Note that disabling prepared statements may impact performance for queries that are executed frequently with different parameters, as the server needs to parse and plan each query from scratch.
478
487
 
@@ -511,6 +520,7 @@ The client provides typed errors for different failure scenarios:
511
520
  | `ERR_POSTGRES_SERVER_ERROR` | General error from PostgreSQL server |
512
521
  | `ERR_POSTGRES_INVALID_QUERY_BINDING` | Invalid parameter binding |
513
522
  | `ERR_POSTGRES_QUERY_CANCELLED` | Query was cancelled |
523
+ | `ERR_POSTGRES_NOT_TAGGED_CALL` | Query was called without a tagged call |
514
524
 
515
525
  ### Data Type Errors
516
526
 
@@ -3,26 +3,34 @@ Using `Bun.serve()`'s `routes` option, you can run your frontend and backend in
3
3
  To get started, import HTML files and pass them to the `routes` option in `Bun.serve()`.
4
4
 
5
5
  ```ts
6
+ import { sql, serve } from "bun";
6
7
  import dashboard from "./dashboard.html";
7
8
  import homepage from "./index.html";
8
9
 
9
- const server = Bun.serve({
10
- // Add HTML imports to `routes` (before Bun v1.2.3, this was called `static`)
10
+ const server = serve({
11
11
  routes: {
12
- // Bundle & route index.html to "/"
12
+ // ** HTML imports **
13
+ // Bundle & route index.html to "/". This uses HTMLRewriter to scan the HTML for `<script>` and `<link>` tags, run's Bun's JavaScript & CSS bundler on them, transpiles any TypeScript, JSX, and TSX, downlevels CSS with Bun's CSS parser and serves the result.
13
14
  "/": homepage,
14
15
  // Bundle & route dashboard.html to "/dashboard"
15
16
  "/dashboard": dashboard,
16
17
 
17
- // API endpoints:
18
- "/api/users": async req => {
19
- const users = await Bun.sql`SELECT * FROM users`;
20
- return Response.json(users);
18
+ // ** API endpoints ** (Bun v1.2.3+ required)
19
+ "/api/users": {
20
+ async GET(req) {
21
+ const users = await sql`SELECT * FROM users`;
22
+ return Response.json(users);
23
+ },
24
+ async POST(req) {
25
+ const { name, email } = await req.json();
26
+ const [user] =
27
+ await sql`INSERT INTO users (name, email) VALUES (${name}, ${email})`;
28
+ return Response.json(user);
29
+ },
21
30
  },
22
-
23
31
  "/api/users/:id": async req => {
24
32
  const { id } = req.params;
25
- const [user] = await Bun.sql`SELECT * FROM users WHERE id = ${id}`;
33
+ const [user] = await sql`SELECT * FROM users WHERE id = ${id}`;
26
34
  return Response.json(user);
27
35
  },
28
36
  },
@@ -32,11 +40,11 @@ const server = Bun.serve({
32
40
  // - Hot reloading (Bun v1.2.3+ required)
33
41
  development: true,
34
42
 
35
- // Handle API requests
36
- async fetch(req) {
37
- // Return 404 for unmatched routes
38
- return new Response("Not Found", { status: 404 });
39
- },
43
+ // Prior to v1.2.3, the `fetch` option was used to handle all API requests. It is now optional.
44
+ // async fetch(req) {
45
+ // // Return 404 for unmatched routes
46
+ // return new Response("Not Found", { status: 404 });
47
+ // },
40
48
  });
41
49
 
42
50
  console.log(`Listening on ${server.url}`);