bun-types 1.2.3-canary.20250215T140625 → 1.2.3-canary.20250217T140554
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/bun.d.ts +268 -81
- package/docs/api/fetch.md +1 -1
- package/docs/api/http.md +373 -81
- package/docs/api/spawn.md +1 -1
- package/docs/api/sql.md +57 -3
- package/docs/bundler/fullstack.md +38 -22
- package/docs/bundler/html.md +1 -1
- package/docs/cli/publish.md +1 -1
- package/docs/guides/ecosystem/nuxt.md +1 -1
- package/docs/guides/install/add-peer.md +2 -2
- package/docs/guides/install/from-npm-install-to-bun-install.md +1 -1
- package/docs/guides/test/run-tests.md +3 -3
- package/docs/guides/test/snapshot.md +3 -3
- package/docs/guides/test/update-snapshots.md +1 -1
- package/docs/guides/util/version.md +1 -1
- package/docs/installation.md +4 -4
- package/docs/runtime/debugger.md +3 -3
- package/docs/test/dom.md +1 -1
- package/package.json +1 -1
package/bun.d.ts
CHANGED
|
@@ -465,55 +465,6 @@ declare module "bun" {
|
|
|
465
465
|
}
|
|
466
466
|
const TOML: TOML;
|
|
467
467
|
|
|
468
|
-
type Serve<WebSocketDataType = undefined> =
|
|
469
|
-
| ServeOptions
|
|
470
|
-
| TLSServeOptions
|
|
471
|
-
| UnixServeOptions
|
|
472
|
-
| UnixTLSServeOptions
|
|
473
|
-
| WebSocketServeOptions<WebSocketDataType>
|
|
474
|
-
| TLSWebSocketServeOptions<WebSocketDataType>
|
|
475
|
-
| UnixWebSocketServeOptions<WebSocketDataType>
|
|
476
|
-
| UnixTLSWebSocketServeOptions<WebSocketDataType>;
|
|
477
|
-
|
|
478
|
-
/**
|
|
479
|
-
* Start a fast HTTP server.
|
|
480
|
-
*
|
|
481
|
-
* @param options Server options (port defaults to $PORT || 3000)
|
|
482
|
-
*
|
|
483
|
-
* -----
|
|
484
|
-
*
|
|
485
|
-
* @example
|
|
486
|
-
*
|
|
487
|
-
* ```ts
|
|
488
|
-
* Bun.serve({
|
|
489
|
-
* fetch(req: Request): Response | Promise<Response> {
|
|
490
|
-
* return new Response("Hello World!");
|
|
491
|
-
* },
|
|
492
|
-
*
|
|
493
|
-
* // Optional port number - the default value is 3000
|
|
494
|
-
* port: process.env.PORT || 3000,
|
|
495
|
-
* });
|
|
496
|
-
* ```
|
|
497
|
-
* -----
|
|
498
|
-
*
|
|
499
|
-
* @example
|
|
500
|
-
*
|
|
501
|
-
* Send a file
|
|
502
|
-
*
|
|
503
|
-
* ```ts
|
|
504
|
-
* Bun.serve({
|
|
505
|
-
* fetch(req: Request): Response | Promise<Response> {
|
|
506
|
-
* return new Response(Bun.file("./package.json"));
|
|
507
|
-
* },
|
|
508
|
-
*
|
|
509
|
-
* // Optional port number - the default value is 3000
|
|
510
|
-
* port: process.env.PORT || 3000,
|
|
511
|
-
* });
|
|
512
|
-
* ```
|
|
513
|
-
*/
|
|
514
|
-
// eslint-disable-next-line @definitelytyped/no-unnecessary-generics
|
|
515
|
-
function serve<T>(options: Serve<T>): Server;
|
|
516
|
-
|
|
517
468
|
/**
|
|
518
469
|
* Synchronously resolve a `moduleId` as though it were imported from `parent`
|
|
519
470
|
*
|
|
@@ -3805,6 +3756,45 @@ declare module "bun" {
|
|
|
3805
3756
|
};
|
|
3806
3757
|
}
|
|
3807
3758
|
|
|
3759
|
+
namespace RouterTypes {
|
|
3760
|
+
type ExtractRouteParams<T> =
|
|
3761
|
+
T extends `${string}:${infer Param}/${infer Rest}`
|
|
3762
|
+
? { [K in Param]: string } & ExtractRouteParams<Rest>
|
|
3763
|
+
: T extends `${string}:${infer Param}`
|
|
3764
|
+
? { [K in Param]: string }
|
|
3765
|
+
: T extends `${string}*`
|
|
3766
|
+
? {}
|
|
3767
|
+
: {};
|
|
3768
|
+
|
|
3769
|
+
type RouteHandler<T extends string> = (
|
|
3770
|
+
req: BunRequest<T>,
|
|
3771
|
+
server: Server,
|
|
3772
|
+
) => Response | Promise<Response>;
|
|
3773
|
+
|
|
3774
|
+
type HTTPMethod =
|
|
3775
|
+
| "GET"
|
|
3776
|
+
| "POST"
|
|
3777
|
+
| "PUT"
|
|
3778
|
+
| "DELETE"
|
|
3779
|
+
| "PATCH"
|
|
3780
|
+
| "HEAD"
|
|
3781
|
+
| "OPTIONS";
|
|
3782
|
+
|
|
3783
|
+
type RouteHandlerObject<T extends string> = {
|
|
3784
|
+
[K in HTTPMethod]?: RouteHandler<T>;
|
|
3785
|
+
};
|
|
3786
|
+
|
|
3787
|
+
type RouteValue<T extends string> =
|
|
3788
|
+
| Response
|
|
3789
|
+
| false
|
|
3790
|
+
| RouteHandler<T>
|
|
3791
|
+
| RouteHandlerObject<T>;
|
|
3792
|
+
}
|
|
3793
|
+
|
|
3794
|
+
interface BunRequest<T extends string = string> extends Request {
|
|
3795
|
+
params: RouterTypes.ExtractRouteParams<T>;
|
|
3796
|
+
}
|
|
3797
|
+
|
|
3808
3798
|
interface GenericServeOptions {
|
|
3809
3799
|
/**
|
|
3810
3800
|
* What URI should be used to make {@link Request.url} absolute?
|
|
@@ -3869,37 +3859,6 @@ declare module "bun" {
|
|
|
3869
3859
|
* This string will currently do nothing. But in the future it could be useful for logs or metrics.
|
|
3870
3860
|
*/
|
|
3871
3861
|
id?: string | null;
|
|
3872
|
-
|
|
3873
|
-
/**
|
|
3874
|
-
* Server static Response objects by route.
|
|
3875
|
-
*
|
|
3876
|
-
* @example
|
|
3877
|
-
* ```ts
|
|
3878
|
-
* Bun.serve({
|
|
3879
|
-
* static: {
|
|
3880
|
-
* "/": new Response("Hello World"),
|
|
3881
|
-
* "/about": new Response("About"),
|
|
3882
|
-
* },
|
|
3883
|
-
* fetch(req) {
|
|
3884
|
-
* return new Response("Fallback response");
|
|
3885
|
-
* },
|
|
3886
|
-
* });
|
|
3887
|
-
* ```
|
|
3888
|
-
*
|
|
3889
|
-
* @experimental
|
|
3890
|
-
*/
|
|
3891
|
-
static?: Record<
|
|
3892
|
-
`/${string}`,
|
|
3893
|
-
| Response
|
|
3894
|
-
/**
|
|
3895
|
-
* An HTML import.
|
|
3896
|
-
*/
|
|
3897
|
-
| HTMLBundle
|
|
3898
|
-
/**
|
|
3899
|
-
* false to force fetch() to handle the route
|
|
3900
|
-
*/
|
|
3901
|
-
| false
|
|
3902
|
-
>;
|
|
3903
3862
|
}
|
|
3904
3863
|
|
|
3905
3864
|
interface ServeOptions extends GenericServeOptions {
|
|
@@ -4305,7 +4264,32 @@ declare module "bun" {
|
|
|
4305
4264
|
*
|
|
4306
4265
|
* Passing other options such as `port` or `hostname` won't do anything.
|
|
4307
4266
|
*/
|
|
4308
|
-
reload
|
|
4267
|
+
reload<T, R extends { [K in keyof R]: RouterTypes.RouteValue<K & string> }>(
|
|
4268
|
+
options: (
|
|
4269
|
+
| (Omit<ServeOptions, "fetch"> & {
|
|
4270
|
+
routes: R;
|
|
4271
|
+
fetch?: (
|
|
4272
|
+
this: Server,
|
|
4273
|
+
request: Request,
|
|
4274
|
+
server: Server,
|
|
4275
|
+
) => Response | Promise<Response>;
|
|
4276
|
+
})
|
|
4277
|
+
| (Omit<ServeOptions, "routes"> & {
|
|
4278
|
+
routes?: never;
|
|
4279
|
+
fetch: (
|
|
4280
|
+
this: Server,
|
|
4281
|
+
request: Request,
|
|
4282
|
+
server: Server,
|
|
4283
|
+
) => Response | Promise<Response>;
|
|
4284
|
+
})
|
|
4285
|
+
| WebSocketServeOptions<T>
|
|
4286
|
+
) & {
|
|
4287
|
+
/**
|
|
4288
|
+
* @deprecated Use `routes` instead in new code. This will continue to work for awhile though.
|
|
4289
|
+
*/
|
|
4290
|
+
static?: R;
|
|
4291
|
+
},
|
|
4292
|
+
): Server;
|
|
4309
4293
|
|
|
4310
4294
|
/**
|
|
4311
4295
|
* Mock the fetch handler for a running server.
|
|
@@ -4503,6 +4487,209 @@ declare module "bun" {
|
|
|
4503
4487
|
readonly id: string;
|
|
4504
4488
|
}
|
|
4505
4489
|
|
|
4490
|
+
type Serve<WebSocketDataType = undefined> =
|
|
4491
|
+
| ServeOptions
|
|
4492
|
+
| TLSServeOptions
|
|
4493
|
+
| UnixServeOptions
|
|
4494
|
+
| UnixTLSServeOptions
|
|
4495
|
+
| WebSocketServeOptions<WebSocketDataType>
|
|
4496
|
+
| TLSWebSocketServeOptions<WebSocketDataType>
|
|
4497
|
+
| UnixWebSocketServeOptions<WebSocketDataType>
|
|
4498
|
+
| UnixTLSWebSocketServeOptions<WebSocketDataType>;
|
|
4499
|
+
|
|
4500
|
+
/**
|
|
4501
|
+
Bun.serve provides a high-performance HTTP server with built-in routing support.
|
|
4502
|
+
It enables both function-based and object-based route handlers with type-safe
|
|
4503
|
+
parameters and method-specific handling.
|
|
4504
|
+
|
|
4505
|
+
@example Basic Usage
|
|
4506
|
+
```ts
|
|
4507
|
+
Bun.serve({
|
|
4508
|
+
port: 3000,
|
|
4509
|
+
fetch(req) {
|
|
4510
|
+
return new Response("Hello World");
|
|
4511
|
+
}
|
|
4512
|
+
});
|
|
4513
|
+
```
|
|
4514
|
+
|
|
4515
|
+
@example Route-based Handlers
|
|
4516
|
+
```ts
|
|
4517
|
+
Bun.serve({
|
|
4518
|
+
routes: {
|
|
4519
|
+
// Static responses
|
|
4520
|
+
"/": new Response("Home page"),
|
|
4521
|
+
|
|
4522
|
+
// Function handlers with type-safe parameters
|
|
4523
|
+
"/users/:id": (req) => {
|
|
4524
|
+
// req.params.id is typed as string
|
|
4525
|
+
return new Response(`User ${req.params.id}`);
|
|
4526
|
+
},
|
|
4527
|
+
|
|
4528
|
+
// Method-specific handlers
|
|
4529
|
+
"/api/posts": {
|
|
4530
|
+
GET: () => new Response("Get posts"),
|
|
4531
|
+
POST: async (req) => {
|
|
4532
|
+
const body = await req.json();
|
|
4533
|
+
return new Response("Created post");
|
|
4534
|
+
},
|
|
4535
|
+
DELETE: (req) => new Response("Deleted post")
|
|
4536
|
+
},
|
|
4537
|
+
|
|
4538
|
+
// Wildcard routes
|
|
4539
|
+
"/static/*": (req) => {
|
|
4540
|
+
// Handle any path under /static/
|
|
4541
|
+
return new Response("Static file");
|
|
4542
|
+
},
|
|
4543
|
+
|
|
4544
|
+
// Disable route (fall through to fetch handler)
|
|
4545
|
+
"/api/legacy": false
|
|
4546
|
+
},
|
|
4547
|
+
|
|
4548
|
+
// Fallback handler for unmatched routes
|
|
4549
|
+
fetch(req) {
|
|
4550
|
+
return new Response("Not Found", { status: 404 });
|
|
4551
|
+
}
|
|
4552
|
+
});
|
|
4553
|
+
```
|
|
4554
|
+
|
|
4555
|
+
@example Path Parameters
|
|
4556
|
+
```ts
|
|
4557
|
+
Bun.serve({
|
|
4558
|
+
routes: {
|
|
4559
|
+
// Single parameter
|
|
4560
|
+
"/users/:id": (req: BunRequest<"/users/:id">) => {
|
|
4561
|
+
return new Response(`User ID: ${req.params.id}`);
|
|
4562
|
+
},
|
|
4563
|
+
|
|
4564
|
+
// Multiple parameters
|
|
4565
|
+
"/posts/:postId/comments/:commentId": (
|
|
4566
|
+
req: BunRequest<"/posts/:postId/comments/:commentId">
|
|
4567
|
+
) => {
|
|
4568
|
+
return new Response(JSON.stringify(req.params));
|
|
4569
|
+
// Output: {"postId": "123", "commentId": "456"}
|
|
4570
|
+
}
|
|
4571
|
+
}
|
|
4572
|
+
});
|
|
4573
|
+
```
|
|
4574
|
+
|
|
4575
|
+
@example Route Precedence
|
|
4576
|
+
```ts
|
|
4577
|
+
// Routes are matched in the following order:
|
|
4578
|
+
// 1. Exact static routes ("/about")
|
|
4579
|
+
// 2. Parameter routes ("/users/:id")
|
|
4580
|
+
// 3. Wildcard routes ("/api/*")
|
|
4581
|
+
|
|
4582
|
+
Bun.serve({
|
|
4583
|
+
routes: {
|
|
4584
|
+
"/api/users": () => new Response("Users list"),
|
|
4585
|
+
"/api/users/:id": (req) => new Response(`User ${req.params.id}`),
|
|
4586
|
+
"/api/*": () => new Response("API catchall"),
|
|
4587
|
+
"/*": () => new Response("Root catchall")
|
|
4588
|
+
}
|
|
4589
|
+
});
|
|
4590
|
+
```
|
|
4591
|
+
|
|
4592
|
+
@example Error Handling
|
|
4593
|
+
```ts
|
|
4594
|
+
Bun.serve({
|
|
4595
|
+
routes: {
|
|
4596
|
+
"/error": () => {
|
|
4597
|
+
throw new Error("Something went wrong");
|
|
4598
|
+
}
|
|
4599
|
+
},
|
|
4600
|
+
error(error) {
|
|
4601
|
+
// Custom error handler
|
|
4602
|
+
console.error(error);
|
|
4603
|
+
return new Response(`Error: ${error.message}`, {
|
|
4604
|
+
status: 500
|
|
4605
|
+
});
|
|
4606
|
+
}
|
|
4607
|
+
});
|
|
4608
|
+
```
|
|
4609
|
+
|
|
4610
|
+
@example Server Lifecycle
|
|
4611
|
+
```ts
|
|
4612
|
+
const server = Bun.serve({
|
|
4613
|
+
// Server config...
|
|
4614
|
+
});
|
|
4615
|
+
|
|
4616
|
+
// Update routes at runtime
|
|
4617
|
+
server.reload({
|
|
4618
|
+
routes: {
|
|
4619
|
+
"/": () => new Response("Updated route")
|
|
4620
|
+
}
|
|
4621
|
+
});
|
|
4622
|
+
|
|
4623
|
+
// Stop the server
|
|
4624
|
+
server.stop();
|
|
4625
|
+
```
|
|
4626
|
+
|
|
4627
|
+
@example Development Mode
|
|
4628
|
+
```ts
|
|
4629
|
+
Bun.serve({
|
|
4630
|
+
development: true, // Enable hot reloading
|
|
4631
|
+
routes: {
|
|
4632
|
+
// Routes will auto-reload on changes
|
|
4633
|
+
}
|
|
4634
|
+
});
|
|
4635
|
+
```
|
|
4636
|
+
|
|
4637
|
+
@example Type-Safe Request Handling
|
|
4638
|
+
```ts
|
|
4639
|
+
type Post = {
|
|
4640
|
+
id: string;
|
|
4641
|
+
title: string;
|
|
4642
|
+
};
|
|
4643
|
+
|
|
4644
|
+
Bun.serve({
|
|
4645
|
+
routes: {
|
|
4646
|
+
"/api/posts/:id": async (
|
|
4647
|
+
req: BunRequest<"/api/posts/:id">
|
|
4648
|
+
) => {
|
|
4649
|
+
if (req.method === "POST") {
|
|
4650
|
+
const body: Post = await req.json();
|
|
4651
|
+
return Response.json(body);
|
|
4652
|
+
}
|
|
4653
|
+
return new Response("Method not allowed", {
|
|
4654
|
+
status: 405
|
|
4655
|
+
});
|
|
4656
|
+
}
|
|
4657
|
+
}
|
|
4658
|
+
});
|
|
4659
|
+
```
|
|
4660
|
+
@param options - Server configuration options
|
|
4661
|
+
@param options.routes - Route definitions mapping paths to handlers
|
|
4662
|
+
*/
|
|
4663
|
+
function serve<
|
|
4664
|
+
T,
|
|
4665
|
+
R extends { [K in keyof R]: RouterTypes.RouteValue<K & string> },
|
|
4666
|
+
>(
|
|
4667
|
+
options: (
|
|
4668
|
+
| (Omit<ServeOptions, "fetch"> & {
|
|
4669
|
+
routes: R;
|
|
4670
|
+
fetch?: (
|
|
4671
|
+
this: Server,
|
|
4672
|
+
request: Request,
|
|
4673
|
+
server: Server,
|
|
4674
|
+
) => Response | Promise<Response>;
|
|
4675
|
+
})
|
|
4676
|
+
| (Omit<ServeOptions, "routes"> & {
|
|
4677
|
+
routes?: never;
|
|
4678
|
+
fetch: (
|
|
4679
|
+
this: Server,
|
|
4680
|
+
request: Request,
|
|
4681
|
+
server: Server,
|
|
4682
|
+
) => Response | Promise<Response>;
|
|
4683
|
+
})
|
|
4684
|
+
| WebSocketServeOptions<T>
|
|
4685
|
+
) & {
|
|
4686
|
+
/**
|
|
4687
|
+
* @deprecated Use `routes` instead in new code. This will continue to work for awhile though.
|
|
4688
|
+
*/
|
|
4689
|
+
static?: R;
|
|
4690
|
+
},
|
|
4691
|
+
): Server;
|
|
4692
|
+
|
|
4506
4693
|
/**
|
|
4507
4694
|
* [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) powered by the fastest system calls available for operating on files.
|
|
4508
4695
|
*
|
package/docs/api/fetch.md
CHANGED
|
@@ -337,7 +337,7 @@ This will print the request and response headers to your terminal:
|
|
|
337
337
|
```sh
|
|
338
338
|
[fetch] > HTTP/1.1 GET http://example.com/
|
|
339
339
|
[fetch] > Connection: keep-alive
|
|
340
|
-
[fetch] > User-Agent: Bun/1.2.3-canary.
|
|
340
|
+
[fetch] > User-Agent: Bun/1.2.3-canary.20250217T140554
|
|
341
341
|
[fetch] > Accept: */*
|
|
342
342
|
[fetch] > Host: example.com
|
|
343
343
|
[fetch] > Accept-Encoding: gzip, deflate, br
|
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
|
-
|
|
11
|
+
Use `Bun.serve` to start an HTTP server in Bun.
|
|
12
12
|
|
|
13
13
|
```ts
|
|
14
14
|
Bun.serve({
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
26
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
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
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
85
|
+
#### Promise in routes
|
|
86
|
+
|
|
87
|
+
You can also return a `Promise<Response>` from a route handler.
|
|
62
88
|
|
|
63
89
|
```ts
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
|
|
106
|
+
#### Type-safe route parameters
|
|
74
107
|
|
|
75
|
-
|
|
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
|
-
|
|
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
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
-
|
|
107
|
-
|
|
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
|
-
|
|
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
|
-
|
|
117
|
-
|
|
140
|
+
routes: {
|
|
141
|
+
// Health checks
|
|
142
|
+
"/health": new Response("OK"),
|
|
143
|
+
"/ready": new Response("Ready", {
|
|
118
144
|
headers: {
|
|
119
|
-
|
|
145
|
+
// Pass custom headers
|
|
146
|
+
"X-Ready": "1",
|
|
120
147
|
},
|
|
121
148
|
}),
|
|
122
|
-
},
|
|
123
149
|
|
|
124
|
-
|
|
125
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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 `
|
|
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.
|
|
113
|
+
console.log(text); // => "1.2.3-canary.20250217T140554"
|
|
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
|
@@ -165,13 +165,39 @@ await sql`
|
|
|
165
165
|
`;
|
|
166
166
|
```
|
|
167
167
|
|
|
168
|
+
## `sql``.simple()`
|
|
169
|
+
|
|
170
|
+
The PostgreSQL wire protocol supports two types of queries: "simple" and "extended". Simple queries can contain multiple statements but don't support parameters, while extended queries (the default) support parameters but only allow one statement.
|
|
171
|
+
|
|
172
|
+
To run multiple statements in a single query, use `sql``.simple()`:
|
|
173
|
+
|
|
174
|
+
```ts
|
|
175
|
+
// Multiple statements in one query
|
|
176
|
+
await sql`
|
|
177
|
+
SELECT 1;
|
|
178
|
+
SELECT 2;
|
|
179
|
+
`.simple();
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Simple queries are often useful for database migrations and setup scripts.
|
|
183
|
+
|
|
184
|
+
Note that simple queries cannot use parameters (`${value}`). If you need parameters, you must split your query into separate statements.
|
|
185
|
+
|
|
168
186
|
### Unsafe Queries
|
|
169
187
|
|
|
170
|
-
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.
|
|
171
189
|
|
|
172
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)
|
|
173
198
|
const result = await sql.unsafe(
|
|
174
|
-
"SELECT " +
|
|
199
|
+
"SELECT " + dangerous + " FROM users WHERE id = $1",
|
|
200
|
+
[id],
|
|
175
201
|
);
|
|
176
202
|
```
|
|
177
203
|
|
|
@@ -431,6 +457,34 @@ try {
|
|
|
431
457
|
} // Automatically released
|
|
432
458
|
```
|
|
433
459
|
|
|
460
|
+
## Prepared Statements
|
|
461
|
+
|
|
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:
|
|
463
|
+
|
|
464
|
+
```ts
|
|
465
|
+
const sql = new SQL({
|
|
466
|
+
// ... other options ...
|
|
467
|
+
prepare: false, // Disable persisting named prepared statements on the server
|
|
468
|
+
});
|
|
469
|
+
```
|
|
470
|
+
|
|
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.
|
|
474
|
+
|
|
475
|
+
- Parameter binding is still safe against SQL injection
|
|
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)
|
|
478
|
+
|
|
479
|
+
You might want to use `prepare: false` when:
|
|
480
|
+
|
|
481
|
+
- Using PGBouncer in transaction mode (though since PGBouncer 1.21.0, protocol-level named prepared statements are supported when configured properly)
|
|
482
|
+
- Debugging query execution plans
|
|
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()`)
|
|
485
|
+
|
|
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.
|
|
487
|
+
|
|
434
488
|
## Error Handling
|
|
435
489
|
|
|
436
490
|
The client provides typed errors for different failure scenarios:
|
|
@@ -501,7 +555,7 @@ The client provides typed errors for different failure scenarios:
|
|
|
501
555
|
|
|
502
556
|
## Numbers and BigInt
|
|
503
557
|
|
|
504
|
-
Bun's SQL client includes special handling for large numbers that exceed the range of a 53-bit integer. Here
|
|
558
|
+
Bun's SQL client includes special handling for large numbers that exceed the range of a 53-bit integer. Here's how it works:
|
|
505
559
|
|
|
506
560
|
```ts
|
|
507
561
|
import { sql } from "bun";
|
|
@@ -1,36 +1,50 @@
|
|
|
1
|
-
Using `Bun.serve()`'s `
|
|
1
|
+
Using `Bun.serve()`'s `routes` option, you can run your frontend and backend in the same app with no extra steps.
|
|
2
2
|
|
|
3
|
-
To get started, import HTML files and pass them to the `
|
|
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 =
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
// Bundle & route index.html to "/"
|
|
10
|
+
const server = serve({
|
|
11
|
+
routes: {
|
|
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,
|
|
17
|
+
|
|
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
|
+
},
|
|
30
|
+
},
|
|
31
|
+
"/api/users/:id": async req => {
|
|
32
|
+
const { id } = req.params;
|
|
33
|
+
const [user] = await sql`SELECT * FROM users WHERE id = ${id}`;
|
|
34
|
+
return Response.json(user);
|
|
35
|
+
},
|
|
16
36
|
},
|
|
17
37
|
|
|
18
38
|
// Enable development mode for:
|
|
19
39
|
// - Detailed error messages
|
|
20
|
-
// -
|
|
40
|
+
// - Hot reloading (Bun v1.2.3+ required)
|
|
21
41
|
development: true,
|
|
22
42
|
|
|
23
|
-
//
|
|
24
|
-
async fetch(req) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
return Response.json(users);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// Return 404 for unmatched routes
|
|
32
|
-
return new Response("Not Found", { status: 404 });
|
|
33
|
-
},
|
|
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
|
+
// },
|
|
34
48
|
});
|
|
35
49
|
|
|
36
50
|
console.log(`Listening on ${server.url}`);
|
|
@@ -55,7 +69,7 @@ These HTML files are used as routes in Bun's dev server you can pass to `Bun.ser
|
|
|
55
69
|
|
|
56
70
|
```ts
|
|
57
71
|
Bun.serve({
|
|
58
|
-
|
|
72
|
+
routes: {
|
|
59
73
|
"/": homepage,
|
|
60
74
|
"/dashboard": dashboard,
|
|
61
75
|
}
|
|
@@ -113,7 +127,7 @@ import dashboard from "../public/dashboard.html";
|
|
|
113
127
|
import { serve } from "bun";
|
|
114
128
|
|
|
115
129
|
serve({
|
|
116
|
-
|
|
130
|
+
routes: {
|
|
117
131
|
"/": dashboard,
|
|
118
132
|
},
|
|
119
133
|
|
|
@@ -171,7 +185,7 @@ import homepage from "./index.html";
|
|
|
171
185
|
import dashboard from "./dashboard.html";
|
|
172
186
|
|
|
173
187
|
Bun.serve({
|
|
174
|
-
|
|
188
|
+
routes: {
|
|
175
189
|
"/": homepage,
|
|
176
190
|
"/dashboard": dashboard,
|
|
177
191
|
}
|
|
@@ -249,6 +263,8 @@ plugins = ["./my-plugin-implementation.ts"]
|
|
|
249
263
|
|
|
250
264
|
Bun will lazily resolve and load each plugin and use them to bundle your routes.
|
|
251
265
|
|
|
266
|
+
Note: this is currently in `bunfig.toml` to make it possible to know statically which plugins are in use when we eventually integrate this with the `bun build` CLI. These plugins work in `Bun.build()`'s JS API, but are not yet supported in the CLI.
|
|
267
|
+
|
|
252
268
|
## How this works
|
|
253
269
|
|
|
254
270
|
Bun uses [`HTMLRewriter`](/docs/api/html-rewriter) to scan for `<script>` and `<link>` tags in HTML files, uses them as entrypoints for [Bun's bundler](/docs/bundler), generates an optimized bundle for the JavaScript/TypeScript/TSX/JSX and CSS files, and serves the result.
|
|
@@ -293,5 +309,5 @@ This works similarly to how [`Bun.build` processes HTML files](/docs/bundler/htm
|
|
|
293
309
|
|
|
294
310
|
## This is a work in progress
|
|
295
311
|
|
|
296
|
-
- Client-side hot reloading isn't wired up yet. It will be in the future.
|
|
312
|
+
- ~Client-side hot reloading isn't wired up yet. It will be in the future.~ New in Bun v1.2.3
|
|
297
313
|
- This doesn't support `bun build` yet. It also will in the future.
|
package/docs/bundler/html.md
CHANGED
|
@@ -301,6 +301,6 @@ This is a small wrapper around Bun's support for HTML imports in JavaScript.
|
|
|
301
301
|
|
|
302
302
|
### Adding a backend to your frontend
|
|
303
303
|
|
|
304
|
-
To add a backend to your frontend, you can use the `"
|
|
304
|
+
To add a backend to your frontend, you can use the `"routes"` option in `Bun.serve`.
|
|
305
305
|
|
|
306
306
|
Learn more in [the full-stack docs](/docs/bundler/fullstack).
|
package/docs/cli/publish.md
CHANGED
|
@@ -9,7 +9,7 @@ $ bunx nuxi init my-nuxt-app
|
|
|
9
9
|
✔ Which package manager would you like to use?
|
|
10
10
|
bun
|
|
11
11
|
◐ Installing dependencies...
|
|
12
|
-
bun install v1.2.3-canary.
|
|
12
|
+
bun install v1.2.3-canary.20250217T140554 (16b4bf34)
|
|
13
13
|
+ @nuxt/devtools@0.8.2
|
|
14
14
|
+ nuxt@3.7.0
|
|
15
15
|
785 packages installed [2.67s]
|
|
@@ -16,7 +16,7 @@ This will add the package to `peerDependencies` in `package.json`.
|
|
|
16
16
|
```json-diff
|
|
17
17
|
{
|
|
18
18
|
"peerDependencies": {
|
|
19
|
-
+ "@types/bun": "^1.2.3-canary.
|
|
19
|
+
+ "@types/bun": "^1.2.3-canary.20250217T140554"
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
```
|
|
@@ -28,7 +28,7 @@ Running `bun install` will install peer dependencies by default, unless marked o
|
|
|
28
28
|
```json-diff
|
|
29
29
|
{
|
|
30
30
|
"peerDependencies": {
|
|
31
|
-
"@types/bun": "^1.2.3-canary.
|
|
31
|
+
"@types/bun": "^1.2.3-canary.20250217T140554"
|
|
32
32
|
},
|
|
33
33
|
"peerDependenciesMeta": {
|
|
34
34
|
+ "@types/bun": {
|
|
@@ -97,7 +97,7 @@ $ bun update
|
|
|
97
97
|
$ bun update @types/bun --latest
|
|
98
98
|
|
|
99
99
|
# Update a dependency to a specific version
|
|
100
|
-
$ bun update @types/bun@1.2.3-canary.
|
|
100
|
+
$ bun update @types/bun@1.2.3-canary.20250217T140554
|
|
101
101
|
|
|
102
102
|
# Update all dependencies to the latest versions
|
|
103
103
|
$ bun update --latest
|
|
@@ -21,7 +21,7 @@ Here's what the output of a typical test run looks like. In this case, there are
|
|
|
21
21
|
|
|
22
22
|
```sh
|
|
23
23
|
$ bun test
|
|
24
|
-
bun test v1.2.3-canary.
|
|
24
|
+
bun test v1.2.3-canary.20250217T140554 (9c68abdb)
|
|
25
25
|
|
|
26
26
|
test.test.js:
|
|
27
27
|
✓ add [0.87ms]
|
|
@@ -47,7 +47,7 @@ To only run certain test files, pass a positional argument to `bun test`. The ru
|
|
|
47
47
|
|
|
48
48
|
```sh
|
|
49
49
|
$ bun test test3
|
|
50
|
-
bun test v1.2.3-canary.
|
|
50
|
+
bun test v1.2.3-canary.20250217T140554 (9c68abdb)
|
|
51
51
|
|
|
52
52
|
test3.test.js:
|
|
53
53
|
✓ add [1.40ms]
|
|
@@ -85,7 +85,7 @@ Adding `-t add` will only run tests with "add" in the name. This works with test
|
|
|
85
85
|
|
|
86
86
|
```sh
|
|
87
87
|
$ bun test -t add
|
|
88
|
-
bun test v1.2.3-canary.
|
|
88
|
+
bun test v1.2.3-canary.20250217T140554 (9c68abdb)
|
|
89
89
|
|
|
90
90
|
test.test.js:
|
|
91
91
|
✓ add [1.79ms]
|
|
@@ -18,7 +18,7 @@ The first time this test is executed, Bun will evaluate the value passed into `e
|
|
|
18
18
|
|
|
19
19
|
```sh
|
|
20
20
|
$ bun test test/snap
|
|
21
|
-
bun test v1.2.3-canary.
|
|
21
|
+
bun test v1.2.3-canary.20250217T140554 (9c68abdb)
|
|
22
22
|
|
|
23
23
|
test/snap.test.ts:
|
|
24
24
|
✓ snapshot [1.48ms]
|
|
@@ -61,7 +61,7 @@ Later, when this test file is executed again, Bun will read the snapshot file an
|
|
|
61
61
|
|
|
62
62
|
```sh
|
|
63
63
|
$ bun test
|
|
64
|
-
bun test v1.2.3-canary.
|
|
64
|
+
bun test v1.2.3-canary.20250217T140554 (9c68abdb)
|
|
65
65
|
|
|
66
66
|
test/snap.test.ts:
|
|
67
67
|
✓ snapshot [1.05ms]
|
|
@@ -78,7 +78,7 @@ To update snapshots, use the `--update-snapshots` flag.
|
|
|
78
78
|
|
|
79
79
|
```sh
|
|
80
80
|
$ bun test --update-snapshots
|
|
81
|
-
bun test v1.2.3-canary.
|
|
81
|
+
bun test v1.2.3-canary.20250217T140554 (9c68abdb)
|
|
82
82
|
|
|
83
83
|
test/snap.test.ts:
|
|
84
84
|
✓ snapshot [0.86ms]
|
package/docs/installation.md
CHANGED
|
@@ -14,7 +14,7 @@ Kernel version 5.6 or higher is strongly recommended, but the minimum is 5.1. Us
|
|
|
14
14
|
```bash#macOS/Linux_(curl)
|
|
15
15
|
$ curl -fsSL https://bun.sh/install | bash # for macOS, Linux, and WSL
|
|
16
16
|
# to install a specific version
|
|
17
|
-
$ curl -fsSL https://bun.sh/install | bash -s "bun-v1.2.3-canary.
|
|
17
|
+
$ curl -fsSL https://bun.sh/install | bash -s "bun-v1.2.3-canary.20250217T140554"
|
|
18
18
|
```
|
|
19
19
|
|
|
20
20
|
```bash#npm
|
|
@@ -188,10 +188,10 @@ Since Bun is a single binary, you can install older versions of Bun by re-runnin
|
|
|
188
188
|
|
|
189
189
|
### Installing a specific version of Bun on Linux/Mac
|
|
190
190
|
|
|
191
|
-
To install a specific version of Bun, you can pass the git tag of the version you want to install to the install script, such as `bun-v1.2.0` or `bun-v1.2.3-canary.
|
|
191
|
+
To install a specific version of Bun, you can pass the git tag of the version you want to install to the install script, such as `bun-v1.2.0` or `bun-v1.2.3-canary.20250217T140554`.
|
|
192
192
|
|
|
193
193
|
```sh
|
|
194
|
-
$ curl -fsSL https://bun.sh/install | bash -s "bun-v1.2.3-canary.
|
|
194
|
+
$ curl -fsSL https://bun.sh/install | bash -s "bun-v1.2.3-canary.20250217T140554"
|
|
195
195
|
```
|
|
196
196
|
|
|
197
197
|
### Installing a specific version of Bun on Windows
|
|
@@ -200,7 +200,7 @@ On Windows, you can install a specific version of Bun by passing the version num
|
|
|
200
200
|
|
|
201
201
|
```sh
|
|
202
202
|
# PowerShell:
|
|
203
|
-
$ iex "& {$(irm https://bun.sh/install.ps1)} -Version 1.2.3-canary.
|
|
203
|
+
$ iex "& {$(irm https://bun.sh/install.ps1)} -Version 1.2.3-canary.20250217T140554"
|
|
204
204
|
```
|
|
205
205
|
|
|
206
206
|
## Downloading Bun binaries directly
|
package/docs/runtime/debugger.md
CHANGED
|
@@ -124,11 +124,11 @@ await fetch("https://example.com", {
|
|
|
124
124
|
This prints the `fetch` request as a single-line `curl` command to let you copy-paste into your terminal to replicate the request.
|
|
125
125
|
|
|
126
126
|
```sh
|
|
127
|
-
[fetch] $ curl --http1.1 "https://example.com/" -X POST -H "content-type: application/json" -H "Connection: keep-alive" -H "User-Agent: Bun/1.2.3-canary.
|
|
127
|
+
[fetch] $ curl --http1.1 "https://example.com/" -X POST -H "content-type: application/json" -H "Connection: keep-alive" -H "User-Agent: Bun/1.2.3-canary.20250217T140554" -H "Accept: */*" -H "Host: example.com" -H "Accept-Encoding: gzip, deflate, br" --compressed -H "Content-Length: 13" --data-raw "{\"foo\":\"bar\"}"
|
|
128
128
|
[fetch] > HTTP/1.1 POST https://example.com/
|
|
129
129
|
[fetch] > content-type: application/json
|
|
130
130
|
[fetch] > Connection: keep-alive
|
|
131
|
-
[fetch] > User-Agent: Bun/1.2.3-canary.
|
|
131
|
+
[fetch] > User-Agent: Bun/1.2.3-canary.20250217T140554
|
|
132
132
|
[fetch] > Accept: */*
|
|
133
133
|
[fetch] > Host: example.com
|
|
134
134
|
[fetch] > Accept-Encoding: gzip, deflate, br
|
|
@@ -170,7 +170,7 @@ This prints the following to the console:
|
|
|
170
170
|
[fetch] > HTTP/1.1 POST https://example.com/
|
|
171
171
|
[fetch] > content-type: application/json
|
|
172
172
|
[fetch] > Connection: keep-alive
|
|
173
|
-
[fetch] > User-Agent: Bun/1.2.3-canary.
|
|
173
|
+
[fetch] > User-Agent: Bun/1.2.3-canary.20250217T140554
|
|
174
174
|
[fetch] > Accept: */*
|
|
175
175
|
[fetch] > Host: example.com
|
|
176
176
|
[fetch] > Accept-Encoding: gzip, deflate, br
|
package/docs/test/dom.md
CHANGED
package/package.json
CHANGED