# Bytecode Caching Source: https://bun.com/docs/bundler/bytecode Speed up JavaScript execution with bytecode caching in Bun's bundler Bytecode caching is a build-time optimization that dramatically improves application startup time by pre-compiling your JavaScript to bytecode. For example, when compiling TypeScript's `tsc` with bytecode enabled, startup time improves by **2x**. ## Usage ### Basic usage Enable bytecode caching with the `--bytecode` flag: ```bash theme={"theme":{"light":"github-light","dark":"dracula"}} bun build ./index.ts --target=bun --bytecode --outdir=./dist ``` This generates two files: * `dist/index.js` - Your bundled JavaScript * `dist/index.jsc` - The bytecode cache file At runtime, Bun automatically detects and uses the `.jsc` file: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun ./dist/index.js # Automatically uses index.jsc ``` ### With standalone executables When creating executables with `--compile`, bytecode is embedded into the binary: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun build ./cli.ts --compile --bytecode --outfile=mycli ``` The resulting executable contains both the code and bytecode, giving you maximum performance in a single file. ### Combining with other optimizations Bytecode works great with minification and source maps: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun build --compile --bytecode --minify --sourcemap ./cli.ts --outfile=mycli ``` * `--minify` reduces code size before generating bytecode (less code -> less bytecode) * `--sourcemap` preserves error reporting (errors still point to original source) * `--bytecode` eliminates parsing overhead ## Performance impact The performance improvement scales with your codebase size: | Application size | Typical startup improvement | | ------------------------- | --------------------------- | | Small CLI (\< 100 KB) | 1.5-2x faster | | Medium-large app (> 5 MB) | 2.5x-4x faster | Larger applications benefit more because they have more code to parse. ## When to use bytecode ### Great for: #### CLI tools * Invoked frequently (linters, formatters, git hooks) * Startup time is the entire user experience * Users notice the difference between 90ms and 45ms startup * Example: TypeScript compiler, Prettier, ESLint #### Build tools and task runners * Run hundreds or thousands of times during development * Milliseconds saved per run compound quickly * Developer experience improvement * Example: Build scripts, test runners, code generators #### Standalone executables * Distributed to users who care about snappy performance * Single-file distribution is convenient * File size less important than startup time * Example: CLIs distributed via npm or as binaries ### Skip it for: * ❌ **Small scripts** * ❌ **Code that runs once** * ❌ **Development builds** * ❌ **Size-constrained environments** * ❌ **Code with top-level await** (not supported) ## Limitations ### CommonJS only Bytecode caching currently works with CommonJS output format. Bun's bundler automatically converts most ESM code to CommonJS, but **top-level await** is the exception: ```js theme={"theme":{"light":"github-light","dark":"dracula"}} // This prevents bytecode caching const data = await fetch("https://api.example.com"); export default data; ``` **Why**: Top-level await requires async module evaluation, which can't be represented in CommonJS. The module graph becomes asynchronous, and the CommonJS wrapper function model breaks down. **Workaround**: Move async initialization into a function: ```js theme={"theme":{"light":"github-light","dark":"dracula"}} async function init() { const data = await fetch("https://api.example.com"); return data; } export default init; ``` Now the module exports a function that the consumer can await when needed. ### Version compatibility Bytecode is **not portable across Bun versions**. The bytecode format is tied to JavaScriptCore's internal representation, which changes between versions. When you update Bun, you must regenerate bytecode: ```bash theme={"theme":{"light":"github-light","dark":"dracula"}} # After updating Bun bun build --bytecode ./index.ts --outdir=./dist ``` If bytecode doesn't match the current Bun version, it's automatically ignored and your code falls back to parsing the JavaScript source. Your app still runs - you just lose the performance optimization. **Best practice**: Generate bytecode as part of your CI/CD build process. Don't commit `.jsc` files to git. Regenerate them whenever you update Bun. ### Source code still required * The `.js` file (your bundled source code) * The `.jsc` file (the bytecode cache) At runtime: 1. Bun loads the `.js` file, sees a `@bytecode` pragma, and checks the `.jsc` file 2. Bun loads the `.jsc` file 3. Bun validates the bytecode hash matches the source 4. If valid, Bun uses the bytecode 5. If invalid, Bun falls back to parsing the source ### Bytecode is not obfuscation Bytecode **does not obscure your source code**. It's an optimization, not a security measure. ## Production deployment ### Docker Include bytecode generation in your Dockerfile: ```dockerfile theme={"theme":{"light":"github-light","dark":"dracula"}} FROM oven/bun:1 AS builder WORKDIR /app COPY package.json bun.lock ./ RUN bun install --frozen-lockfile COPY . . RUN bun build --bytecode --minify --sourcemap \ --target=bun \ --outdir=./dist \ --compile \ ./src/server.ts --outfile=./dist/server FROM oven/bun:1 AS runner WORKDIR /app COPY --from=builder /dist/server /app/server CMD ["./server"] ``` The bytecode is architecture-independent. ### CI/CD Generate bytecode during your build pipeline: ```yaml theme={"theme":{"light":"github-light","dark":"dracula"}} # GitHub Actions - name: Build with bytecode run: | bun install bun build --bytecode --minify \ --outdir=./dist \ --target=bun \ ./src/index.ts ``` ## Debugging ### Verify bytecode is being used Check that the `.jsc` file exists: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} ls -lh dist/ ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} -rw-r--r-- 1 user staff 245K index.js -rw-r--r-- 1 user staff 1.1M index.jsc ``` The `.jsc` file should be 2-8x larger than the `.js` file. To log if bytecode is being used, set `BUN_JSC_verboseDiskCache=1` in your environment. On success, it will log something like: ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} [Disk cache] cache hit for sourceCode ``` If you see a cache miss, it will log something like: ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} [Disk cache] cache miss for sourceCode ``` It's normal for it it to log a cache miss multiple times since Bun doesn't currently bytecode cache JavaScript code used in builtin modules. ### Common issues **Bytecode silently ignored**: Usually caused by a Bun version update. The cache version doesn't match, so bytecode is rejected. Regenerate to fix. **File size too large**: This is expected. Consider: * Using `--minify` to reduce code size before bytecode generation * Compressing `.jsc` files for network transfer (gzip/brotli) * Evaluating if the startup performance gain is worth the size increase **Top-level await**: Not supported. Refactor to use async initialization functions. ## What is bytecode? When you run JavaScript, the JavaScript engine doesn't execute your source code directly. Instead, it goes through several steps: 1. **Parsing**: The engine reads your JavaScript source code and converts it into an Abstract Syntax Tree (AST) 2. **Bytecode compilation**: The AST is compiled into bytecode - a lower-level representation that's faster to execute 3. **Execution**: The bytecode is executed by the engine's interpreter or JIT compiler Bytecode is an intermediate representation - it's lower-level than JavaScript source code, but higher-level than machine code. Think of it as assembly language for a virtual machine. Each bytecode instruction represents a single operation like "load this variable," "add two numbers," or "call this function." This happens **every single time** you run your code. If you have a CLI tool that runs 100 times a day, your code gets parsed 100 times. If you have a serverless function with frequent cold starts, parsing happens on every cold start. With bytecode caching, Bun moves steps 1 and 2 to the build step. At runtime, the engine loads the pre-compiled bytecode and jumps straight to execution. ### Why lazy parsing makes this even better Modern JavaScript engines use a clever optimization called **lazy parsing**. They don't parse all your code upfront - instead, functions are only parsed when they're first called: ```js theme={"theme":{"light":"github-light","dark":"dracula"}} // Without bytecode caching: function rarely_used() { // This 500-line function is only parsed // when it's actually called } function main() { console.log("Starting app"); // rarely_used() is never called, so it's never parsed } ``` This means parsing overhead isn't just a startup cost - it happens throughout your application's lifetime as different code paths execute. With bytecode caching, **all functions are pre-compiled**, even the ones that are lazily parsed. The parsing work happens once at build time instead of being distributed throughout your application's execution. ## The bytecode format ### Inside a .jsc file A `.jsc` file contains a serialized bytecode structure. Understanding what's inside helps explain both the performance benefits and the file size tradeoff. **Header section** (validated on every load): * **Cache version**: A hash tied to the JavaScriptCore framework version. This ensures bytecode generated with one version of Bun only runs with that exact version. * **Code block type tag**: Identifies whether this is a Program, Module, Eval, or Function code block. **SourceCodeKey** (validates bytecode matches source): * **Source code hash**: A hash of the original JavaScript source code. Bun verifies this matches before using the bytecode. * **Source code length**: The exact length of the source, for additional validation. * **Compilation flags**: Critical compilation context like strict mode, whether it's a script vs module, eval context type, etc. The same source code compiled with different flags produces different bytecode. **Bytecode instructions**: * **Instruction stream**: The actual bytecode opcodes - the compiled representation of your JavaScript. This is a variable-length sequence of bytecode instructions. * **Metadata table**: Each opcode has associated metadata - things like profiling counters, type hints, and execution counts (even if not yet populated). * **Jump targets**: Pre-computed addresses for control flow (if/else, loops, switch statements). * **Switch tables**: Optimized lookup tables for switch statements. **Constants and identifiers**: * **Constant pool**: All literal values in your code - numbers, strings, booleans, null, undefined. These are stored as actual JavaScript values (JSValues) so they don't need to be parsed from source at runtime. * **Identifier table**: All variable and function names used in the code. Stored as deduplicated strings. * **Source code representation markers**: Flags indicating how constants should be represented (as integers, doubles, big ints, etc.). **Function metadata** (for each function in your code): * **Register allocation**: How many registers (local variables) the function needs - `thisRegister`, `scopeRegister`, `numVars`, `numCalleeLocals`, `numParameters`. * **Code features**: A bitmask of function characteristics: is it a constructor? an arrow function? does it use `super`? does it have tail calls? These affect how the function is executed. * **Lexically scoped features**: Strict mode and other lexical context. * **Parse mode**: The mode in which the function was parsed (normal, async, generator, async generator). **Nested structures**: * **Function declarations and expressions**: Each nested function gets its own bytecode block, recursively. A file with 100 functions has 100 separate bytecode blocks, all nested in the structure. * **Exception handlers**: Try/catch/finally blocks with their boundaries and handler addresses pre-computed. * **Expression info**: Maps bytecode positions back to source code locations for error reporting and debugging. ### What bytecode does NOT contain Importantly, **bytecode does not embed your source code**. Instead: * The JavaScript source is stored separately (in the `.js` file) * The bytecode only stores a hash and length of the source * At load time, Bun validates the bytecode matches the current source code This is why you need to deploy both the `.js` and `.jsc` files. The `.jsc` file is useless without its corresponding `.js` file. ## The tradeoff: file size Bytecode files are significantly larger than source code - typically 2-8x larger. ### Why is bytecode so much larger? **Bytecode instructions are verbose**: A single line of minified JavaScript might compile to dozens of bytecode instructions. For example: ```js theme={"theme":{"light":"github-light","dark":"dracula"}} const sum = arr.reduce((a, b) => a + b, 0); ``` Compiles to bytecode that: * Loads the `arr` variable * Gets the `reduce` property * Creates the arrow function (which itself has bytecode) * Loads the initial value `0` * Sets up the call with the right number of arguments * Actually performs the call * Stores the result in `sum` Each of these steps is a separate bytecode instruction with its own metadata. **Constant pools store everything**: Every string literal, number, property name - everything gets stored in the constant pool. Even if your source code has `"hello"` a hundred times, the constant pool stores it once, but the identifier table and constant references add overhead. **Per-function metadata**: Each function - even small one-line functions - gets its own complete metadata: * Register allocation info * Code features bitmask * Parse mode * Exception handlers * Expression info for debugging A file with 1,000 small functions has 1,000 sets of metadata. **Profiling data structures**: Even though profiling data isn't populated yet, the *structures* to hold profiling data are allocated. This includes: * Value profile slots (tracking what types flow through each operation) * Array profile slots (tracking array access patterns) * Binary arithmetic profile slots (tracking number types in math operations) * Unary arithmetic profile slots These take up space even when empty. **Pre-computed control flow**: Jump targets, switch tables, and exception handler boundaries are all pre-computed and stored. This makes execution faster but increases file size. ### Mitigation strategies **Compression**: Bytecode compresses extremely well with gzip/brotli (60-70% compression). The repetitive structure and metadata compress efficiently. **Minification first**: Using `--minify` before bytecode generation helps: * Shorter identifiers → smaller identifier table * Dead code elimination → less bytecode generated * Constant folding → fewer constants in the pool **The tradeoff**: You're trading 2-4x larger files for 2-4x faster startup. For CLIs, this is usually worth it. For long-running servers where a few megabytes of disk space don't matter, it's even less of an issue. ## Versioning and portability ### Cross-architecture portability: ✅ Bytecode is **architecture-independent**. You can: * Build on macOS ARM64, deploy to Linux x64 * Build on Linux x64, deploy to AWS Lambda ARM64 * Build on Windows x64, deploy to macOS ARM64 The bytecode contains abstract instructions that work on any architecture. Architecture-specific optimizations happen during JIT compilation at runtime, not in the cached bytecode. ### Cross-version portability: ❌ Bytecode is **not stable across Bun versions**. Here's why: **Bytecode format changes**: JavaScriptCore's bytecode format evolves. New opcodes get added, old ones get removed or changed, metadata structures change. Each version of JavaScriptCore has a different bytecode format. **Version validation**: The cache version in the `.jsc` file header is a hash of the JavaScriptCore framework. When Bun loads bytecode: 1. It extracts the cache version from the `.jsc` file 2. It computes the current JavaScriptCore version 3. If they don't match, the bytecode is **silently rejected** 4. Bun falls back to parsing the `.js` source code Your application still runs - you just lose the performance optimization. **Graceful degradation**: This design means bytecode caching "fails open" - if anything goes wrong (version mismatch, corrupted file, missing file), your code still runs normally. You might see slower startup, but you won't see errors. ## Unlinked vs. linked bytecode JavaScriptCore makes a crucial distinction between "unlinked" and "linked" bytecode. This separation is what makes bytecode caching possible: ### Unlinked bytecode (what's cached) The bytecode saved in `.jsc` files is **unlinked bytecode**. It contains: * The compiled bytecode instructions * Structural information about the code * Constants and identifiers * Control flow information But it **doesn't** contain: * Pointers to actual runtime objects * JIT-compiled machine code * Profiling data from previous runs * Call link information (which functions call which) Unlinked bytecode is **immutable and shareable**. Multiple executions of the same code can all reference the same unlinked bytecode. ### Linked bytecode (runtime execution) When Bun runs bytecode, it "links" it - creating a runtime wrapper that adds: * **Call link information**: As your code runs, the engine learns which functions call which and optimizes those call sites. * **Profiling data**: The engine tracks how many times each instruction executes, what types of values flow through the code, array access patterns, etc. * **JIT compilation state**: References to baseline JIT or optimizing JIT (DFG/FTL) compiled versions of hot code. * **Runtime objects**: Pointers to actual JavaScript objects, prototypes, scopes, etc. This linked representation is created fresh every time you run your code. This allows: 1. **Caching the expensive work** (parsing and compilation to unlinked bytecode) 2. **Still collecting runtime profiling data** to guide optimizations 3. **Still applying JIT optimizations** based on actual execution patterns Bytecode caching moves expensive work (parsing and compiling to bytecode) from runtime to build time. For applications that start frequently, this can halve your startup time at the cost of larger files on disk. For production CLIs and serverless deployments, the combination of `--bytecode --minify --sourcemap` gives you the best performance while maintaining debuggability. # CSS Source: https://bun.com/docs/bundler/css Bun's bundler has built-in support for CSS with modern features Bun's bundler has built-in support for CSS with the following features: * Transpiling modern/future features to work on all browsers (including vendor prefixing) * Minification * CSS Modules * Tailwind (via a native bundler plugin) ## Transpiling Bun's CSS bundler lets you use modern/future CSS features without having to worry about browser compatibility — all thanks to its transpiling and vendor prefixing features which are enabled by default. Bun's CSS parser and bundler is a direct Rust → Zig port of LightningCSS, with a bundling approach inspired by esbuild. The transpiler converts modern CSS syntax into backwards-compatible equivalents that work across browsers. A huge thanks goes to the amazing work from the authors of LightningCSS and esbuild. ## Browser Compatibility By default, Bun's CSS bundler targets the following browsers: * ES2020 * Edge 88+ * Firefox 78+ * Chrome 87+ * Safari 14+ ## Syntax Lowering ### Nesting The CSS Nesting specification allows you to write more concise and intuitive stylesheets by nesting selectors inside one another. Instead of repeating parent selectors across your CSS file, you can write child styles directly within their parent blocks. ```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} /* With nesting */ .card { background: white; border-radius: 4px; .title { font-size: 1.2rem; font-weight: bold; } .content { padding: 1rem; } } ``` Bun's CSS bundler automatically converts this nested syntax into traditional flat CSS that works in all browsers: ```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} /* Compiled output */ .card { background: white; border-radius: 4px; } .card .title { font-size: 1.2rem; font-weight: bold; } .card .content { padding: 1rem; } ``` You can also nest media queries and other at-rules inside selectors, eliminating the need to repeat selector patterns: ```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} .responsive-element { display: block; @media (min-width: 768px) { display: flex; } } ``` This compiles to: ```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} .responsive-element { display: block; } @media (min-width: 768px) { .responsive-element { display: flex; } } ``` ### Color mix The `color-mix()` function gives you an easy way to blend two colors together according to a specified ratio in a chosen color space. This powerful feature lets you create color variations without manually calculating the resulting values. ```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} .button { /* Mix blue and red in the RGB color space with a 30/70 proportion */ background-color: color-mix(in srgb, blue 30%, red); /* Create a lighter variant for hover state */ &:hover { background-color: color-mix(in srgb, blue 30%, red, white 20%); } } ``` Bun's CSS bundler evaluates these color mixes at build time when all color values are known (not CSS variables), generating static color values that work in all browsers: ```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} .button { /* Computed to the exact resulting color */ background-color: #b31a1a; } .button:hover { background-color: #c54747; } ``` This feature is particularly useful for creating color systems with programmatically derived shades, tints, and accents without needing preprocessors or custom tooling. ### Relative colors CSS now allows you to modify individual components of a color using relative color syntax. This powerful feature lets you create color variations by adjusting specific attributes like lightness, saturation, or individual channels without having to recalculate the entire color. ```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} .theme-color { /* Start with a base color and increase lightness by 15% */ --accent: lch(from purple calc(l + 15%) c h); /* Take our brand blue and make a desaturated version */ --subtle-blue: oklch(from var(--brand-blue) l calc(c * 0.8) h); } ``` Bun's CSS bundler computes these relative color modifications at build time (when not using CSS variables) and generates static color values for browser compatibility: ```css theme={"theme":{"light":"github-light","dark":"dracula"}} .theme-color { --accent: lch(69.32% 58.34 328.37); --subtle-blue: oklch(60.92% 0.112 240.01); } ``` This approach is extremely useful for theme generation, creating accessible color variants, or building color scales based on mathematical relationships instead of hard-coding each value. ### LAB colors Modern CSS supports perceptually uniform color spaces like LAB, LCH, OKLAB, and OKLCH that offer significant advantages over traditional RGB. These color spaces can represent colors outside the standard RGB gamut, resulting in more vibrant and visually consistent designs. ```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} .vibrant-element { /* A vibrant red that exceeds sRGB gamut boundaries */ color: lab(55% 78 35); /* A smooth gradient using perceptual color space */ background: linear-gradient(to right, oklch(65% 0.25 10deg), oklch(65% 0.25 250deg)); } ``` Bun's CSS bundler automatically converts these advanced color formats to backwards-compatible alternatives for browsers that don't yet support them: ```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} .vibrant-element { /* Fallback to closest RGB approximation */ color: #ff0f52; /* P3 fallback for browsers with wider gamut support */ color: color(display-p3 1 0.12 0.37); /* Original value preserved for browsers that support it */ color: lab(55% 78 35); background: linear-gradient(to right, #cd4e15, #3887ab); background: linear-gradient(to right, oklch(65% 0.25 10deg), oklch(65% 0.25 250deg)); } ``` This layered approach ensures optimal color rendering across all browsers while allowing you to use the latest color technologies in your designs. ### Color function The `color()` function provides a standardized way to specify colors in various predefined color spaces, expanding your design options beyond the traditional RGB space. This allows you to access wider color gamuts and create more vibrant designs. ```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} .vivid-element { /* Using the Display P3 color space for wider gamut colors */ color: color(display-p3 1 0.1 0.3); /* Using A98 RGB color space */ background-color: color(a98-rgb 0.44 0.5 0.37); } ``` For browsers that don't support these advanced color functions yet, Bun's CSS bundler provides appropriate RGB fallbacks: ```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} .vivid-element { /* RGB fallback first for maximum compatibility */ color: #fa1a4c; /* Keep original for browsers that support it */ color: color(display-p3 1 0.1 0.3); background-color: #6a805d; background-color: color(a98-rgb 0.44 0.5 0.37); } ``` This functionality lets you use modern color spaces immediately while ensuring your designs remain functional across all browsers, with optimal colors displayed in supporting browsers and reasonable approximations elsewhere. ### HWB colors The HWB (Hue, Whiteness, Blackness) color model provides an intuitive way to express colors based on how much white or black is mixed with a pure hue. Many designers find this approach more natural for creating color variations compared to manipulating RGB or HSL values. ```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} .easy-theming { /* Pure cyan with no white or black added */ --primary: hwb(180 0% 0%); /* Same hue, but with 20% white added (tint) */ --primary-light: hwb(180 20% 0%); /* Same hue, but with 30% black added (shade) */ --primary-dark: hwb(180 0% 30%); /* Muted version with both white and black added */ --primary-muted: hwb(180 30% 20%); } ``` Bun's CSS bundler automatically converts HWB colors to RGB for compatibility with all browsers: ```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} .easy-theming { --primary: #00ffff; --primary-light: #33ffff; --primary-dark: #00b3b3; --primary-muted: #339999; } ``` The HWB model makes it particularly easy to create systematic color variations for design systems, providing a more intuitive approach to creating consistent tints and shades than working directly with RGB or HSL values. ### Color notation Modern CSS has introduced more intuitive and concise ways to express colors. Space-separated color syntax eliminates the need for commas in RGB and HSL values, while hex colors with alpha channels provide a compact way to specify transparency. ```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} .modern-styling { /* Space-separated RGB notation (no commas) */ color: rgb(50 100 200); /* Space-separated RGB with alpha */ border-color: rgba(100 50 200 / 75%); /* Hex with alpha channel (8 digits) */ background-color: #00aaff80; /* HSL with simplified notation */ box-shadow: 0 5px 10px hsl(200 50% 30% / 40%); } ``` Bun's CSS bundler automatically converts these modern color formats to ensure compatibility with older browsers: ```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} .modern-styling { /* Converted to comma format for older browsers */ color: rgb(50, 100, 200); /* Alpha channels handled appropriately */ border-color: rgba(100, 50, 200, 0.75); /* Hex+alpha converted to rgba when needed */ background-color: rgba(0, 170, 255, 0.5); box-shadow: 0 5px 10px rgba(38, 115, 153, 0.4); } ``` This conversion process lets you write cleaner, more modern CSS while ensuring your styles work correctly across all browsers. ### light-dark() color function The `light-dark()` function provides an elegant solution for implementing color schemes that respect the user's system preference without requiring complex media queries. This function accepts two color values and automatically selects the appropriate one based on the current color scheme context. ```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} :root { /* Define color scheme support */ color-scheme: light dark; } .themed-component { /* Automatically picks the right color based on system preference */ background-color: light-dark(#ffffff, #121212); color: light-dark(#333333, #eeeeee); border-color: light-dark(#dddddd, #555555); } /* Override system preference when needed */ .light-theme { color-scheme: light; } .dark-theme { color-scheme: dark; } ``` For browsers that don't support this feature yet, Bun's CSS bundler converts it to use CSS variables with proper fallbacks: ```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} :root { --lightningcss-light: initial; --lightningcss-dark: ; color-scheme: light dark; } @media (prefers-color-scheme: dark) { :root { --lightningcss-light: ; --lightningcss-dark: initial; } } .light-theme { --lightningcss-light: initial; --lightningcss-dark: ; color-scheme: light; } .dark-theme { --lightningcss-light: ; --lightningcss-dark: initial; color-scheme: dark; } .themed-component { background-color: var(--lightningcss-light, #ffffff) var(--lightningcss-dark, #121212); color: var(--lightningcss-light, #333333) var(--lightningcss-dark, #eeeeee); border-color: var(--lightningcss-light, #dddddd) var(--lightningcss-dark, #555555); } ``` This approach gives you a clean way to handle light and dark themes without duplicating styles or writing complex media queries, while maintaining compatibility with browsers that don't yet support the feature natively. ### Logical properties CSS logical properties let you define layout, spacing, and sizing relative to the document's writing mode and text direction rather than physical screen directions. This is crucial for creating truly international layouts that automatically adapt to different writing systems. ```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} .multilingual-component { /* Margin that adapts to writing direction */ margin-inline-start: 1rem; /* Padding that makes sense regardless of text direction */ padding-block: 1rem 2rem; /* Border radius for the starting corner at the top */ border-start-start-radius: 4px; /* Size that respects the writing mode */ inline-size: 80%; block-size: auto; } ``` For browsers that don't fully support logical properties, Bun's CSS bundler compiles them to physical properties with appropriate directional adjustments: ```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} /* For left-to-right languages */ .multilingual-component:dir(ltr) { margin-left: 1rem; padding-top: 1rem; padding-bottom: 2rem; border-top-left-radius: 4px; width: 80%; height: auto; } /* For right-to-left languages */ .multilingual-component:dir(rtl) { margin-right: 1rem; padding-top: 1rem; padding-bottom: 2rem; border-top-right-radius: 4px; width: 80%; height: auto; } ``` If the `:dir()` selector isn't supported, additional fallbacks are automatically generated to ensure your layouts work properly across all browsers and writing systems. This makes creating internationalized designs much simpler while maintaining compatibility with older browsers. ### :dir() selector The `:dir()` pseudo-class selector allows you to style elements based on their text direction (RTL or LTR), providing a powerful way to create direction-aware designs without JavaScript. This selector matches elements based on their directionality as determined by the document or explicit direction attributes. ```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} /* Apply different styles based on text direction */ .nav-arrow:dir(ltr) { transform: rotate(0deg); } .nav-arrow:dir(rtl) { transform: rotate(180deg); } /* Position elements based on text flow */ .sidebar:dir(ltr) { border-right: 1px solid #ddd; } .sidebar:dir(rtl) { border-left: 1px solid #ddd; } ``` For browsers that don't support the `:dir()` selector yet, Bun's CSS bundler converts it to the more widely supported `:lang()` selector with appropriate language mappings: ```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} /* Converted to use language-based selectors as fallback */ .nav-arrow:lang(en, fr, de, es, it, pt, nl) { transform: rotate(0deg); } .nav-arrow:lang(ar, he, fa, ur) { transform: rotate(180deg); } .sidebar:lang(en, fr, de, es, it, pt, nl) { border-right: 1px solid #ddd; } .sidebar:lang(ar, he, fa, ur) { border-left: 1px solid #ddd; } ``` This conversion lets you write direction-aware CSS that works reliably across browsers, even those that don't yet support the `:dir()` selector natively. If multiple arguments to `:lang()` aren't supported, further fallbacks are automatically provided. ### :lang() selector The `:lang()` pseudo-class selector allows you to target elements based on the language they're in, making it easy to apply language-specific styling. Modern CSS allows the `:lang()` selector to accept multiple language codes, letting you group language-specific rules more efficiently. ```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} /* Typography adjustments for CJK languages */ :lang(zh, ja, ko) { line-height: 1.8; font-size: 1.05em; } /* Different quote styles by language group */ blockquote:lang(fr, it, es, pt) { font-style: italic; } blockquote:lang(de, nl, da, sv) { font-weight: 500; } ``` For browsers that don't support multiple arguments in the `:lang()` selector, Bun's CSS bundler converts this syntax to use the `:is()` selector to maintain the same behavior: ```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} /* Multiple languages grouped with :is() for better browser support */ :is(:lang(zh), :lang(ja), :lang(ko)) { line-height: 1.8; font-size: 1.05em; } blockquote:is(:lang(fr), :lang(it), :lang(es), :lang(pt)) { font-style: italic; } blockquote:is(:lang(de), :lang(nl), :lang(da), :lang(sv)) { font-weight: 500; } ``` If needed, Bun can provide additional fallbacks for `:is()` as well, ensuring your language-specific styles work across all browsers. This approach simplifies creating internationalized designs with distinct typographic and styling rules for different language groups. ### :is() selector The `:is()` pseudo-class function (formerly `:matches()`) allows you to create more concise and readable selectors by grouping multiple selectors together. It accepts a selector list as its argument and matches if any of the selectors in that list match, significantly reducing repetition in your CSS. ```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} /* Instead of writing these separately */ /* .article h1, .article h2, .article h3 { margin-top: 1.5em; } */ /* You can write this */ .article :is(h1, h2, h3) { margin-top: 1.5em; } /* Complex example with multiple groups */ :is(header, main, footer) :is(h1, h2, .title) { font-family: "Heading Font", sans-serif; } ``` For browsers that don't support `:is()`, Bun's CSS bundler provides fallbacks using vendor-prefixed alternatives: ```css theme={"theme":{"light":"github-light","dark":"dracula"}} /* Fallback using -webkit-any */ .article :-webkit-any(h1, h2, h3) { margin-top: 1.5em; } /* Fallback using -moz-any */ .article :-moz-any(h1, h2, h3) { margin-top: 1.5em; } /* Original preserved for modern browsers */ .article :is(h1, h2, h3) { margin-top: 1.5em; } /* Complex example with fallbacks */ :-webkit-any(header, main, footer) :-webkit-any(h1, h2, .title) { font-family: "Heading Font", sans-serif; } :-moz-any(header, main, footer) :-moz-any(h1, h2, .title) { font-family: "Heading Font", sans-serif; } :is(header, main, footer) :is(h1, h2, .title) { font-family: "Heading Font", sans-serif; } ``` The vendor-prefixed versions have some limitations compared to the standardized `:is()` selector, particularly with complex selectors. Bun handles these limitations intelligently, only using prefixed versions when they'll work correctly. ### :not() selector The `:not()` pseudo-class allows you to exclude elements that match a specific selector. The modern version of this selector accepts multiple arguments, letting you exclude multiple patterns with a single, concise selector. ```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} /* Select all buttons except primary and secondary variants */ button:not(.primary, .secondary) { background-color: #f5f5f5; border: 1px solid #ddd; } /* Apply styles to all headings except those inside sidebars or footers */ h2:not(.sidebar *, footer *) { margin-top: 2em; } ``` For browsers that don't support multiple arguments in `:not()`, Bun's CSS bundler converts this syntax to a more compatible form while preserving the same behavior: ```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} /* Converted to use :not with :is() for compatibility */ button:not(:is(.primary, .secondary)) { background-color: #f5f5f5; border: 1px solid #ddd; } h2:not(:is(.sidebar *, footer *)) { margin-top: 2em; } ``` And if `:is()` isn't supported, Bun can generate further fallbacks: ```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} /* Even more fallbacks for maximum compatibility */ button:not(:-webkit-any(.primary, .secondary)) { background-color: #f5f5f5; border: 1px solid #ddd; } button:not(:-moz-any(.primary, .secondary)) { background-color: #f5f5f5; border: 1px solid #ddd; } button:not(:is(.primary, .secondary)) { background-color: #f5f5f5; border: 1px solid #ddd; } ``` This conversion ensures your negative selectors work correctly across all browsers while maintaining the correct specificity and behavior of the original selector. ### Math functions CSS now includes a rich set of mathematical functions that let you perform complex calculations directly in your stylesheets. These include standard math functions (`round()`, `mod()`, `rem()`, `abs()`, `sign()`), trigonometric functions (`sin()`, `cos()`, `tan()`, `asin()`, `acos()`, `atan()`, `atan2()`), and exponential functions (`pow()`, `sqrt()`, `exp()`, `log()`, `hypot()`). ```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} .dynamic-sizing { /* Clamp a value between minimum and maximum */ width: clamp(200px, 50%, 800px); /* Round to the nearest multiple */ padding: round(14.8px, 5px); /* Trigonometry for animations or layouts */ transform: rotate(calc(sin(45deg) * 50deg)); /* Complex math with multiple functions */ --scale-factor: pow(1.25, 3); font-size: calc(16px * var(--scale-factor)); } ``` Bun's CSS bundler evaluates these mathematical expressions at build time when all values are known constants (not variables), resulting in optimized output: ```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} .dynamic-sizing { width: clamp(200px, 50%, 800px); padding: 15px; transform: rotate(35.36deg); --scale-factor: 1.953125; font-size: calc(16px * var(--scale-factor)); } ``` This approach lets you write more expressive and maintainable CSS with meaningful mathematical relationships, which then gets compiled to optimized values for maximum browser compatibility and performance. ### Media query ranges Modern CSS supports intuitive range syntax for media queries, allowing you to specify breakpoints using comparison operators like `<`, `>`, `<=`, and `>=` instead of the more verbose `min-` and `max-` prefixes. This syntax is more readable and matches how we normally think about values and ranges. ```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} /* Modern syntax with comparison operators */ @media (width >= 768px) { .container { max-width: 720px; } } /* Inclusive range using <= and >= */ @media (768px <= width <= 1199px) { .sidebar { display: flex; } } /* Exclusive range using < and > */ @media (width > 320px) and (width < 768px) { .mobile-only { display: block; } } ``` Bun's CSS bundler converts these modern range queries to traditional media query syntax for compatibility with all browsers: ```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} /* Converted to traditional min/max syntax */ @media (min-width: 768px) { .container { max-width: 720px; } } @media (min-width: 768px) and (max-width: 1199px) { .sidebar { display: flex; } } @media (min-width: 321px) and (max-width: 767px) { .mobile-only { display: block; } } ``` This lets you write more intuitive and mathematical media queries while ensuring your stylesheets work correctly across all browsers, including those that don't support the modern range syntax. ### Shorthands CSS has introduced several modern shorthand properties that improve code readability and maintainability. Bun's CSS bundler ensures these convenient shorthands work on all browsers by converting them to their longhand equivalents when needed. ```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} /* Alignment shorthands */ .flex-container { /* Shorthand for align-items and justify-items */ place-items: center start; /* Shorthand for align-content and justify-content */ place-content: space-between center; } .grid-item { /* Shorthand for align-self and justify-self */ place-self: end center; } /* Two-value overflow */ .content-box { /* First value for horizontal, second for vertical */ overflow: hidden auto; } /* Enhanced text-decoration */ .fancy-link { /* Combines multiple text decoration properties */ text-decoration: underline dotted blue 2px; } /* Two-value display syntax */ .component { /* Outer display type + inner display type */ display: inline flex; } ``` For browsers that don't support these modern shorthands, Bun converts them to their component longhand properties: ```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} .flex-container { /* Expanded alignment properties */ align-items: center; justify-items: start; align-content: space-between; justify-content: center; } .grid-item { align-self: end; justify-self: center; } .content-box { /* Separate overflow properties */ overflow-x: hidden; overflow-y: auto; } .fancy-link { /* Individual text decoration properties */ text-decoration-line: underline; text-decoration-style: dotted; text-decoration-color: blue; text-decoration-thickness: 2px; } .component { /* Single value display */ display: inline-flex; } ``` This conversion ensures your stylesheets remain clean and maintainable while providing the broadest possible browser compatibility. ### Double position gradients The double position gradient syntax is a modern CSS feature that allows you to create hard color stops in gradients by specifying the same color at two adjacent positions. This creates a sharp transition rather than a smooth fade, which is useful for creating stripes, color bands, and other multi-color designs. ```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} .striped-background { /* Creates a sharp transition from green to red at 30%-40% */ background: linear-gradient( to right, yellow 0%, green 20%, green 30%, red 30%, /* Double position creates hard stop */ red 70%, blue 70%, blue 100% ); } .progress-bar { /* Creates distinct color sections */ background: linear-gradient( to right, #4caf50 0% 25%, /* Green from 0% to 25% */ #ffc107 25% 50%, /* Yellow from 25% to 50% */ #2196f3 50% 75%, /* Blue from 50% to 75% */ #9c27b0 75% 100% /* Purple from 75% to 100% */ ); } ``` For browsers that don't support this syntax, Bun's CSS bundler automatically converts it to the traditional format by duplicating color stops: ```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} .striped-background { background: linear-gradient( to right, yellow 0%, green 20%, green 30%, red 30%, /* Split into two color stops */ red 70%, blue 70%, blue 100% ); } .progress-bar { background: linear-gradient( to right, #4caf50 0%, #4caf50 25%, /* Two stops for green section */ #ffc107 25%, #ffc107 50%, /* Two stops for yellow section */ #2196f3 50%, #2196f3 75%, /* Two stops for blue section */ #9c27b0 75%, #9c27b0 100% /* Two stops for purple section */ ); } ``` This conversion lets you use the cleaner double position syntax in your source code while ensuring gradients display correctly in all browsers. ### system-ui font The `system-ui` generic font family lets you use the device's native UI font, creating interfaces that feel more integrated with the operating system. This provides a more native look and feel without having to specify different font stacks for each platform. ```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} .native-interface { /* Use the system's default UI font */ font-family: system-ui; } .fallback-aware { /* System UI font with explicit fallbacks */ font-family: system-ui, sans-serif; } ``` For browsers that don't support `system-ui`, Bun's CSS bundler automatically expands it to a comprehensive cross-platform font stack: ```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} .native-interface { /* Expanded to support all major platforms */ font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Noto Sans", Ubuntu, Cantarell, "Helvetica Neue"; } .fallback-aware { /* Preserves the original fallback after the expanded stack */ font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Noto Sans", Ubuntu, Cantarell, "Helvetica Neue", sans-serif; } ``` This approach gives you the simplicity of writing just `system-ui` in your source code while ensuring your interface adapts correctly to all operating systems and browsers. The expanded font stack includes appropriate system fonts for macOS/iOS, Windows, Android, Linux, and fallbacks for older browsers. ## CSS Modules Bun's bundler also supports bundling CSS modules in addition to regular CSS with support for the following features: * Automatically detecting CSS module files (`.module.css`) with zero configuration * Composition (`composes` property) * Importing CSS modules into JSX/TSX * Warnings/errors for invalid usages of CSS modules A CSS module is a CSS file (with the `.module.css` extension) where are all class names and animations are scoped to the file. This helps you avoid class name collisions as CSS declarations are globally scoped by default. Under the hood, Bun's bundler transforms locally scoped class names into unique identifiers. ### Getting started Create a CSS file with the `.module.css` extension: ```css title="styles.module.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} .button { color: red; } ``` ```css title="other-styles.module.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} .button { color: blue; } ``` You can then import this file, for example into a TSX file: ```tsx title="app.tsx" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import styles from "./styles.module.css"; import otherStyles from "./other-styles.module.css"; export default function App() { return ( <> ); } ``` The styles object from importing the CSS module file will be an object with all class names as keys and their unique identifiers as values: ```ts title="app.tsx" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import styles from "./styles.module.css"; import otherStyles from "./other-styles.module.css"; console.log(styles); console.log(otherStyles); ``` This will output: ```ts title="app.tsx" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} { button: "button_123"; } { button: "button_456"; } ``` As you can see, the class names are unique to each file, avoiding any collisions! ### Composition CSS modules allow you to compose class selectors together. This lets you reuse style rules across multiple classes. For example: ```css title="styles.module.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} .button { composes: background; color: red; } .background { background-color: blue; } ``` Would be the same as writing: ```css title="styles.module.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} .button { background-color: blue; color: red; } .background { background-color: blue; } ``` There are a couple rules to keep in mind when using `composes`: **Composition Rules:** - A `composes` property must come before any regular CSS properties or declarations - You can only use `composes` on a simple selector with a single class name ```css title="styles.module.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} #button { /* Invalid! `#button` is not a class selector */ composes: background; } .button, .button-secondary { /* Invalid! `.button, .button-secondary` is not a simple selector */ composes: background; } ``` ### Composing from a separate CSS module file You can also compose from a separate CSS module file: ```css title="background.module.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} .background { background-color: blue; } ``` ```css title="styles.module.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} .button { composes: background from "./background.module.css"; color: red; } ``` When composing classes from separate files, be sure that they do not contain the same properties. The CSS module spec says that composing classes from separate files with conflicting properties is undefined behavior, meaning that the output may differ and be unreliable. # esbuild Source: https://bun.com/docs/bundler/esbuild Migration guide from esbuild to Bun's bundler Bun's bundler API is inspired heavily by esbuild. Migrating to Bun's bundler from esbuild should be relatively painless. This guide will briefly explain why you might consider migrating to Bun's bundler and provide a side-by-side API comparison reference for those who are already familiar with esbuild's API. There are a few behavioral differences to note. **Bundling by default.** Unlike esbuild, Bun always bundles by default. This is why the `--bundle` flag isn't necessary in the Bun example. To transpile each file individually, use `Bun.Transpiler`. **It's just a bundler.** Unlike esbuild, Bun's bundler does not include a built-in development server or file watcher. It's just a bundler. The bundler is intended for use in conjunction with `Bun.serve` and other runtime APIs to achieve the same effect. As such, all options relating to HTTP/file watching are not applicable. ## Performance With a performance-minded API coupled with the extensively optimized Zig-based JS/TS parser, Bun's bundler is 1.75x faster than esbuild on esbuild's three.js benchmark. Bundling 10 copies of three.js from scratch, with sourcemaps and minification ## CLI API Bun and esbuild both provide a command-line interface. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} # esbuild esbuild --outdir=out --bundle # bun bun build --outdir=out ``` In Bun's CLI, simple boolean flags like `--minify` do not accept an argument. Other flags like `--outdir ` do accept an argument; these flags can be written as `--outdir out` or `--outdir=out`. Some flags like `--define` can be specified several times: `--define foo=bar --define bar=baz`. | esbuild | bun build | Notes | | ---------------------- | -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `--bundle` | n/a | Bun always bundles, use `--no-bundle` to disable this behavior. | | `--define:K=V` | `--define K=V` | Small syntax difference; no colon.
`esbuild --define:foo=bar`
`bun build --define foo=bar` | | `--external:` | `--external ` | Small syntax difference; no colon.
`esbuild --external:react`
`bun build --external react` | | `--format` | `--format` | Bun supports `"esm"` and `"cjs"` currently, but more module formats are planned. esbuild defaults to `"iife"`. | | `--loader:.ext=loader` | `--loader .ext:loader` | Bun supports a different set of built-in loaders than esbuild; see Bundler > Loaders for a complete reference. The esbuild loaders `dataurl`, `binary`, `base64`, `copy`, and `empty` are not yet implemented.

The syntax for `--loader` is slightly different.
`esbuild app.ts --bundle --loader:.svg=text`
`bun build app.ts --loader .svg:text` | | `--minify` | `--minify` | No differences | | `--outdir` | `--outdir` | No differences | | `--outfile` | `--outfile` | No differences | | `--packages` | `--packages` | No differences | | `--platform` | `--target` | Renamed to `--target` for consistency with tsconfig. Does not support `neutral`. | | `--serve` | n/a | Not applicable | | `--sourcemap` | `--sourcemap` | No differences | | `--splitting` | `--splitting` | No differences | | `--target` | n/a | Not supported. Bun's bundler performs no syntactic down-leveling at this time. | | `--watch` | `--watch` | No differences | | `--allow-overwrite` | n/a | Overwriting is never allowed | | `--analyze` | n/a | Not supported | | `--asset-names` | `--asset-naming` | Renamed for consistency with naming in JS API | | `--banner` | `--banner` | Only applies to js bundles | | `--footer` | `--footer` | Only applies to js bundles | | `--certfile` | n/a | Not applicable | | `--charset=utf8` | n/a | Not supported | | `--chunk-names` | `--chunk-naming` | Renamed for consistency with naming in JS API | | `--color` | n/a | Always enabled | | `--drop` | `--drop` | | | `--entry-names` | `--entry-naming` | Renamed for consistency with naming in JS API | | `--global-name` | n/a | Not applicable, Bun does not support `iife` output at this time | | `--ignore-annotations` | `--ignore-dce-annotations` | | | `--inject` | n/a | Not supported | | `--jsx` | `--jsx-runtime ` | Supports `"automatic"` (uses jsx transform) and `"classic"` (uses `React.createElement`) | | `--jsx-dev` | n/a | Bun reads `compilerOptions.jsx` from `tsconfig.json` to determine a default. If `compilerOptions.jsx` is `"react-jsx"`, or if `NODE_ENV=production`, Bun will use the jsx transform. Otherwise, it uses `jsxDEV`. The bundler does not support `preserve`. | | `--jsx-factory` | `--jsx-factory` | | | `--jsx-fragment` | `--jsx-fragment` | | | `--jsx-import-source` | `--jsx-import-source` | | | `--jsx-side-effects` | n/a | JSX is always assumed to be side-effect-free | | `--keep-names` | n/a | Not supported | | `--keyfile` | n/a | Not applicable | | `--legal-comments` | n/a | Not supported | | `--log-level` | n/a | Not supported. This can be set in `bunfig.toml` as `logLevel`. | | `--log-limit` | n/a | Not supported | | `--log-override:X=Y` | n/a | Not supported | | `--main-fields` | n/a | Not supported | | `--mangle-cache` | n/a | Not supported | | `--mangle-props` | n/a | Not supported | | `--mangle-quoted` | n/a | Not supported | | `--metafile` | n/a | Not supported | | `--minify-whitespace` | `--minify-whitespace` | | | `--minify-identifiers` | `--minify-identifiers` | | | `--minify-syntax` | `--minify-syntax` | | | `--out-extension` | n/a | Not supported | | `--outbase` | `--root` | | | `--preserve-symlinks` | n/a | Not supported | | `--public-path` | `--public-path` | | | `--pure` | n/a | Not supported | | `--reserve-props` | n/a | Not supported | | `--resolve-extensions` | n/a | Not supported | | `--servedir` | n/a | Not applicable | | `--source-root` | n/a | Not supported | | `--sourcefile` | n/a | Not supported. Bun does not support stdin input yet. | | `--sourcemap` | `--sourcemap` | No differences | | `--sources-content` | n/a | Not supported | | `--supported` | n/a | Not supported | | `--tree-shaking` | n/a | Always true | | `--tsconfig` | `--tsconfig-override` | | | `--version` | n/a | Run `bun --version` to see the version of Bun. | ## JavaScript API | esbuild.build() | Bun.build() | Notes | | ------------------- | -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `absWorkingDir` | n/a | Always set to `process.cwd()` | | `alias` | n/a | Not supported | | `allowOverwrite` | n/a | Always false | | `assetNames` | `naming.asset` | Uses same templating syntax as esbuild, but `[ext]` must be included explicitly.

`ts
Bun.build({
entrypoints: ["./index.tsx"],
naming: {
asset: "[name].[ext]",
},
});
` | | `banner` | n/a | Not supported | | `bundle` | n/a | Always true. Use `Bun.Transpiler` to transpile without bundling. | | `charset` | n/a | Not supported | | `chunkNames` | `naming.chunk` | Uses same templating syntax as esbuild, but `[ext]` must be included explicitly.

`ts
Bun.build({
entrypoints: ["./index.tsx"],
naming: {
chunk: "[name].[ext]",
},
});
` | | `color` | n/a | Bun returns logs in the `logs` property of the build result. | | `conditions` | n/a | Not supported. Export conditions priority is determined by `target`. | | `define` | `define` | | | `drop` | n/a | Not supported | | `entryNames` | `naming` or `naming.entry` | Bun supports a `naming` key that can either be a string or an object. Uses same templating syntax as esbuild, but `[ext]` must be included explicitly.

`ts
Bun.build({
entrypoints: ["./index.tsx"],
// when string, this is equivalent to entryNames
naming: "[name].[ext]",

// granular naming options
naming: {
entry: "[name].[ext]",
asset: "[name].[ext]",
chunk: "[name].[ext]",
},
});
` | | `entryPoints` | `entrypoints` | Capitalization difference | | `external` | `external` | No differences | | `footer` | n/a | Not supported | | `format` | `format` | Only supports `"esm"` currently. Support for `"cjs"` and `"iife"` is planned. | | `globalName` | n/a | Not supported | | `ignoreAnnotations` | n/a | Not supported | | `inject` | n/a | Not supported | | `jsx` | `jsx` | Not supported in JS API, configure in `tsconfig.json` | | `jsxDev` | `jsxDev` | Not supported in JS API, configure in `tsconfig.json` | | `jsxFactory` | `jsxFactory` | Not supported in JS API, configure in `tsconfig.json` | | `jsxFragment` | `jsxFragment` | Not supported in JS API, configure in `tsconfig.json` | | `jsxImportSource` | `jsxImportSource` | Not supported in JS API, configure in `tsconfig.json` | | `jsxSideEffects` | `jsxSideEffects` | Not supported in JS API, configure in `tsconfig.json` | | `keepNames` | n/a | Not supported | | `legalComments` | n/a | Not supported | | `loader` | `loader` | Bun supports a different set of built-in loaders than esbuild; see Bundler > Loaders for a complete reference. The esbuild loaders `dataurl`, `binary`, `base64`, `copy`, and `empty` are not yet implemented. | | `logLevel` | n/a | Not supported | | `logLimit` | n/a | Not supported | | `logOverride` | n/a | Not supported | | `mainFields` | n/a | Not supported | | `mangleCache` | n/a | Not supported | | `mangleProps` | n/a | Not supported | | `mangleQuoted` | n/a | Not supported | | `metafile` | n/a | Not supported | | `minify` | `minify` | In Bun, `minify` can be a boolean or an object.

`ts
await Bun.build({
entrypoints: ['./index.tsx'],
// enable all minification
minify: true

// granular options
minify: {
identifiers: true,
syntax: true,
whitespace: true
}
})
` | | `minifyIdentifiers` | `minify.identifiers` | See `minify` | | `minifySyntax` | `minify.syntax` | See `minify` | | `minifyWhitespace` | `minify.whitespace` | See `minify` | | `nodePaths` | n/a | Not supported | | `outExtension` | n/a | Not supported | | `outbase` | `root` | Different name | | `outdir` | `outdir` | No differences | | `outfile` | `outfile` | No differences | | `packages` | n/a | Not supported, use `external` | | `platform` | `target` | Supports `"bun"`, `"node"` and `"browser"` (the default). Does not support `"neutral"`. | | `plugins` | `plugins` | Bun's plugin API is a subset of esbuild's. Some esbuild plugins will work out of the box with Bun. | | `preserveSymlinks` | n/a | Not supported | | `publicPath` | `publicPath` | No differences | | `pure` | n/a | Not supported | | `reserveProps` | n/a | Not supported | | `resolveExtensions` | n/a | Not supported | | `sourceRoot` | n/a | Not supported | | `sourcemap` | `sourcemap` | Supports `"inline"`, `"external"`, and `"none"` | | `sourcesContent` | n/a | Not supported | | `splitting` | `splitting` | No differences | | `stdin` | n/a | Not supported | | `supported` | n/a | Not supported | | `target` | n/a | No support for syntax downleveling | | `treeShaking` | n/a | Always true | | `tsconfig` | n/a | Not supported | | `write` | n/a | Set to true if `outdir`/`outfile` is set, otherwise false | ## Plugin API Bun's plugin API is designed to be esbuild compatible. Bun doesn't support esbuild's entire plugin API surface, but the core functionality is implemented. Many third-party esbuild plugins will work out of the box with Bun. Long term, we aim for feature parity with esbuild's API, so if something doesn't work please file an issue to help us prioritize. Plugins in Bun and esbuild are defined with a builder object. ```ts title="myPlugin.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import type { BunPlugin } from "bun"; const myPlugin: BunPlugin = { name: "my-plugin", setup(builder) { // define plugin }, }; ``` The builder object provides some methods for hooking into parts of the bundling process. Bun implements `onResolve` and `onLoad`; it does not yet implement the esbuild hooks `onStart`, `onEnd`, and `onDispose`, and `resolve` utilities. `initialOptions` is partially implemented, being read-only and only having a subset of esbuild's options; use `config` (same thing but with Bun's `BuildConfig` format) instead. ```ts title="myPlugin.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import type { BunPlugin } from "bun"; const myPlugin: BunPlugin = { name: "my-plugin", setup(builder) { builder.onResolve( { /* onResolve.options */ }, args => { return { /* onResolve.results */ }; }, ); builder.onLoad( { /* onLoad.options */ }, args => { return { /* onLoad.results */ }; }, ); }, }; ``` ### onResolve - 🟢 `filter` - 🟢 `namespace` * 🟢 `path` - 🟢 `importer` - 🔴 `namespace` - 🔴 `resolveDir` - 🔴 `kind` - 🔴 `pluginData` * 🟢 `namespace` - 🟢 `path` - 🔴 `errors` - 🔴 `external` - 🔴 `pluginData` - 🔴 `pluginName` - 🔴 `sideEffects` - 🔴 `suffix` - 🔴 `warnings` - 🔴 `watchDirs` - 🔴 `watchFiles` ### onLoad - 🟢 `filter` - 🟢 `namespace` - 🟢 `path` - 🔴 `namespace` - 🔴 `suffix` - 🔴 `pluginData` * 🟢 `contents` - 🟢 `loader` - 🔴 `errors` - 🔴 `pluginData` - 🔴 `pluginName` - 🔴 `resolveDir` - 🔴 `warnings` - 🔴 `watchDirs` - 🔴 `watchFiles` # Single-file executable Source: https://bun.com/docs/bundler/executables Generate standalone executables from TypeScript or JavaScript files with Bun Bun's bundler implements a `--compile` flag for generating a standalone binary from a TypeScript or JavaScript file. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun build ./cli.ts --compile --outfile mycli ``` ```ts cli.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} console.log("Hello world!"); ``` This bundles `cli.ts` into an executable that can be executed directly: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} ./mycli ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} Hello world! ``` All imported files and packages are bundled into the executable, along with a copy of the Bun runtime. All built-in Bun and Node.js APIs are supported. *** ## Cross-compile to other platforms The `--target` flag lets you compile your standalone executable for a different operating system, architecture, or version of Bun than the machine you're running `bun build` on. To build for Linux x64 (most servers): ```bash icon="terminal" terminal theme={"theme":{"light":"github-light","dark":"dracula"}} bun build --compile --target=bun-linux-x64 ./index.ts --outfile myapp # To support CPUs from before 2013, use the baseline version (nehalem) bun build --compile --target=bun-linux-x64-baseline ./index.ts --outfile myapp # To explicitly only support CPUs from 2013 and later, use the modern version (haswell) # modern is faster, but baseline is more compatible. bun build --compile --target=bun-linux-x64-modern ./index.ts --outfile myapp ``` To build for Linux ARM64 (e.g. Graviton or Raspberry Pi): ```bash icon="terminal" terminal theme={"theme":{"light":"github-light","dark":"dracula"}} # Note: the default architecture is x64 if no architecture is specified. bun build --compile --target=bun-linux-arm64 ./index.ts --outfile myapp ``` To build for Windows x64: ```bash icon="terminal" terminal theme={"theme":{"light":"github-light","dark":"dracula"}} bun build --compile --target=bun-windows-x64 ./path/to/my/app.ts --outfile myapp # To support CPUs from before 2013, use the baseline version (nehalem) bun build --compile --target=bun-windows-x64-baseline ./path/to/my/app.ts --outfile myapp # To explicitly only support CPUs from 2013 and later, use the modern version (haswell) bun build --compile --target=bun-windows-x64-modern ./path/to/my/app.ts --outfile myapp # note: if no .exe extension is provided, Bun will automatically add it for Windows executables ``` To build for macOS arm64: ```bash icon="terminal" terminal theme={"theme":{"light":"github-light","dark":"dracula"}} bun build --compile --target=bun-darwin-arm64 ./path/to/my/app.ts --outfile myapp ``` To build for macOS x64: ```bash icon="terminal" terminal theme={"theme":{"light":"github-light","dark":"dracula"}} bun build --compile --target=bun-darwin-x64 ./path/to/my/app.ts --outfile myapp ``` ### Supported targets The order of the `--target` flag does not matter, as long as they're delimited by a `-`. | --target | Operating System | Architecture | Modern | Baseline | Libc | | --------------------- | ---------------- | ------------ | ------ | -------- | ----- | | bun-linux-x64 | Linux | x64 | ✅ | ✅ | glibc | | bun-linux-arm64 | Linux | arm64 | ✅ | N/A | glibc | | bun-windows-x64 | Windows | x64 | ✅ | ✅ | - | | ~~bun-windows-arm64~~ | Windows | arm64 | ❌ | ❌ | - | | bun-darwin-x64 | macOS | x64 | ✅ | ✅ | - | | bun-darwin-arm64 | macOS | arm64 | ✅ | N/A | - | | bun-linux-x64-musl | Linux | x64 | ✅ | ✅ | musl | | bun-linux-arm64-musl | Linux | arm64 | ✅ | N/A | musl | On x64 platforms, Bun uses SIMD optimizations which require a modern CPU supporting AVX2 instructions. The `-baseline` build of Bun is for older CPUs that don't support these optimizations. Normally, when you install Bun we automatically detect which version to use but this can be harder to do when cross-compiling since you might not know the target CPU. You usually don't need to worry about it on Darwin x64, but it is relevant for Windows x64 and Linux x64. If you or your users see `"Illegal instruction"` errors, you might need to use the baseline version. *** ## Build-time constants Use the `--define` flag to inject build-time constants into your executable, such as version numbers, build timestamps, or configuration values: ```bash icon="terminal" terminal theme={"theme":{"light":"github-light","dark":"dracula"}} bun build --compile --define BUILD_VERSION='"1.2.3"' --define BUILD_TIME='"2024-01-15T10:30:00Z"' src/cli.ts --outfile mycli ``` These constants are embedded directly into your compiled binary at build time, providing zero runtime overhead and enabling dead code elimination optimizations. For comprehensive examples and advanced patterns, see the [Build-time constants guide](https://bun.com/guides/runtime/build-time-constants). *** ## Deploying to production Compiled executables reduce memory usage and improve Bun's start time. Normally, Bun reads and transpiles JavaScript and TypeScript files on `import` and `require`. This is part of what makes so much of Bun "just work", but it's not free. It costs time and memory to read files from disk, resolve file paths, parse, transpile, and print source code. With compiled executables, you can move that cost from runtime to build-time. When deploying to production, we recommend the following: ```bash icon="terminal" terminal theme={"theme":{"light":"github-light","dark":"dracula"}} bun build --compile --minify --sourcemap ./path/to/my/app.ts --outfile myapp ``` ### Bytecode compilation To improve startup time, enable bytecode compilation: ```bash icon="terminal" terminal theme={"theme":{"light":"github-light","dark":"dracula"}} bun build --compile --minify --sourcemap --bytecode ./path/to/my/app.ts --outfile myapp ``` Using bytecode compilation, `tsc` starts 2x faster: ![Bytecode performance comparison](https://github.com/user-attachments/assets/dc8913db-01d2-48f8-a8ef-ac4e984f9763) Bytecode compilation moves parsing overhead for large input files from runtime to bundle time. Your app starts faster, in exchange for making the `bun build` command a little slower. It doesn't obscure source code. **Experimental:** Bytecode compilation is an experimental feature introduced in Bun v1.1.30. Only `cjs` format is supported (which means no top-level-await). Let us know if you run into any issues! ### What do these flags do? The `--minify` argument optimizes the size of the transpiled output code. If you have a large application, this can save megabytes of space. For smaller applications, it might still improve start time a little. The `--sourcemap` argument embeds a sourcemap compressed with zstd, so that errors & stacktraces point to their original locations instead of the transpiled location. Bun will automatically decompress & resolve the sourcemap when an error occurs. The `--bytecode` argument enables bytecode compilation. Every time you run JavaScript code in Bun, JavaScriptCore (the engine) will compile your source code into bytecode. We can move this parsing work from runtime to bundle time, saving you startup time. *** ## Embedding runtime arguments **`--compile-exec-argv="args"`** - Embed runtime arguments that are available via `process.execArgv`: ```bash icon="terminal" terminal theme={"theme":{"light":"github-light","dark":"dracula"}} bun build --compile --compile-exec-argv="--smol --user-agent=MyBot" ./app.ts --outfile myapp ``` ```ts app.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} // In the compiled app console.log(process.execArgv); // ["--smol", "--user-agent=MyBot"] ``` *** ## Act as the Bun CLI New in Bun v1.2.16 You can run a standalone executable as if it were the `bun` CLI itself by setting the `BUN_BE_BUN=1` environment variable. When this variable is set, the executable will ignore its bundled entrypoint and instead expose all the features of Bun's CLI. For example, consider an executable compiled from a simple script: ```bash icon="terminal" terminal theme={"theme":{"light":"github-light","dark":"dracula"}} echo "console.log(\"you shouldn't see this\");" > such-bun.js bun build --compile ./such-bun.js ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} [3ms] bundle 1 modules [89ms] compile such-bun ``` Normally, running `./such-bun` with arguments would execute the script. ```bash icon="terminal" terminal theme={"theme":{"light":"github-light","dark":"dracula"}} # Executable runs its own entrypoint by default ./such-bun install ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} you shouldn't see this ``` However, with the `BUN_BE_BUN=1` environment variable, it acts just like the `bun` binary: ```bash icon="terminal" terminal theme={"theme":{"light":"github-light","dark":"dracula"}} # With the env var, the executable acts like the `bun` CLI bun_BE_BUN=1 ./such-bun install ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} bun install v1.2.16-canary.1 (1d1db811) Checked 63 installs across 64 packages (no changes) [5.00ms] ``` This is useful for building CLI tools on top of Bun that may need to install packages, bundle dependencies, run different or local files and more without needing to download a separate binary or install bun. *** ## Full-stack executables New in Bun v1.2.17 Bun's `--compile` flag can create standalone executables that contain both server and client code, making it ideal for full-stack applications. When you import an HTML file in your server code, Bun automatically bundles all frontend assets (JavaScript, CSS, etc.) and embeds them into the executable. When Bun sees the HTML import on the server, it kicks off a frontend build process to bundle JavaScript, CSS, and other assets. ```ts server.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import { serve } from "bun"; import index from "./index.html"; const server = serve({ routes: { "/": index, "/api/hello": { GET: () => Response.json({ message: "Hello from API" }) }, }, }); console.log(`Server running at http://localhost:${server.port}`); ``` ```html index.html icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} My App

Hello World

``` ```ts app.js icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} console.log("Hello from the client!"); ``` ```css styles.css icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} body { background-color: #f0f0f0; } ```
To build this into a single executable: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun build --compile ./server.ts --outfile myapp ``` This creates a self-contained binary that includes: * Your server code * The Bun runtime * All frontend assets (HTML, CSS, JavaScript) * Any npm packages used by your server The result is a single file that can be deployed anywhere without needing Node.js, Bun, or any dependencies installed. Just run: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} ./myapp ``` Bun automatically handles serving the frontend assets with proper MIME types and cache headers. The HTML import is replaced with a manifest object that `Bun.serve` uses to efficiently serve pre-bundled assets. For more details on building full-stack applications with Bun, see the [full-stack guide](/bundler/fullstack). *** ## Worker To use workers in a standalone executable, add the worker's entrypoint to the CLI arguments: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun build --compile ./index.ts ./my-worker.ts --outfile myapp ``` Then, reference the worker in your code: ```ts index.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} console.log("Hello from Bun!"); // Any of these will work: new Worker("./my-worker.ts"); new Worker(new URL("./my-worker.ts", import.meta.url)); new Worker(new URL("./my-worker.ts", import.meta.url).href); ``` As of Bun v1.1.25, when you add multiple entrypoints to a standalone executable, they will be bundled separately into the executable. In the future, we may automatically detect usages of statically-known paths in `new Worker(path)` and then bundle those into the executable, but for now, you'll need to add it to the shell command manually like the above example. If you use a relative path to a file not included in the standalone executable, it will attempt to load that path from disk relative to the current working directory of the process (and then error if it doesn't exist). *** ## SQLite You can use `bun:sqlite` imports with `bun build --compile`. By default, the database is resolved relative to the current working directory of the process. ```ts index.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import db from "./my.db" with { type: "sqlite" }; console.log(db.query("select * from users LIMIT 1").get()); ``` That means if the executable is located at `/usr/bin/hello`, the user's terminal is located at `/home/me/Desktop`, it will look for `/home/me/Desktop/my.db`. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} cd /home/me/Desktop ./hello ``` *** ## Embed assets & files Standalone executables support embedding files. To embed files into an executable with `bun build --compile`, import the file in your code. ```ts index.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} // this becomes an internal file path import icon from "./icon.png" with { type: "file" }; import { file } from "bun"; export default { fetch(req) { // Embedded files can be streamed from Response objects return new Response(file(icon)); }, }; ``` Embedded files can be read using `Bun.file`'s functions or the Node.js `fs.readFile` function (in `"node:fs"`). For example, to read the contents of the embedded file: ```ts index.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import icon from "./icon.png" with { type: "file" }; import { file } from "bun"; const bytes = await file(icon).arrayBuffer(); // await fs.promises.readFile(icon) // fs.readFileSync(icon) ``` ### Embed SQLite databases If your application wants to embed a SQLite database, set `type: "sqlite"` in the import attribute and the `embed` attribute to `"true"`. ```ts index.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import myEmbeddedDb from "./my.db" with { type: "sqlite", embed: "true" }; console.log(myEmbeddedDb.query("select * from users LIMIT 1").get()); ``` This database is read-write, but all changes are lost when the executable exits (since it's stored in memory). ### Embed N-API Addons As of Bun v1.0.23, you can embed `.node` files into executables. ```ts index.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} const addon = require("./addon.node"); console.log(addon.hello()); ``` Unfortunately, if you're using `@mapbox/node-pre-gyp` or other similar tools, you'll need to make sure the `.node` file is directly required or it won't bundle correctly. ### Embed directories To embed a directory with `bun build --compile`, use a shell glob in your `bun build` command: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun build --compile ./index.ts ./public/**/*.png ``` Then, you can reference the files in your code: ```ts index.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import icon from "./public/assets/icon.png" with { type: "file" }; import { file } from "bun"; export default { fetch(req) { // Embedded files can be streamed from Response objects return new Response(file(icon)); }, }; ``` This is honestly a workaround, and we expect to improve this in the future with a more direct API. ### Listing embedded files To get a list of all embedded files, use `Bun.embeddedFiles`: ```ts index.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import "./icon.png" with { type: "file" }; import { embeddedFiles } from "bun"; console.log(embeddedFiles[0].name); // `icon-${hash}.png` ``` `Bun.embeddedFiles` returns an array of `Blob` objects which you can use to get the size, contents, and other properties of the files. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} embeddedFiles: Blob[] ``` The list of embedded files excludes bundled source code like `.ts` and `.js` files. #### Content hash By default, embedded files have a content hash appended to their name. This is useful for situations where you want to serve the file from a URL or CDN and have fewer cache invalidation issues. But sometimes, this is unexpected and you might want the original name instead: To disable the content hash, pass `--asset-naming` to `bun build --compile` like this: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun build --compile --asset-naming="[name].[ext]" ./index.ts ``` *** ## Minification To trim down the size of the executable a little, pass `--minify` to `bun build --compile`. This uses Bun's minifier to reduce the code size. Overall though, Bun's binary is still way too big and we need to make it smaller. *** ## Windows-specific flags When compiling a standalone executable on Windows, there are two platform-specific options that can be used to customize metadata on the generated `.exe` file: * `--windows-icon=path/to/icon.ico` to customize the executable file icon. * `--windows-hide-console` to disable the background terminal, which can be used for applications that do not need a TTY. These flags currently cannot be used when cross-compiling because they depend on Windows APIs. *** ## Code signing on macOS To codesign a standalone executable on macOS (which fixes Gatekeeper warnings), use the `codesign` command. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} codesign --deep --force -vvvv --sign "XXXXXXXXXX" ./myapp ``` We recommend including an `entitlements.plist` file with JIT permissions. ```xml icon="xml" title="info.plist" theme={"theme":{"light":"github-light","dark":"dracula"}} com.apple.security.cs.allow-jit com.apple.security.cs.allow-unsigned-executable-memory com.apple.security.cs.disable-executable-page-protection com.apple.security.cs.allow-dyld-environment-variables com.apple.security.cs.disable-library-validation ``` To codesign with JIT support, pass the `--entitlements` flag to `codesign`. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} codesign --deep --force -vvvv --sign "XXXXXXXXXX" --entitlements entitlements.plist ./myapp ``` After codesigning, verify the executable: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} codesign -vvv --verify ./myapp ./myapp: valid on disk ./myapp: satisfies its Designated Requirement ``` Codesign support requires Bun v1.2.4 or newer. *** ## Unsupported CLI arguments Currently, the `--compile` flag can only accept a single entrypoint at a time and does not support the following flags: * `--outdir` — use `outfile` instead. * `--splitting` * `--public-path` * `--target=node` or `--target=browser` * `--no-bundle` - we always bundle everything into the executable. # Fullstack dev server Source: https://bun.com/docs/bundler/fullstack Build fullstack applications with Bun's integrated dev server that bundles frontend assets and handles API routes To get started, import HTML files and pass them to the `routes` option in `Bun.serve()`. ```ts title="app.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import { serve } from "bun"; import dashboard from "./dashboard.html"; import homepage from "./index.html"; const server = serve({ routes: { // ** HTML imports ** // Bundle & route index.html to "/". This uses HTMLRewriter to scan // the HTML for ` ``` Becomes something like this: ```html title="index.html" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} Home
``` ## React Integration To use React in your client-side code, import `react-dom/client` and render your app. ```ts title="src/backend.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import dashboard from "../public/dashboard.html"; import { serve } from "bun"; serve({ routes: { "/": dashboard, }, async fetch(req) { // ...api requests return new Response("hello world"); }, }); ``` ```tsx title="src/frontend.tsx" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import { createRoot } from 'react-dom/client'; import App from './app'; const container = document.getElementById('root'); const root = createRoot(container!); root.render(); ``` ```html title="public/dashboard.html" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} Dashboard
``` ```tsx title="src/app.tsx" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import { useState } from 'react'; export default function App() { const [count, setCount] = useState(0); return (

Dashboard

); } ```
## Development Mode When building locally, enable development mode by setting `development: true` in `Bun.serve()`. ```ts title="src/backend.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import homepage from "./index.html"; import dashboard from "./dashboard.html"; Bun.serve({ routes: { "/": homepage, "/dashboard": dashboard, }, development: true, fetch(req) { // ... api requests }, }); ``` ### Development Mode Features When `development` is `true`, Bun will: * Include the SourceMap header in the response so that devtools can show the original source code * Disable minification * Re-bundle assets on each request to a `.html` file * Enable hot module reloading (unless `hmr: false` is set) * Echo console logs from browser to terminal ### Advanced Development Configuration `Bun.serve()` supports echoing console logs from the browser to the terminal. To enable this, pass `console: true` in the development object in `Bun.serve()`. ```ts title="src/backend.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import homepage from "./index.html"; Bun.serve({ // development can also be an object. development: { // Enable Hot Module Reloading hmr: true, // Echo console logs from the browser to the terminal console: true, }, routes: { "/": homepage, }, }); ``` When `console: true` is set, Bun will stream console logs from the browser to the terminal. This reuses the existing WebSocket connection from HMR to send the logs. ### Development vs Production | Feature | Development | Production | | ------------------- | ---------------------- | ---------- | | **Source maps** | ✅ Enabled | ❌ Disabled | | **Minification** | ❌ Disabled | ✅ Enabled | | **Hot reloading** | ✅ Enabled | ❌ Disabled | | **Asset bundling** | 🔄 On each request | 💾 Cached | | **Console logging** | 🖥️ Browser → Terminal | ❌ Disabled | | **Error details** | 📝 Detailed | 🔒 Minimal | ## Production Mode Hot reloading and `development: true` helps you iterate quickly, but in production, your server should be as fast as possible and have as few external dependencies as possible. ### Ahead of Time Bundling (Recommended) As of Bun v1.2.17, you can use `Bun.build` or `bun build` to bundle your full-stack application ahead of time. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun build --target=bun --production --outdir=dist ./src/index.ts ``` When Bun's bundler sees an HTML import from server-side code, it will bundle the referenced JavaScript/TypeScript/TSX/JSX and CSS files into a manifest object that `Bun.serve()` can use to serve the assets. ```ts title="src/backend.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import { serve } from "bun"; import index from "./index.html"; serve({ routes: { "/": index }, }); ``` ### Runtime Bundling When adding a build step is too complicated, you can set `development: false` in `Bun.serve()`. This will: * Enable in-memory caching of bundled assets. Bun will bundle assets lazily on the first request to an `.html` file, and cache the result in memory until the server restarts. * Enable `Cache-Control` headers and `ETag` headers * Minify JavaScript/TypeScript/TSX/JSX files ```ts title="src/backend.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import { serve } from "bun"; import homepage from "./index.html"; serve({ routes: { "/": homepage, }, // Production mode development: false, }); ``` ## API Routes ### HTTP Method Handlers Define API endpoints with HTTP method handlers: ```ts title="src/backend.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import { serve } from "bun"; serve({ routes: { "/api/users": { async GET(req) { // Handle GET requests const users = await getUsers(); return Response.json(users); }, async POST(req) { // Handle POST requests const userData = await req.json(); const user = await createUser(userData); return Response.json(user, { status: 201 }); }, async PUT(req) { // Handle PUT requests const userData = await req.json(); const user = await updateUser(userData); return Response.json(user); }, async DELETE(req) { // Handle DELETE requests await deleteUser(req.params.id); return new Response(null, { status: 204 }); }, }, }, }); ``` ### Dynamic Routes Use URL parameters in your routes: ```ts title="src/backend.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} serve({ routes: { // Single parameter "/api/users/:id": async req => { const { id } = req.params; const user = await getUserById(id); return Response.json(user); }, // Multiple parameters "/api/users/:userId/posts/:postId": async req => { const { userId, postId } = req.params; const post = await getPostByUser(userId, postId); return Response.json(post); }, // Wildcard routes "/api/files/*": async req => { const filePath = req.params["*"]; const file = await getFile(filePath); return new Response(file); }, }, }); ``` ### Request Handling ```ts title="src/backend.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} serve({ routes: { "/api/data": { async POST(req) { // Parse JSON body const body = await req.json(); // Access headers const auth = req.headers.get("Authorization"); // Access URL parameters const { id } = req.params; // Access query parameters const url = new URL(req.url); const page = url.searchParams.get("page") || "1"; // Return response return Response.json({ message: "Data processed", page: parseInt(page), authenticated: !!auth, }); }, }, }, }); ``` ## Plugins Bun's bundler plugins are also supported when bundling static routes. To configure plugins for `Bun.serve`, add a `plugins` array in the `[serve.static]` section of your `bunfig.toml`. ### TailwindCSS Plugin You can use TailwindCSS by installing and adding the `tailwindcss` package and `bun-plugin-tailwind` plugin. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun add tailwindcss bun-plugin-tailwind ``` ```toml title="bunfig.toml" icon="settings" theme={"theme":{"light":"github-light","dark":"dracula"}} [serve.static] plugins = ["bun-plugin-tailwind"] ``` This will allow you to use TailwindCSS utility classes in your HTML and CSS files. All you need to do is import `tailwindcss` somewhere: ```html title="index.html" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} ``` Alternatively, you can import TailwindCSS in your CSS file: ```css title="style.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} @import "tailwindcss"; .custom-class { @apply bg-red-500 text-white; } ``` ```html index.html icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} ``` ### Custom Plugins Any JS file or module which exports a valid bundler plugin object (essentially an object with a `name` and `setup` field) can be placed inside the plugins array: ```toml title="bunfig.toml" icon="settings" theme={"theme":{"light":"github-light","dark":"dracula"}} [serve.static] plugins = ["./my-plugin-implementation.ts"] ``` ```ts title="my-plugin-implementation.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import type { BunPlugin } from "bun"; const myPlugin: BunPlugin = { name: "my-custom-plugin", setup(build) { // Plugin implementation build.onLoad({ filter: /\.custom$/ }, async args => { const text = await Bun.file(args.path).text(); return { contents: `export default ${JSON.stringify(text)};`, loader: "js", }; }); }, }; export default myPlugin; ``` Bun will lazily resolve and load each plugin and use them to bundle your routes. 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. ## How It Works Bun uses `HTMLRewriter` to scan for ` ``` * Processes CSS imports and `` tags * Concatenates CSS files * Rewrites url and asset paths to include content-addressable hashes in URLs ```html title="index.html" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} ``` * Links to assets are rewritten to include content-addressable hashes in URLs * Small assets in CSS files are inlined into `data:` URLs, reducing the total number of HTTP requests sent over the wire * Combines all ` ``` ```tsx title="src/main.tsx" theme={"theme":{"light":"github-light","dark":"dracula"}} import { createRoot } from "react-dom/client"; import { App } from "./App"; const container = document.getElementById("root")!; const root = createRoot(container); root.render(); ``` ```tsx title="src/App.tsx" theme={"theme":{"light":"github-light","dark":"dracula"}} import { useState, useEffect } from "react"; interface User { id: number; name: string; email: string; created_at: string; } export function App() { const [users, setUsers] = useState([]); const [name, setName] = useState(""); const [email, setEmail] = useState(""); const [loading, setLoading] = useState(false); const fetchUsers = async () => { const response = await fetch("/api/users"); const data = await response.json(); setUsers(data); }; const createUser = async (e: React.FormEvent) => { e.preventDefault(); setLoading(true); try { const response = await fetch("/api/users", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name, email }), }); if (response.ok) { setName(""); setEmail(""); await fetchUsers(); } else { const error = await response.json(); alert(error.error); } } catch (error) { alert("Failed to create user"); } finally { setLoading(false); } }; const deleteUser = async (id: number) => { if (!confirm("Are you sure?")) return; try { const response = await fetch(`/api/users/${id}`, { method: "DELETE", }); if (response.ok) { await fetchUsers(); } } catch (error) { alert("Failed to delete user"); } }; useEffect(() => { fetchUsers(); }, []); return (

User Management

setName(e.target.value)} required /> setEmail(e.target.value)} required />

Users ({users.length})

{users.map(user => (
{user.name}
{user.email}
))}
); } ``` ```css title="src/styles.css" theme={"theme":{"light":"github-light","dark":"dracula"}} * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: -apple-system, BlinkMacSystemFont, sans-serif; background: #f5f5f5; color: #333; } .container { max-width: 800px; margin: 0 auto; padding: 2rem; } h1 { color: #2563eb; margin-bottom: 2rem; } .form { background: white; padding: 1.5rem; border-radius: 8px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); margin-bottom: 2rem; display: flex; gap: 1rem; flex-wrap: wrap; } .form input { flex: 1; min-width: 200px; padding: 0.75rem; border: 1px solid #ddd; border-radius: 4px; } .form button { padding: 0.75rem 1.5rem; background: #2563eb; color: white; border: none; border-radius: 4px; cursor: pointer; } .form button:hover { background: #1d4ed8; } .form button:disabled { opacity: 0.5; cursor: not-allowed; } .users { background: white; padding: 1.5rem; border-radius: 8px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); } .user-card { display: flex; justify-content: space-between; align-items: center; padding: 1rem; border-bottom: 1px solid #eee; } .user-card:last-child { border-bottom: none; } .delete-btn { padding: 0.5rem 1rem; background: #dc2626; color: white; border: none; border-radius: 4px; cursor: pointer; } .delete-btn:hover { background: #b91c1c; } ``` ## Best Practices ### Project Structure ``` my-app/ ├── src/ │ ├── components/ │ │ ├── Header.tsx │ │ └── UserList.tsx │ ├── styles/ │ │ ├── globals.css │ │ └── components.css │ ├── utils/ │ │ └── api.ts │ ├── App.tsx │ └── main.tsx ├── public/ │ ├── index.html │ ├── dashboard.html │ └── favicon.ico ├── server/ │ ├── routes/ │ │ ├── users.ts │ │ └── auth.ts │ ├── db/ │ │ └── schema.sql │ └── index.ts ├── bunfig.toml └── package.json ``` ### Environment-Based Configuration ```ts title="server/config.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} export const config = { development: process.env.NODE_ENV !== "production", port: process.env.PORT || 3000, database: { url: process.env.DATABASE_URL || "./dev.db", }, cors: { origin: process.env.CORS_ORIGIN || "*", }, }; ``` ### Error Handling ```ts title="server/middleware.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} export function errorHandler(error: Error, req: Request) { console.error("Server error:", error); if (process.env.NODE_ENV === "production") { return Response.json({ error: "Internal server error" }, { status: 500 }); } return Response.json( { error: error.message, stack: error.stack, }, { status: 500 }, ); } ``` ### API Response Helpers ```ts title="server/utils.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} export function json(data: any, status = 200) { return Response.json(data, { status }); } export function error(message: string, status = 400) { return Response.json({ error: message }, { status }); } export function notFound(message = "Not found") { return error(message, 404); } export function unauthorized(message = "Unauthorized") { return error(message, 401); } ``` ### Type Safety ```ts title="types/api.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} export interface User { id: number; name: string; email: string; created_at: string; } export interface CreateUserRequest { name: string; email: string; } export interface ApiResponse { data?: T; error?: string; } ``` ## Deployment ### Production Build ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} # Build for production bun build --target=bun --production --outdir=dist ./server/index.ts # Run production server NODE_ENV=production bun dist/index.js ``` ### Docker Deployment ```dockerfile title="Dockerfile" icon="docker" theme={"theme":{"light":"github-light","dark":"dracula"}} FROM oven/bun:1 as base WORKDIR /usr/src/app # Install dependencies COPY package.json bun.lockb ./ RUN bun install --frozen-lockfile # Copy source code COPY . . # Build application RUN bun build --target=bun --production --outdir=dist ./server/index.ts # Production stage FROM oven/bun:1-slim WORKDIR /usr/src/app COPY --from=base /usr/src/app/dist ./ COPY --from=base /usr/src/app/public ./public EXPOSE 3000 CMD ["bun", "index.js"] ``` ### Environment Variables ```bash title=".env.production" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} NODE_ENV=production PORT=3000 DATABASE_URL=postgresql://user:pass@localhost:5432/myapp CORS_ORIGIN=https://myapp.com ``` ## Migration from Other Frameworks ### From Express + Webpack ```ts title="server.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} // Before (Express + Webpack) app.use(express.static("dist")); app.get("/api/users", (req, res) => { res.json(users); }); // After (Bun fullstack) serve({ routes: { "/": homepage, // Replaces express.static "/api/users": { GET() { return Response.json(users); }, }, }, }); ``` ### From Next.js API Routes ```ts title="server.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} // Before (Next.js) export default function handler(req, res) { if (req.method === 'GET') { res.json(users); } } // After (Bun) "/api/users": { GET() { return Response.json(users); } } ``` ## Limitations and Future Plans ### Current Limitations * `bun build` CLI integration is not yet available for fullstack apps * Auto-discovery of API routes is not implemented * Server-side rendering (SSR) is not built-in ### Planned Features * Integration with `bun build` CLI * File-based routing for API endpoints * Built-in SSR support * Enhanced plugin ecosystem This is a work in progress. Features and APIs may change as Bun continues to evolve. # Hot reloading Source: https://bun.com/docs/bundler/hot-reloading Hot Module Replacement (HMR) for Bun's development server Hot Module Replacement (HMR) allows you to update modules in a running application without needing a full page reload. This preserves the application state and improves the development experience. HMR is enabled by default when using Bun's full-stack development server. ## `import.meta.hot` API Reference Bun implements a client-side HMR API modeled after [Vite's `import.meta.hot` API](https://vitejs.dev/guide/api-hmr.html). It can be checked for with `if (import.meta.hot)`, tree-shaking it in production. ```ts title="index.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} if (import.meta.hot) { // HMR APIs are available. } ``` However, this check is often not needed as Bun will dead-code-eliminate calls to all of the HMR APIs in production builds. ```ts title="index.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} // This entire function call will be removed in production! import.meta.hot.dispose(() => { console.log("dispose"); }); ``` For this to work, Bun forces these APIs to be called without indirection. That means the following do not work: ```ts title="index.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} // INVALID: Assigning `hot` to a variable const hot = import.meta.hot; hot.accept(); // INVALID: Assigning `import.meta` to a variable const meta = import.meta; meta.hot.accept(); console.log(meta.hot.data); // INVALID: Passing to a function doSomething(import.meta.hot.dispose); // OK: The full phrase "import.meta.hot." must be called directly: import.meta.hot.accept(); // OK: `data` can be passed to functions: doSomething(import.meta.hot.data); ``` The HMR API is still a work in progress. Some features are missing. HMR can be disabled in `Bun.serve` by setting the development option to `{ hmr: false }`. ## API Methods | Method | Status | Notes | | ------------------ | ------ | --------------------------------------------------------------------- | | `hot.accept()` | ✅ | Indicate that a hot update can be replaced gracefully. | | `hot.data` | ✅ | Persist data between module evaluations. | | `hot.dispose()` | ✅ | Add a callback function to run when a module is about to be replaced. | | `hot.invalidate()` | ❌ | | | `hot.on()` | ✅ | Attach an event listener | | `hot.off()` | ✅ | Remove an event listener from `on`. | | `hot.send()` | ❌ | | | `hot.prune()` | 🚧 | NOTE: Callback is currently never called. | | `hot.decline()` | ✅ | No-op to match Vite's `import.meta.hot` | ## import.meta.hot.accept() The `accept()` method indicates that a module can be hot-replaced. When called without arguments, it indicates that this module can be replaced simply by re-evaluating the file. After a hot update, importers of this module will be automatically patched. ```ts title="index.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} // index.ts import { getCount } from "./foo.ts"; console.log("count is ", getCount()); import.meta.hot.accept(); export function getNegativeCount() { return -getCount(); } ``` This creates a hot-reloading boundary for all of the files that `index.ts` imports. That means whenever `foo.ts` or any of its dependencies are saved, the update will bubble up to `index.ts` will re-evaluate. Files that import `index.ts` will then be patched to import the new version of `getNegativeCount()`. If only `index.ts` is updated, only the one file will be re-evaluated, and the counter in `foo.ts` is reused. This may be used in combination with `import.meta.hot.data` to transfer state from the previous module to the new one. When no modules call `import.meta.hot.accept()` (and there isn't React Fast Refresh or a plugin calling it for you), the page will reload when the file updates, and a console warning shows which files were invalidated. This warning is safe to ignore if it makes more sense to rely on full page reloads. ### With callback When provided one callback, `import.meta.hot.accept` will function how it does in Vite. Instead of patching the importers of this module, it will call the callback with the new module. ```ts title="index.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} export const count = 0; import.meta.hot.accept(newModule => { if (newModule) { // newModule is undefined when SyntaxError happened console.log("updated: count is now ", newModule.count); } }); ``` Prefer using `import.meta.hot.accept()` without an argument as it usually makes your code easier to understand. ### Accepting other modules ```ts title="index.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import { count } from "./foo"; import.meta.hot.accept("./foo", () => { if (!newModule) return; console.log("updated: count is now ", count); }); ``` Indicates that a dependency's module can be accepted. When the dependency is updated, the callback will be called with the new module. ### With multiple dependencies ```ts title="index.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import.meta.hot.accept(["./foo", "./bar"], newModules => { // newModules is an array where each item corresponds to the updated module // or undefined if that module had a syntax error }); ``` Indicates that multiple dependencies' modules can be accepted. This variant accepts an array of dependencies, where the callback will receive the updated modules, and `undefined` for any that had errors. ## import.meta.hot.data `import.meta.hot.data` maintains state between module instances during hot replacement, enabling data transfer from previous to new versions. When `import.meta.hot.data` is written into, Bun will also mark this module as capable of self-accepting (equivalent of calling `import.meta.hot.accept()`). ```jsx title="index.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import { createRoot } from "react-dom/client"; import { App } from "./app"; const root = (import.meta.hot.data.root ??= createRoot(elem)); root.render(); // re-use an existing root ``` In production, `data` is inlined to be `{}`, meaning it cannot be used as a state holder. The above pattern is recommended for stateful modules because Bun knows it can minify `{}.prop ??= value` into `value` in production. ## import.meta.hot.dispose() Attaches an on-dispose callback. This is called: * Just before the module is replaced with another copy (before the next is loaded) * After the module is detached (removing all imports to this module, see `import.meta.hot.prune()`) ```ts title="index.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} const sideEffect = setupSideEffect(); import.meta.hot.dispose(() => { sideEffect.cleanup(); }); ``` This callback is not called on route navigation or when the browser tab closes. Returning a promise will delay module replacement until the module is disposed. All dispose callbacks are called in parallel. ## import.meta.hot.prune() Attaches an on-prune callback. This is called when all imports to this module are removed, but the module was previously loaded. This can be used to clean up resources that were created when the module was loaded. Unlike `import.meta.hot.dispose()`, this pairs much better with `accept` and `data` to manage stateful resources. A full example managing a WebSocket: ```ts title="index.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import { something } from "./something"; // Initialize or re-use a WebSocket connection export const ws = (import.meta.hot.data.ws ??= new WebSocket(location.origin)); // If the module's import is removed, clean up the WebSocket connection. import.meta.hot.prune(() => { ws.close(); }); ``` If `dispose` was used instead, the WebSocket would close and re-open on every hot update. Both versions of the code will prevent page reloads when imported files are updated. ## import.meta.hot.on() and off() `on()` and `off()` are used to listen for events from the HMR runtime. Event names are prefixed with a prefix so that plugins do not conflict with each other. ```ts title="index.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import.meta.hot.on("bun:beforeUpdate", () => { console.log("before a hot update"); }); ``` When a file is replaced, all of its event listeners are automatically removed. ### Built-in events | Event | Emitted when | | ---------------------- | ----------------------------------------------------------------------------------------------- | | `bun:beforeUpdate` | before a hot update is applied. | | `bun:afterUpdate` | after a hot update is applied. | | `bun:beforeFullReload` | before a full page reload happens. | | `bun:beforePrune` | before prune callbacks are called. | | `bun:invalidate` | when a module is invalidated with `import.meta.hot.invalidate()` | | `bun:error` | when a build or runtime error occurs | | `bun:ws:disconnect` | when the HMR WebSocket connection is lost. This can indicate the development server is offline. | | `bun:ws:connect` | when the HMR WebSocket connects or re-connects. | For compatibility with Vite, the above events are also available via `vite:*` prefix instead of `bun:*`. # HTML & static sites Source: https://bun.com/docs/bundler/html-static Build static sites, landing pages, and web applications with Bun's bundler Bun's bundler has first-class support for HTML. Build static sites, landing pages, and web applications with zero configuration. Just point Bun at your HTML file and it handles everything else. ```html title="index.html" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} ``` To get started, pass HTML files to `bun`. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun ./index.html ``` ``` Bun v1.2.20 ready in 6.62ms → http://localhost:3000/ Press h + Enter to show shortcuts ``` Bun's development server provides powerful features with zero configuration: * **Automatic Bundling** - Bundles and serves your HTML, JavaScript, and CSS * **Multi-Entry Support** - Handles multiple HTML entry points and glob entry points * **Modern JavaScript** - TypeScript & JSX support out of the box * **Smart Configuration** - Reads `tsconfig.json` for paths, JSX options, experimental decorators, and more * **Plugins** - Plugins for TailwindCSS and more * **ESM & CommonJS** - Use ESM and CommonJS in your JavaScript, TypeScript, and JSX files * **CSS Bundling & Minification** - Bundles CSS from `` tags and `@import` statements * **Asset Management** - Automatic copying & hashing of images and assets; Rewrites asset paths in JavaScript, CSS, and HTML ## Single Page Apps (SPA) When you pass a single `.html` file to Bun, Bun will use it as a fallback route for all paths. This makes it perfect for single page apps that use client-side routing: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun index.html ``` ``` Bun v1.2.20 ready in 6.62ms → http://localhost:3000/ Press h + Enter to show shortcuts ``` Your React or other SPA will work out of the box — no configuration needed. All routes like `/about`, `/users/123`, etc. will serve the same HTML file, letting your client-side router handle the navigation. ```html title="index.html" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} My SPA
``` ## Multi-page apps (MPA) Some projects have several separate routes or HTML files as entry points. To support multiple entry points, pass them all to `bun`: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun ./index.html ./about.html ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} Bun v1.2.20 ready in 6.62ms → http://localhost:3000/ Routes: / ./index.html /about ./about.html Press h + Enter to show shortcuts ``` This will serve: * `index.html` at `/` * `about.html` at `/about` ### Glob patterns To specify multiple files, you can use glob patterns that end in `.html`: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun ./**/*.html ``` ``` Bun v1.2.20 ready in 6.62ms → http://localhost:3000/ Routes: / ./index.html /about ./about.html Press h + Enter to show shortcuts ``` ### Path normalization The base path is chosen from the longest common prefix among all the files. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun ./index.html ./about/index.html ./about/foo/index.html ``` ``` Bun v1.2.20 ready in 6.62ms → http://localhost:3000/ Routes: / ./index.html /about ./about/index.html /about/foo ./about/foo/index.html Press h + Enter to show shortcuts ``` ## JavaScript, TypeScript, and JSX Bun's transpiler natively implements JavaScript, TypeScript, and JSX support. Learn more about loaders in Bun. Bun's transpiler is also used at runtime. ### ES Modules & CommonJS You can use ESM and CJS in your JavaScript, TypeScript, and JSX files. Bun will handle the transpilation and bundling automatically. There is no pre-build or separate optimization step. It's all done at the same time. Learn more about module resolution in Bun. ## CSS Bun's CSS parser is also natively implemented (clocking in around 58,000 lines of Zig). It's also a CSS bundler. You can use `@import` in your CSS files to import other CSS files. For example: ```css styles.css icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} @import "./abc.css"; .container { background-color: blue; } ``` ```css abc.css theme={"theme":{"light":"github-light","dark":"dracula"}} body { background-color: red; } ``` This outputs: ```css theme={"theme":{"light":"github-light","dark":"dracula"}} body { background-color: red; } .container { background-color: blue; } ``` ### Referencing local assets in CSS You can reference local assets in your CSS files. ```css styles.css icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} body { background-image: url("./logo.png"); } ``` This will copy `./logo.png` to the output directory and rewrite the path in the CSS file to include a content hash. ```css styles.css icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} body { background-image: url("./logo-[ABC123].png"); } ``` ### Importing CSS in JavaScript To associate a CSS file with a JavaScript file, you can import it in your JavaScript file. ```ts app.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import "./styles.css"; import "./more-styles.css"; ``` This generates `./app.css` and `./app.js` in the output directory. All CSS files imported from JavaScript will be bundled into a single CSS file per entry point. If you import the same CSS file from multiple JavaScript files, it will only be included once in the output CSS file. ## Plugins The dev server supports plugins. ### Tailwind CSS To use TailwindCSS, install the `bun-plugin-tailwind` plugin: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} # Or any npm client bun install --dev bun-plugin-tailwind ``` Then, add the plugin to your `bunfig.toml`: ```toml title="bunfig.toml" icon="settings" theme={"theme":{"light":"github-light","dark":"dracula"}} [serve.static] plugins = ["bun-plugin-tailwind"] ``` Then, reference TailwindCSS in your HTML via `` tag, `@import` in CSS, or import in JavaScript. ```html title="index.html" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} {/* Reference TailwindCSS in your HTML */} ``` `css title="styles.css" icon="file-code" @import "tailwindcss"; ` `ts title="app.ts" icon="/icons/typescript.svg" import "tailwindcss"; ` Only one of those are necessary, not all three. ## Echo console logs from browser to terminal Bun's dev server supports streaming console logs from the browser to the terminal. To enable, pass the `--console` CLI flag. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun ./index.html --console ``` ``` Bun v1.2.20 ready in 6.62ms → http://localhost:3000/ Press h + Enter to show shortcuts ``` Each call to `console.log` or `console.error` will be broadcast to the terminal that started the server. This is useful to see errors from the browser in the same place you run your server. This is also useful for AI agents that watch terminal output. Internally, this reuses the existing WebSocket connection from hot module reloading to send the logs. ## Edit files in the browser Bun's frontend dev server has support for Automatic Workspace Folders in Chrome DevTools, which lets you save edits to files in the browser. ## Keyboard Shortcuts While the server is running: * `o + Enter` - Open in browser * `c + Enter` - Clear console * `q + Enter` (or `Ctrl+C`) - Quit server ## Build for Production When you're ready to deploy, use `bun build` to create optimized production bundles: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun build ./index.html --minify --outdir=dist ``` ```ts title="build.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} await Bun.build({ entrypoints: ["./index.html"], outdir: "./dist", minify: true, }); ``` Currently, plugins are only supported through `Bun.build`'s API or through `bunfig.toml` with the frontend dev server - not yet supported in `bun build`'s CLI. ### Watch Mode You can run `bun build --watch` to watch for changes and rebuild automatically. This works nicely for library development. You've never seen a watch mode this fast. ## Plugin API Need more control? Configure the bundler through the JavaScript API and use Bun's builtin `HTMLRewriter` to preprocess HTML. ```ts title="build.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} await Bun.build({ entrypoints: ["./index.html"], outdir: "./dist", minify: true, plugins: [ { // A plugin that makes every HTML tag lowercase name: "lowercase-html-plugin", setup({ onLoad }) { const rewriter = new HTMLRewriter().on("*", { element(element) { element.tagName = element.tagName.toLowerCase(); }, text(element) { element.replace(element.text.toLowerCase()); }, }); onLoad({ filter: /\.html$/ }, async args => { const html = await Bun.file(args.path).text(); return { // Bun's bundler will scan the HTML for ``` It will output a new HTML file with the bundled assets: ```html title="dist/index.html" theme={"theme":{"light":"github-light","dark":"dracula"}} Local image External image ``` Under the hood, it uses [`lol-html`](https://github.com/cloudflare/lol-html) to extract script and link tags as entrypoints, and other assets as external. Currently, the list of selectors is: * `audio[src]` * `iframe[src]` * `img[src]` * `img[srcset]` * `link:not([rel~='stylesheet']):not([rel~='modulepreload']):not([rel~='manifest']):not([rel~='icon']):not([rel~='apple-touch-icon'])[href]` * `link[as='font'][href], link[type^='font/'][href]` * `link[as='image'][href]` * `link[as='style'][href]` * `link[as='video'][href], link[as='audio'][href]` * `link[as='worker'][href]` * `link[rel='icon'][href], link[rel='apple-touch-icon'][href]` * `link[rel='manifest'][href]` * `link[rel='stylesheet'][href]` * `script[src]` * `source[src]` * `source[srcset]` * `video[poster]` * `video[src]` **HTML Loader Behavior in Different Contexts** The `html` loader behaves differently depending on how it's used: * Static Build: When you run `bun build ./index.html`, Bun produces a static site with all assets bundled and hashed. * Runtime: When you run `bun run server.ts` (where `server.ts` imports an HTML file), Bun bundles assets on-the-fly during development, enabling features like hot module replacement. * Full-stack Build: When you run `bun build --target=bun server.ts` (where `server.ts` imports an HTML file), the import resolves to a manifest object that `Bun.serve` uses to efficiently serve pre-bundled assets in production. *** ### sh **Bun Shell loader.** Default for `.sh` files. This loader is used to parse Bun Shell scripts. It's only supported when starting Bun itself, so it's not available in the bundler or in the runtime. ```bash theme={"theme":{"light":"github-light","dark":"dracula"}} bun run ./script.sh ``` *** ### file **File loader.** Default for all unrecognized file types. The file loader resolves the import as a path/URL to the imported file. It's commonly used for referencing media or font assets. ```js theme={"theme":{"light":"github-light","dark":"dracula"}} // logo.ts import logo from "./logo.svg"; console.log(logo); ``` In the runtime, Bun checks that the `logo.svg` file exists and converts it to an absolute path to the location of `logo.svg` on disk. ```bash theme={"theme":{"light":"github-light","dark":"dracula"}} bun run logo.ts # Output: /path/to/project/logo.svg ``` In the bundler, things are slightly different. The file is copied into `outdir` as-is, and the import is resolved as a relative path pointing to the copied file. ```js theme={"theme":{"light":"github-light","dark":"dracula"}} // Output var logo = "./logo.svg"; console.log(logo); ``` If a value is specified for `publicPath`, the import will use value as a prefix to construct an absolute path/URL. | Public path | Resolved import | | ---------------------------- | ---------------------------------- | | `""` (default) | `/logo.svg` | | `"/assets"` | `/assets/logo.svg` | | `"https://cdn.example.com/"` | `https://cdn.example.com/logo.svg` | The location and file name of the copied file is determined by the value of `naming.asset`. This loader is copied into the `outdir` as-is. The name of the copied file is determined using the value of `naming.asset`. # Macros Source: https://bun.com/docs/bundler/macros Run JavaScript functions at bundle-time with Bun macros Macros are a mechanism for running JavaScript functions at bundle-time. The value returned from these functions are directly inlined into your bundle. As a toy example, consider this simple function that returns a random number. ```ts title="random.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} export function random() { return Math.random(); } ``` This is just a regular function in a regular file, but we can use it as a macro like so: ```tsx title="cli.tsx" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import { random } from "./random.ts" with { type: "macro" }; console.log(`Your random number is ${random()}`); ``` Macros are indicated using import attribute syntax. If you haven't seen this syntax before, it's a Stage 3 TC39 proposal that lets you attach additional metadata to import statements. Now we'll bundle this file with `bun build`. The bundled file will be printed to stdout. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun build ./cli.tsx ``` ```js theme={"theme":{"light":"github-light","dark":"dracula"}} console.log(`Your random number is ${0.6805550949689833}`); ``` As you can see, the source code of the `random` function occurs nowhere in the bundle. Instead, it is executed during bundling and function call (`random()`) is replaced with the result of the function. Since the source code will never be included in the bundle, macros can safely perform privileged operations like reading from a database. ## When to use macros If you have several build scripts for small things where you would otherwise have a one-off build script, bundle-time code execution can be easier to maintain. It lives with the rest of your code, it runs with the rest of the build, it is automatically parallelized, and if it fails, the build fails too. If you find yourself running a lot of code at bundle-time though, consider running a server instead. ## Import attributes Bun Macros are import statements annotated using either: * `with { type: 'macro' }` — an import attribute, a Stage 3 ECMA Script proposal * `assert { type: 'macro' }` — an import assertion, an earlier incarnation of import attributes that has now been abandoned (but is already supported by a number of browsers and runtimes) ## Security considerations Macros must explicitly be imported with `{ type: "macro" }` in order to be executed at bundle-time. These imports have no effect if they are not called, unlike regular JavaScript imports which may have side effects. You can disable macros entirely by passing the `--no-macros` flag to Bun. It produces a build error like this: ``` error: Macros are disabled foo(); ^ ./hello.js:3:1 53 ``` To reduce the potential attack surface for malicious packages, macros cannot be invoked from inside `node_modules/**/*`. If a package attempts to invoke a macro, you'll see an error like this: ``` error: For security reasons, macros cannot be run from node_modules. beEvil(); ^ node_modules/evil/index.js:3:1 50 ``` Your application code can still import macros from `node_modules` and invoke them. ```ts title="cli.tsx" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import { macro } from "some-package" with { type: "macro" }; macro(); ``` ## Export condition "macro" When shipping a library containing a macro to npm or another package registry, use the `"macro"` export condition to provide a special version of your package exclusively for the macro environment. ```json title="package.json" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} { "name": "my-package", "exports": { "import": "./index.js", "require": "./index.js", "default": "./index.js", "macro": "./index.macro.js" } } ``` With this configuration, users can consume your package at runtime or at bundle-time using the same import specifier: ```ts title="index.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import pkg from "my-package"; // runtime import import { macro } from "my-package" with { type: "macro" }; // macro import ``` The first import will resolve to `./node_modules/my-package/index.js`, while the second will be resolved by Bun's bundler to `./node_modules/my-package/index.macro.js`. ## Execution When Bun's transpiler sees a macro import, it calls the function inside the transpiler using Bun's JavaScript runtime and converts the return value from JavaScript into an AST node. These JavaScript functions are called at bundle-time, not runtime. Macros are executed synchronously in the transpiler during the visiting phase—before plugins and before the transpiler generates the AST. They are executed in the order they are imported. The transpiler will wait for the macro to finish executing before continuing. The transpiler will also await any Promise returned by a macro. Bun's bundler is multi-threaded. As such, macros execute in parallel inside of multiple spawned JavaScript "workers". ## Dead code elimination The bundler performs dead code elimination after running and inlining macros. So given the following macro: ```ts title="returnFalse.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} export function returnFalse() { return false; } ``` ...then bundling the following file will produce an empty bundle, provided that the minify syntax option is enabled. ```ts title="index.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import { returnFalse } from "./returnFalse.ts" with { type: "macro" }; if (returnFalse()) { console.log("This code is eliminated"); } ``` ## Serializability Bun's transpiler needs to be able to serialize the result of the macro so it can be inlined into the AST. All JSON-compatible data structures are supported: ```ts title="macro.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} export function getObject() { return { foo: "bar", baz: 123, array: [1, 2, { nested: "value" }], }; } ``` Macros can be async, or return Promise instances. Bun's transpiler will automatically await the Promise and inline the result. ```ts title="macro.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} export async function getText() { return "async value"; } ``` The transpiler implements special logic for serializing common data formats like `Response`, `Blob`, `TypedArray`. * **TypedArray**: Resolves to a base64-encoded string. * **Response**: Bun will read the `Content-Type` and serialize accordingly; for instance, a Response with type `application/json` will be automatically parsed into an object and `text/plain` will be inlined as a string. Responses with an unrecognized or undefined type will be base-64 encoded. * **Blob**: As with Response, the serialization depends on the `type` property. The result of `fetch` is `Promise`, so it can be directly returned. ```ts title="macro.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} export function getObject() { return fetch("https://bun.com"); } ``` Functions and instances of most classes (except those mentioned above) are not serializable. ```ts title="macro.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} export function getText(url: string) { // this doesn't work! return () => {}; } ``` ## Arguments Macros can accept inputs, but only in limited cases. The value must be statically known. For example, the following is not allowed: ```ts title="index.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import { getText } from "./getText.ts" with { type: "macro" }; export function howLong() { // the value of `foo` cannot be statically known const foo = Math.random() ? "foo" : "bar"; const text = getText(`https://example.com/${foo}`); console.log("The page is ", text.length, " characters long"); } ``` However, if the value of `foo` is known at bundle-time (say, if it's a constant or the result of another macro) then it's allowed: ```ts title="index.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import { getText } from "./getText.ts" with { type: "macro" }; import { getFoo } from "./getFoo.ts" with { type: "macro" }; export function howLong() { // this works because getFoo() is statically known const foo = getFoo(); const text = getText(`https://example.com/${foo}`); console.log("The page is", text.length, "characters long"); } ``` This outputs: ```js theme={"theme":{"light":"github-light","dark":"dracula"}} function howLong() { console.log("The page is", 1322, "characters long"); } export { howLong }; ``` ## Examples ### Embed latest git commit hash ```ts title="getGitCommitHash.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} export function getGitCommitHash() { const { stdout } = Bun.spawnSync({ cmd: ["git", "rev-parse", "HEAD"], stdout: "pipe", }); return stdout.toString(); } ``` When we build it, the `getGitCommitHash` is replaced with the result of calling the function: ```ts input theme={"theme":{"light":"github-light","dark":"dracula"}} import { getGitCommitHash } from "./getGitCommitHash.ts" with { type: "macro" }; console.log(`The current Git commit hash is ${getGitCommitHash()}`); ``` ```ts output theme={"theme":{"light":"github-light","dark":"dracula"}} console.log(`The current Git commit hash is 3ee3259104e4507cf62c160f0ff5357ec4c7a7f8`); ``` You're probably thinking "Why not just use `process.env.GIT_COMMIT_HASH`?" Well, you can do that too. But can you do this with an environment variable? ### Make fetch() requests at bundle-time In this example, we make an outgoing HTTP request using `fetch()`, parse the HTML response using `HTMLRewriter`, and return an object containing the title and meta tags–all at bundle-time. ```ts title="meta.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} export async function extractMetaTags(url: string) { const response = await fetch(url); const meta = { title: "", }; new HTMLRewriter() .on("title", { text(element) { meta.title += element.text; }, }) .on("meta", { element(element) { const name = element.getAttribute("name") || element.getAttribute("property") || element.getAttribute("itemprop"); if (name) meta[name] = element.getAttribute("content"); }, }) .transform(response); return meta; } ``` The `extractMetaTags` function is erased at bundle-time and replaced with the result of the function call. This means that the fetch request happens at bundle-time, and the result is embedded in the bundle. Also, the branch throwing the error is eliminated since it's unreachable. ```jsx input theme={"theme":{"light":"github-light","dark":"dracula"}} import { extractMetaTags } from "./meta.ts" with { type: "macro" }; export const Head = () => { const headTags = extractMetaTags("https://example.com"); if (headTags.title !== "Example Domain") { throw new Error("Expected title to be 'Example Domain'"); } return ( {headTags.title} ); }; ``` ```jsx output theme={"theme":{"light":"github-light","dark":"dracula"}} export const Head = () => { const headTags = { title: "Example Domain", viewport: "width=device-width, initial-scale=1", }; return ( {headTags.title} ); }; ``` # null Source: https://bun.com/docs/bundler/minifier Reduce bundle sizes with Bun's JavaScript and TypeScript minifier Bun includes a fast JavaScript and TypeScript minifier that can reduce bundle sizes by 80% or more (depending on the codebase) and make output code run faster. The minifier performs dozens of optimizations including constant folding, dead code elimination, and syntax transformations. Unlike other minifiers, Bun's minifier makes `bun build` run faster since there's less code to print. ## CLI Usage ### Enable all minification Use the `--minify` flag to enable all minification modes: ```bash theme={"theme":{"light":"github-light","dark":"dracula"}} bun build ./index.ts --minify --outfile=out.js ``` The `--minify` flag automatically enables: * Whitespace minification * Syntax minification * Identifier minification ### Production mode The `--production` flag automatically enables minification: ```bash theme={"theme":{"light":"github-light","dark":"dracula"}} bun build ./index.ts --production --outfile=out.js ``` The `--production` flag also: * Sets `process.env.NODE_ENV` to `production` * Enables the production-mode JSX import & transform ### Granular control You can enable specific minification modes individually: ```bash theme={"theme":{"light":"github-light","dark":"dracula"}} # Only remove whitespace bun build ./index.ts --minify-whitespace --outfile=out.js # Only minify syntax bun build ./index.ts --minify-syntax --outfile=out.js # Only minify identifiers bun build ./index.ts --minify-identifiers --outfile=out.js # Combine specific modes bun build ./index.ts --minify-whitespace --minify-syntax --outfile=out.js ``` ## JavaScript API When using Bun's bundler programmatically, configure minification through the `minify` option: ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} await Bun.build({ entrypoints: ["./index.ts"], outdir: "./out", minify: true, // Enable all minification modes }); ``` For granular control, pass an object: ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} await Bun.build({ entrypoints: ["./index.ts"], outdir: "./out", minify: { whitespace: true, syntax: true, identifiers: true, }, }); ``` ## Minification Modes Bun's minifier has three independent modes that can be enabled separately or combined. ### Whitespace minification (`--minify-whitespace`) Removes all unnecessary whitespace, newlines, and formatting from the output. ### Syntax minification (`--minify-syntax`) Rewrites JavaScript syntax to shorter equivalent forms and performs constant folding, dead code elimination, and other optimizations. ### Identifier minification (`--minify-identifiers`) Renames local variables and function names to shorter identifiers using frequency-based optimization. ## All Transformations ### Boolean literal shortening **Mode:** `--minify-syntax` Converts boolean literals to shorter expressions. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} true; false; ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} !0; !1; ``` ### Boolean algebra optimizations **Mode:** `--minify-syntax` Simplifies boolean expressions using logical rules. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} !!x; x === true; x && true; x || false; !true; !false; ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} x; x; x; x; !1; !0; ``` ### Undefined shortening **Mode:** `--minify-syntax` Replaces `undefined` with shorter equivalent. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} undefined; let x = undefined; ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} void 0; let x = void 0; ``` ### Undefined equality optimization **Mode:** `--minify-syntax` Optimizes loose equality checks with undefined. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} x == undefined; x != undefined; ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} x == null; x != null; ``` ### Infinity shortening **Mode:** `--minify-syntax` Converts Infinity to mathematical expressions. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} Infinity - Infinity; ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} 1 / 0 - 1 / 0; ``` ### Typeof optimizations **Mode:** `--minify-syntax` Optimizes typeof comparisons and evaluates constant typeof expressions. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} typeof x === "undefined"; typeof x !== "undefined"; typeof require; typeof null; typeof true; typeof 123; typeof "str"; typeof 123n; ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} typeof x > "u"; typeof x < "u"; ("function"); ("object"); ("boolean"); ("number"); ("string"); ("bigint"); ``` ### Number formatting **Mode:** `--minify-syntax` Formats numbers in the most compact representation. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} 10000; 100000; 1000000; 1.0 - 42.0; ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} 1e4; 1e5; 1e6; 1 - 42; ``` ### Arithmetic constant folding **Mode:** `--minify-syntax` Evaluates arithmetic operations at compile time. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} 1 + 2; 10 - 5; 3 * 4; 10 / 2; 10 % 3; 2 ** 3; ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} 3; 5; 12; 5; 1; 8; ``` ### Bitwise constant folding **Mode:** `--minify-syntax` Evaluates bitwise operations at compile time. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} 5 & 3; 5 | 3; 5 ^ 3; 8 << 2; 32 >> 2; ~5; ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} 1; 7; 6; 32; 8 - 6; ``` ### String concatenation **Mode:** `--minify-syntax` Combines string literals at compile time. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} "a" + "b"; "x" + 123; "foo" + "bar" + "baz"; ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} "ab"; "x123"; "foobarbaz"; ``` ### String indexing **Mode:** `--minify-syntax` Evaluates string character access at compile time. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} "foo"[2]; "hello"[0]; ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} "o"; "h"; ``` ### Template literal folding **Mode:** `--minify-syntax` Evaluates template literals with constant expressions. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} `a${123}b``result: ${5 + 10}`; ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} "a123b"; "result: 15"; ``` ### Template literal to string conversion **Mode:** `--minify-syntax` Converts simple template literals to regular strings. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} `Hello World``Line 1 Line 2`; ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} "Hello World"; "Line 1\nLine 2"; ``` ### String quote optimization **Mode:** `--minify-syntax` Chooses the optimal quote character to minimize escapes. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} "It's a string"; 'He said "hello"'`Simple string`; ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} "It's a string"; 'He said "hello"'; "Simple string"; ``` ### Array spread inlining **Mode:** `--minify-syntax` Inlines array spread operations with constant arrays. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} [1, ...[2, 3], 4] [...[a, b]] ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} [1, 2, 3, 4][(a, b)]; ``` ### Array indexing **Mode:** `--minify-syntax` Evaluates constant array access at compile time. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} [x][0] ['a', 'b', 'c'][1] ['a', , 'c'][1] ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} x; ("b"); void 0; ``` ### Property access optimization **Mode:** `--minify-syntax` Converts bracket notation to dot notation when possible. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} obj["property"]; obj["validName"]; obj["123"]; obj["invalid-name"]; ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} obj.property; obj.validName; obj["123"]; obj["invalid-name"]; ``` ### Comparison folding **Mode:** `--minify-syntax` Evaluates constant comparisons at compile time. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} 3 < 5; 5 > 3; 3 <= 3; 5 >= 6; "a" < "b"; ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} !0; !0; !0; !1; !0; ``` ### Logical operation folding **Mode:** `--minify-syntax` Simplifies logical operations with constant values. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} true && x; false && x; true || x; false || x; ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} x; !1; !0; x; ``` ### Nullish coalescing folding **Mode:** `--minify-syntax` Evaluates nullish coalescing with known values. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} null ?? x; undefined ?? x; 42 ?? x; ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} x; x; 42; ``` ### Comma expression simplification **Mode:** `--minify-syntax` Removes side-effect-free expressions from comma sequences. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} (0, x)(123, "str", x); ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} x; x; ``` ### Ternary conditional folding **Mode:** `--minify-syntax` Evaluates conditional expressions with constant conditions. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} true ? a : b; false ? a : b; x ? true : false; x ? false : true; ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} a; b; x; !x; ``` ### Unary expression folding **Mode:** `--minify-syntax` Simplifies unary operations. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} +123 + "123" - -x; ~~x; !!x; ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} 123; 123; x; x; x; ``` ### Double negation removal **Mode:** `--minify-syntax` Removes unnecessary double negations. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} !!x; !!!x; ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} x; !x; ``` ### If statement optimization **Mode:** `--minify-syntax` Optimizes if statements with constant conditions. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} if (true) x; if (false) x; if (x) { a; } if (x) { } else y; ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} x; // removed if (x) a; if (!x) y; ``` ### Dead code elimination **Mode:** `--minify-syntax` Removes unreachable code and code without side effects. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} if (false) { unreachable(); } function foo() { return x; deadCode(); } ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} function foo() { return x; } ``` ### Unreachable branch removal **Mode:** `--minify-syntax` Removes branches that can never execute. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} while (false) { neverRuns(); } ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} // removed entirely ``` ### Empty block removal **Mode:** `--minify-syntax` Removes empty blocks and unnecessary braces. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} { } if (x) { } ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} // removed ``` ### Single statement block unwrapping **Mode:** `--minify-syntax` Removes unnecessary braces around single statements. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} if (condition) { doSomething(); } ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} if (condition) doSomething(); ``` ### TypeScript enum inlining **Mode:** `--minify-syntax` Inlines TypeScript enum values at compile time. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} enum Color { Red, Green, Blue, } const x = Color.Red; ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} const x = 0; ``` ### Pure annotation support **Mode:** Always active Respects `/*@__PURE__*/` annotations for tree shaking. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} const x = /*@__PURE__*/ expensive(); // If x is unused... ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} // removed entirely ``` ### Identifier renaming **Mode:** `--minify-identifiers` Renames local variables to shorter names based on usage frequency. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} function calculateSum(firstNumber, secondNumber) { const result = firstNumber + secondNumber; return result; } ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} function a(b, c) { const d = b + c; return d; } ``` **Naming strategy:** * Most frequently used identifiers get the shortest names (a, b, c...) * Single letters: a-z (26 names) * Double letters: aa-zz (676 names) * Triple letters and beyond as needed **Preserved identifiers:** * JavaScript keywords and reserved words * Global identifiers * Named exports (to maintain API) * CommonJS names: `exports`, `module` ### Whitespace removal **Mode:** `--minify-whitespace` Removes all unnecessary whitespace. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} function add(a, b) { return a + b; } let x = 10; ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} function add(a, b) { return a + b; } let x = 10; ``` ### Semicolon optimization **Mode:** `--minify-whitespace` Inserts semicolons only when necessary. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} let a = 1; let b = 2; return a + b; ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} let a = 1; let b = 2; return a + b; ``` ### Operator spacing removal **Mode:** `--minify-whitespace` Removes spaces around operators. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} a + b; x = y * z; (foo && bar) || baz; ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} a + b; x = y * z; (foo && bar) || baz; ``` ### Comment removal **Mode:** `--minify-whitespace` Removes comments except important license comments. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} // This comment is removed /* So is this */ /*! But this license comment is kept */ function test() { /* inline comment */ } ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} /*! But this license comment is kept */ function test() {} ``` ### Object and array formatting **Mode:** `--minify-whitespace` Removes whitespace in object and array literals. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} const obj = { name: "John", age: 30, }; const arr = [1, 2, 3]; ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} const obj = { name: "John", age: 30 }; const arr = [1, 2, 3]; ``` ### Control flow formatting **Mode:** `--minify-whitespace` Removes whitespace in control structures. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} if (condition) { doSomething(); } for (let i = 0; i < 10; i++) { console.log(i); } ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} if (condition) doSomething(); for (let i = 0; i < 10; i++) console.log(i); ``` ### Function formatting **Mode:** `--minify-whitespace` Removes whitespace in function declarations. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} function myFunction(param1, param2) { return param1 + param2; } const arrow = (a, b) => a + b; ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} function myFunction(a, b) { return a + b; } const arrow = (a, b) => a + b; ``` ### Parentheses minimization **Mode:** Always active Only adds parentheses when necessary for operator precedence. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} (a + b) * c; a + (b * c)(x); ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} (a + b) * c; a + b * c; x; ``` ### Property mangling **Mode:** `--minify-identifiers` (with configuration) Renames object properties to shorter names when configured. ```ts#input.ts (with property mangling enabled) theme={"theme":{"light":"github-light","dark":"dracula"}} obj.longPropertyName ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} obj.a; ``` ### Template literal value folding **Mode:** `--minify-syntax` Converts non-string interpolated values to strings and folds them into the template. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} `hello ${123}``value: ${true}``result: ${null}``status: ${undefined}``big: ${10n}`; ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} "hello 123"; "value: true"; "result: null"; "status: undefined"; "big: 10"; ``` ### String length constant folding **Mode:** `--minify-syntax` Evaluates `.length` property on string literals at compile time. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} "hello world".length; "test".length; ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} 11; 4; ``` ### Constructor call simplification **Mode:** `--minify-syntax` Simplifies constructor calls for built-in types. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} new Object(); new Object(null); new Object({ a: 1 }); new Array(); new Array(x, y); ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} { } { } { a: 1; } [][(x, y)]; ``` ### Single property object inlining **Mode:** `--minify-syntax` Inlines property access for objects with a single property. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} ({ fn: () => console.log("hi") }).fn(); ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} (() => console.log("hi"))(); ``` ### String charCodeAt constant folding **Mode:** Always active Evaluates `charCodeAt()` on string literals for ASCII characters. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} "hello".charCodeAt(1); "A".charCodeAt(0); ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} 101; 65; ``` ### Void 0 equality to null equality **Mode:** `--minify-syntax` Converts loose equality checks with `void 0` to `null` since they're equivalent. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} x == void 0; x != void 0; ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} x == null; x != null; ``` ### Negation operator optimization **Mode:** `--minify-syntax` Moves negation operator through comma expressions. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} -(a, b) - (x, y, z); ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} (a, -b); (x, y, -z); ``` ### Import.meta property inlining **Mode:** Bundle mode Inlines `import.meta` properties at build time when values are known. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} import.meta.dir; import.meta.file; import.meta.path; import.meta.url; ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} "/path/to/directory"; "filename.js"; "/full/path/to/file.js"; "file:///full/path/to/file.js"; ``` ### Variable declaration merging **Mode:** `--minify-syntax` Merges adjacent variable declarations of the same type. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} let a = 1; let b = 2; const c = 3; const d = 4; ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} let a = 1, b = 2; const c = 3, d = 4; ``` ### Expression statement merging **Mode:** `--minify-syntax` Merges adjacent expression statements using comma operator. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} console.log(1); console.log(2); console.log(3); ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} (console.log(1), console.log(2), console.log(3)); ``` ### Return statement merging **Mode:** `--minify-syntax` Merges expressions before return with comma operator. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} console.log(x); return y; ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} return (console.log(x), y); ``` ### Throw statement merging **Mode:** `--minify-syntax` Merges expressions before throw with comma operator. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} console.log(x); throw new Error(); ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} throw (console.log(x), new Error()); ``` ### TypeScript enum cross-module inlining **Mode:** `--minify-syntax` (bundle mode) Inlines enum values across module boundaries. ```ts#input.ts (lib.ts) theme={"theme":{"light":"github-light","dark":"dracula"}} export enum Color { Red, Green, Blue } // Input (main.ts) import { Color } from './lib'; const x = Color.Red; ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} const x = 0; ``` ### Computed property enum inlining **Mode:** `--minify-syntax` Inlines enum values used as computed object properties. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} enum Keys { FOO = "foo", } const obj = { [Keys.FOO]: value }; ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} const obj = { foo: value }; ``` ### String number to numeric index **Mode:** `--minify-syntax` Converts string numeric property access to numeric index. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} obj["0"]; arr["5"]; ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} obj[0]; arr[5]; ``` ### Arrow function body shortening **Mode:** Always active Uses expression body syntax when an arrow function only returns a value. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} () => { return x; }; a => { return a + 1; }; ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} () => x; a => a + 1; ``` ### Object property shorthand **Mode:** Always active Uses shorthand syntax when property name and value identifier match. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} { x: x, y: y } { name: name, age: age } ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} { (x, y); } { (name, age); } ``` ### Method shorthand **Mode:** Always active Uses method shorthand syntax in object literals. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} { foo: function() {}, bar: async function() {} } ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} { foo() {}, async bar() {} } ``` ### Drop debugger statements **Mode:** `--drop=debugger` Removes `debugger` statements from code. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} function test() { debugger; return x; } ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} function test() { return x; } ``` ### Drop console calls **Mode:** `--drop=console` Removes all `console.*` method calls from code. ```ts Input theme={"theme":{"light":"github-light","dark":"dracula"}} console.log("debug"); console.warn("warning"); x = console.error("error"); ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} void 0; void 0; x = void 0; ``` ### Drop custom function calls **Mode:** `--drop=` Removes calls to specified global functions or methods. ```ts#input.ts with --drop=assert theme={"theme":{"light":"github-light","dark":"dracula"}} assert(condition); obj.assert(test); ``` ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} void 0; void 0; ``` ## Keep Names When minifying identifiers, you may want to preserve original function and class names for debugging purposes. Use the `--keep-names` flag: ```bash theme={"theme":{"light":"github-light","dark":"dracula"}} bun build ./index.ts --minify --keep-names --outfile=out.js ``` Or in the JavaScript API: ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} await Bun.build({ entrypoints: ["./index.ts"], outdir: "./out", minify: { identifiers: true, keepNames: true, }, }); ``` This preserves the `.name` property on functions and classes while still minifying the actual identifier names in the code. ## Combined Example Using all three minification modes together: ```ts#input.ts (158 bytes) theme={"theme":{"light":"github-light","dark":"dracula"}} const myVariable = 42; const myFunction = () => { const isValid = true; const result = undefined; return isValid ? myVariable : result; }; const output = myFunction(); ``` ```ts#output.ts theme={"theme":{"light":"github-light","dark":"dracula"}} // Output with --minify (49 bytes, 69% reduction) const a=42,b=()=>{const c=!0,d=void 0;return c?a:d},e=b(); ``` ## When to Use Minification **Use `--minify` for:** * Production bundles * Reducing CDN bandwidth costs * Improving page load times **Use individual modes for:** * **`--minify-whitespace`:** Quick size reduction without semantic changes * **`--minify-syntax`:** Smaller output while keeping readable identifiers for debugging * **`--minify-identifiers`:** Maximum size reduction (combine with `--keep-names` for better stack traces) **Avoid minification for:** * Development builds (harder to debug) * When you need readable error messages * Libraries where consumers may read the source # Plugins Source: https://bun.com/docs/bundler/plugins Universal plugin API for extending Bun's runtime and bundler Bun provides a universal plugin API that can be used to extend both the runtime and bundler. Plugins intercept imports and perform custom loading logic: reading files, transpiling code, etc. They can be used to add support for additional file types, like `.scss` or `.yaml`. In the context of Bun's bundler, plugins can be used to implement framework-level features like CSS extraction, macros, and client-server code co-location. ## Lifecycle hooks Plugins can register callbacks to be run at various points in the lifecycle of a bundle: * `onStart()`: Run once the bundler has started a bundle * `onResolve()`: Run before a module is resolved * `onLoad()`: Run before a module is loaded * `onBeforeParse()`: Run zero-copy native addons in the parser thread before a file is parsed ## Reference A rough overview of the types (please refer to Bun's `bun.d.ts` for the full type definitions): ```ts title="bun.d.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} type PluginBuilder = { onStart(callback: () => void): void; onResolve: ( args: { filter: RegExp; namespace?: string }, callback: (args: { path: string; importer: string }) => { path: string; namespace?: string; } | void, ) => void; onLoad: ( args: { filter: RegExp; namespace?: string }, defer: () => Promise, callback: (args: { path: string }) => { loader?: Loader; contents?: string; exports?: Record; }, ) => void; config: BuildConfig; }; type Loader = "js" | "jsx" | "ts" | "tsx" | "css" | "json" | "toml"; ``` ## Usage A plugin is defined as simple JavaScript object containing a `name` property and a `setup` function. ```ts title="myPlugin.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import type { BunPlugin } from "bun"; const myPlugin: BunPlugin = { name: "Custom loader", setup(build) { // implementation }, }; ``` This plugin can be passed into the `plugins` array when calling `Bun.build`. ```ts title="index.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} await Bun.build({ entrypoints: ["./app.ts"], outdir: "./out", plugins: [myPlugin], }); ``` ## Plugin lifecycle ### Namespaces `onLoad` and `onResolve` accept an optional `namespace` string. What is a namespace? Every module has a namespace. Namespaces are used to prefix the import in transpiled code; for instance, a loader with a `filter: /\.yaml$/` and `namespace: "yaml:"` will transform an import from `./myfile.yaml` into `yaml:./myfile.yaml`. The default namespace is `"file"` and it is not necessary to specify it, for instance: `import myModule from "./my-module.ts"` is the same as `import myModule from "file:./my-module.ts"`. Other common namespaces are: * `"bun"`: for Bun-specific modules (e.g. `"bun:test"`, `"bun:sqlite"`) * `"node"`: for Node.js modules (e.g. `"node:fs"`, `"node:path"`) ### onStart ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} onStart(callback: () => void): Promise | void; ``` Registers a callback to be run when the bundler starts a new bundle. ```ts title="index.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import { plugin } from "bun"; plugin({ name: "onStart example", setup(build) { build.onStart(() => { console.log("Bundle started!"); }); }, }); ``` The callback can return a Promise. After the bundle process has initialized, the bundler waits until all `onStart()` callbacks have completed before continuing. For example: ```ts title="index.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} const result = await Bun.build({ entrypoints: ["./app.ts"], outdir: "./dist", sourcemap: "external", plugins: [ { name: "Sleep for 10 seconds", setup(build) { build.onStart(async () => { await Bun.sleep(10_000); }); }, }, { name: "Log bundle time to a file", setup(build) { build.onStart(async () => { const now = Date.now(); await Bun.$`echo ${now} > bundle-time.txt`; }); }, }, ], }); ``` In the above example, Bun will wait until the first `onStart()` (sleeping for 10 seconds) has completed, as well as the second `onStart()` (writing the bundle time to a file). `onStart()` callbacks (like every other lifecycle callback) do not have the ability to modify the `build.config` object. If you want to mutate `build.config`, you must do so directly in the `setup()` function. ### onResolve ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} onResolve( args: { filter: RegExp; namespace?: string }, callback: (args: { path: string; importer: string }) => { path: string; namespace?: string; } | void, ): void; ``` To bundle your project, Bun walks down the dependency tree of all modules in your project. For each imported module, Bun actually has to find and read that module. The "finding" part is known as "resolving" a module. The `onResolve()` plugin lifecycle callback allows you to configure how a module is resolved. The first argument to `onResolve()` is an object with a `filter` and `namespace` property. The `filter` is a regular expression which is run on the import string. Effectively, these allow you to filter which modules your custom resolution logic will apply to. The second argument to `onResolve()` is a callback which is run for each module import Bun finds that matches the filter and namespace defined in the first argument. The callback receives as input the path to the matching module. The callback can return a new path for the module. Bun will read the contents of the new path and parse it as a module. For example, redirecting all imports to `images/` to `./public/images/`: ```ts title="index.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import { plugin } from "bun"; plugin({ name: "onResolve example", setup(build) { build.onResolve({ filter: /.*/, namespace: "file" }, args => { if (args.path.startsWith("images/")) { return { path: args.path.replace("images/", "./public/images/"), }; } }); }, }); ``` ### onLoad ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} onLoad( args: { filter: RegExp; namespace?: string }, defer: () => Promise, callback: (args: { path: string, importer: string, namespace: string, kind: ImportKind }) => { loader?: Loader; contents?: string; exports?: Record; }, ): void; ``` After Bun's bundler has resolved a module, it needs to read the contents of the module and parse it. The `onLoad()` plugin lifecycle callback allows you to modify the contents of a module before it is read and parsed by Bun. Like `onResolve()`, the first argument to `onLoad()` allows you to filter which modules this invocation of `onLoad()` will apply to. The second argument to `onLoad()` is a callback which is run for each matching module before Bun loads the contents of the module into memory. This callback receives as input the path to the matching module, the importer of the module (the module that imported the module), the namespace of the module, and the kind of the module. The callback can return a new `contents` string for the module as well as a new `loader`. For example: ```ts title="index.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import { plugin } from "bun"; const envPlugin: BunPlugin = { name: "env plugin", setup(build) { build.onLoad({ filter: /env/, namespace: "file" }, args => { return { contents: `export default ${JSON.stringify(process.env)}`, loader: "js", }; }); }, }); Bun.build({ entrypoints: ["./app.ts"], outdir: "./dist", plugins: [envPlugin], }); // import env from "env" // env.FOO === "bar" ``` This plugin will transform all imports of the form `import env from "env"` into a JavaScript module that exports the current environment variables. #### .defer() One of the arguments passed to the `onLoad` callback is a `defer` function. This function returns a Promise that is resolved when all other modules have been loaded. This allows you to delay execution of the `onLoad` callback until all other modules have been loaded. This is useful for returning contents of a module that depends on other modules. ```ts title="index.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import { plugin } from "bun"; plugin({ name: "track imports", setup(build) { const transpiler = new Bun.Transpiler(); let trackedImports: Record = {}; // Each module that goes through this onLoad callback // will record its imports in `trackedImports` build.onLoad({ filter: /\.ts/ }, async ({ path }) => { const contents = await Bun.file(path).arrayBuffer(); const imports = transpiler.scanImports(contents); for (const i of imports) { trackedImports[i.path] = (trackedImports[i.path] || 0) + 1; } return undefined; }); build.onLoad({ filter: /stats\.json/ }, async ({ defer }) => { // Wait for all files to be loaded, ensuring // that every file goes through the above `onLoad()` function // and their imports tracked await defer(); // Emit JSON containing the stats of each import return { contents: `export default ${JSON.stringify(trackedImports)}`, loader: "json", }; }); }, }); ``` The `.defer()` function currently has the limitation that it can only be called once per `onLoad` callback. ## Native plugins One of the reasons why Bun's bundler is so fast is that it is written in native code and leverages multi-threading to load and parse modules in parallel. However, one limitation of plugins written in JavaScript is that JavaScript itself is single-threaded. Native plugins are written as NAPI modules and can be run on multiple threads. This allows native plugins to run much faster than JavaScript plugins. In addition, native plugins can skip unnecessary work such as the UTF-8 -> UTF-16 conversion needed to pass strings to JavaScript. These are the following lifecycle hooks which are available to native plugins: * `onBeforeParse()`: Called on any thread before a file is parsed by Bun's bundler. Native plugins are NAPI modules which expose lifecycle hooks as C ABI functions. To create a native plugin, you must export a C ABI function which matches the signature of the native lifecycle hook you want to implement. ### Creating a native plugin in Rust Native plugins are NAPI modules which expose lifecycle hooks as C ABI functions. To create a native plugin, you must export a C ABI function which matches the signature of the native lifecycle hook you want to implement. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun add -g @napi-rs/cli napi new ``` Then install this crate: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} cargo add bun-native-plugin ``` Now, inside the `lib.rs` file, we'll use the `bun_native_plugin::bun` proc macro to define a function which will implement our native plugin. Here's an example implementing the `onBeforeParse` hook: ```rust title="lib.rs" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/rust.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=a19fdd19ab10419707f1ba90fa0a2cde" theme={"theme":{"light":"github-light","dark":"dracula"}} use bun_native_plugin::{define_bun_plugin, OnBeforeParse, bun, Result, anyhow, BunLoader}; use napi_derive::napi; /// Define the plugin and its name define_bun_plugin!("replace-foo-with-bar"); /// Here we'll implement `onBeforeParse` with code that replaces all occurrences of /// `foo` with `bar`. /// /// We use the #[bun] macro to generate some of the boilerplate code. /// /// The argument of the function (`handle: &mut OnBeforeParse`) tells /// the macro that this function implements the `onBeforeParse` hook. #[bun] pub fn replace_foo_with_bar(handle: &mut OnBeforeParse) -> Result<()> { // Fetch the input source code. let input_source_code = handle.input_source_code()?; // Get the Loader for the file let loader = handle.output_loader(); let output_source_code = input_source_code.replace("foo", "bar"); handle.set_output_source_code(output_source_code, BunLoader::BUN_LOADER_JSX); Ok(()) } ``` And to use it in `Bun.build()`: ```ts title="index.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import myNativeAddon from "./my-native-addon"; Bun.build({ entrypoints: ["./app.tsx"], plugins: [ { name: "my-plugin", setup(build) { build.onBeforeParse( { namespace: "file", filter: "**/*.tsx", }, { napiModule: myNativeAddon, symbol: "replace_foo_with_bar", // external: myNativeAddon.getSharedState() }, ); }, }, ], }); ``` ### onBeforeParse ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} onBeforeParse( args: { filter: RegExp; namespace?: string }, callback: { napiModule: NapiModule; symbol: string; external?: unknown }, ): void; ``` This lifecycle callback is run immediately before a file is parsed by Bun's bundler. As input, it receives the file's contents and can optionally return new source code. This callback can be called from any thread and so the napi module implementation must be thread-safe. # Feedback Source: https://bun.com/docs/feedback Share feedback, bug reports, and feature requests Whether you've found a bug, have a performance issue, or just want to suggest an improvement, here's how you can open a helpful issue: For general questions, please join our [Discord](https://discord.com/invite/CXdq2DP29u). ## Reporting Issues Try upgrading Bun to the latest version with `bun upgrade`. This might fix your problem without having to open an issue. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun upgrade ``` You can also try the latest canary release, which includes the most recent changes and bug fixes that haven't been released in a stable version yet. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun upgrade --canary # To revert back to the stable bun upgrade --stable ``` If the issue still persists after upgrading, continue to the next step. First take a minute to check if the issue has already been reported. Don't open a new issue if it has already been reported, it saves time for everyone and helps us focus on fixing things faster. * 🔍 [**Search existing issues**](https://github.com/oven-sh/bun/issues) * 💬 [**Check discussions**](https://github.com/oven-sh/bun/discussions) If you find a related issue, add a 👍 reaction or comment with extra details instead of opening a new one. If no one has reported the issue, please open a new issue or suggest an improvement. * 🐞 [**Report a Bug**](https://github.com/oven-sh/bun/issues/new?template=2-bug-report.yml) * ⚡ [**Suggest an Improvement**](https://github.com/oven-sh/bun/issues/new?template=4-feature-request.yml) Please provide as much detail as possible, including: * A clear and concise title * A code example or steps to reproduce the issue * The version of Bun you are using (run `bun --version`) * A detailed description of the issue (what happened, what you expected to happen, and what actually happened) * The operating system and version you are using * For MacOS and Linux: copy the output of `uname -mprs` * For Windows: copy the output of this command in the powershell console: ```powershell theme={"theme":{"light":"github-light","dark":"dracula"}} "$([Environment]::OSVersion | ForEach-Object VersionString) $(if ([Environment]::Is64BitOperatingSystem) { "x64" } else { "x86" })" ``` The Bun team will review the issue and get back to you as soon as possible! *** ## Use `bun feedback` Alternatively, you can use `bun feedback` to share feedback, bug reports, and feature requests directly with the Bun team. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun feedback "Love the new release!" bun feedback report.txt details.log echo "please document X" | bun feedback --email you@example.com ``` You can provide feedback as text arguments, file paths, or piped input. *** ## CLI Usage ```bash theme={"theme":{"light":"github-light","dark":"dracula"}} bun feedback [options] [feedback text ... | files ...] ``` ### Contact Information Set the email address used for this submission. Alias: -e ### Help Show this help message and exit. Alias: -h # Convert an ArrayBuffer to an array of numbers Source: https://bun.com/docs/guides/binary/arraybuffer-to-array To retrieve the contents of an `ArrayBuffer` as an array of numbers, create a [`Uint8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) over of the buffer. and use the [`Array.from()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from) method to convert it to an array. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const buf = new ArrayBuffer(64); const arr = new Uint8Array(buf); arr.length; // 64 arr[0]; // 0 (instantiated with all zeros) ``` *** The `Uint8Array` class supports array indexing and iteration. However if you wish to convert the instance to a regular `Array`, use `Array.from()`. (This will likely be slower than using the `Uint8Array` directly.) ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const buf = new ArrayBuffer(64); const uintArr = new Uint8Array(buf); const regularArr = Array.from(uintArr); // number[] ``` *** See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun. # Convert an ArrayBuffer to a Blob Source: https://bun.com/docs/guides/binary/arraybuffer-to-blob A [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) can be constructed from an array of "chunks", where each chunk is a string, binary data structure, or another `Blob`. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const buf = new ArrayBuffer(64); const blob = new Blob([buf]); ``` *** By default the `type` of the resulting `Blob` will be unset. This can be set manually. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const buf = new ArrayBuffer(64); const blob = new Blob([buf], { type: "application/octet-stream" }); blob.type; // => "application/octet-stream" ``` *** See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun. # Convert an ArrayBuffer to a Buffer Source: https://bun.com/docs/guides/binary/arraybuffer-to-buffer The Node.js [`Buffer`](https://nodejs.org/api/buffer.html) API predates the introduction of `ArrayBuffer` into the JavaScript language. Bun implements both. Use the static `Buffer.from()` method to create a `Buffer` from an `ArrayBuffer`. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const arrBuffer = new ArrayBuffer(64); const nodeBuffer = Buffer.from(arrBuffer); ``` *** To create a `Buffer` that only views a portion of the underlying buffer, pass the offset and length to the constructor. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const arrBuffer = new ArrayBuffer(64); const nodeBuffer = Buffer.from(arrBuffer, 0, 16); // view first 16 bytes ``` *** See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun. # Convert an ArrayBuffer to a string Source: https://bun.com/docs/guides/binary/arraybuffer-to-string Bun implements the Web-standard [`TextDecoder`](https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder) class for converting between binary data types and strings. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const buf = new ArrayBuffer(64); const decoder = new TextDecoder(); const str = decoder.decode(buf); ``` *** See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun. # Convert an ArrayBuffer to a Uint8Array Source: https://bun.com/docs/guides/binary/arraybuffer-to-typedarray A `Uint8Array` is a *typed array*, meaning it is a mechanism for viewing the data in an underlying `ArrayBuffer`. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const buffer = new ArrayBuffer(64); const arr = new Uint8Array(buffer); ``` *** Instances of other typed arrays can be created similarly. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const buffer = new ArrayBuffer(64); const arr1 = new Uint8Array(buffer); const arr2 = new Uint16Array(buffer); const arr3 = new Uint32Array(buffer); const arr4 = new Float32Array(buffer); const arr5 = new Float64Array(buffer); const arr6 = new BigInt64Array(buffer); const arr7 = new BigUint64Array(buffer); ``` *** To create a typed array that only views a portion of the underlying buffer, pass the offset and length to the constructor. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const buffer = new ArrayBuffer(64); const arr = new Uint8Array(buffer, 0, 16); // view first 16 bytes ``` *** See [Docs > API > Utils](https://bun.com/docs/api/utils) for more useful utilities. # Convert a Blob to an ArrayBuffer Source: https://bun.com/docs/guides/binary/blob-to-arraybuffer The [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) class provides a number of methods for consuming its contents in different formats, including `.arrayBuffer()`. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const blob = new Blob(["hello world"]); const buf = await blob.arrayBuffer(); ``` *** See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun. # Convert a Blob to a DataView Source: https://bun.com/docs/guides/binary/blob-to-dataview The [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) class provides a number of methods for consuming its contents in different formats. This snippets reads the contents to an `ArrayBuffer`, then creates a `DataView` from the buffer. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const blob = new Blob(["hello world"]); const arr = new DataView(await blob.arrayBuffer()); ``` *** See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun. # Convert a Blob to a ReadableStream Source: https://bun.com/docs/guides/binary/blob-to-stream The [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) class provides a number of methods for consuming its contents in different formats, including `.stream()`. This returns `Promise`. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const blob = new Blob(["hello world"]); const stream = await blob.stream(); ``` *** See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun. # Convert a Blob to a string Source: https://bun.com/docs/guides/binary/blob-to-string The [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) class provides a number of methods for consuming its contents in different formats, including `.text()`. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const blob = new Blob(["hello world"]); const str = await blob.text(); // => "hello world" ``` *** See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun. # Convert a Blob to a Uint8Array Source: https://bun.com/docs/guides/binary/blob-to-typedarray The [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) class provides a number of methods for consuming its contents in different formats. This snippets reads the contents to an `ArrayBuffer`, then creates a `Uint8Array` from the buffer. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const blob = new Blob(["hello world"]); const arr = new Uint8Array(await blob.arrayBuffer()); ``` *** See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun. # Convert a Buffer to an ArrayBuffer Source: https://bun.com/docs/guides/binary/buffer-to-arraybuffer The Node.js [`Buffer`](https://nodejs.org/api/buffer.html) class provides a way to view and manipulate data in an underlying `ArrayBuffer`, which is available via the `buffer` property. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const nodeBuf = Buffer.alloc(64); const arrBuf = nodeBuf.buffer; ``` *** See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun. # Convert a Buffer to a blob Source: https://bun.com/docs/guides/binary/buffer-to-blob A [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) can be constructed from an array of "chunks", where each chunk is a string, binary data structure (including `Buffer`), or another `Blob`. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const buf = Buffer.from("hello"); const blob = new Blob([buf]); ``` *** See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun. # Convert a Buffer to a ReadableStream Source: https://bun.com/docs/guides/binary/buffer-to-readablestream The naive approach to creating a [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) from a [`Buffer`](https://nodejs.org/api/buffer.html) is to use the `ReadableStream` constructor and enqueue the entire array as a single chunk. For a large buffer, this may be undesirable as this approach does not "streaming" the data in smaller chunks. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const buf = Buffer.from("hello world"); const stream = new ReadableStream({ start(controller) { controller.enqueue(buf); controller.close(); }, }); ``` *** To stream the data in smaller chunks, first create a `Blob` instance from the `Buffer`. Then use the [`Blob.stream()`](https://developer.mozilla.org/en-US/docs/Web/API/Blob/stream) method to create a `ReadableStream` that streams the data in chunks of a specified size. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const buf = Buffer.from("hello world"); const blob = new Blob([buf]); const stream = blob.stream(); ``` *** The chunk size can be set by passing a number to the `.stream()` method. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const buf = Buffer.from("hello world"); const blob = new Blob([buf]); // set chunk size of 1024 bytes const stream = blob.stream(1024); ``` *** See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun. # Convert a Buffer to a string Source: https://bun.com/docs/guides/binary/buffer-to-string The [`Buffer`](https://nodejs.org/api/buffer.html) class provides a built-in `.toString()` method that converts a `Buffer` to a string. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const buf = Buffer.from("hello"); const str = buf.toString(); // => "hello" ``` *** You can optionally specify an encoding and byte range. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const buf = Buffer.from("hello world!"); const str = buf.toString("utf8", 0, 5); // => "hello" ``` *** See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun. # Convert a Buffer to a Uint8Array Source: https://bun.com/docs/guides/binary/buffer-to-typedarray The Node.js [`Buffer`](https://nodejs.org/api/buffer.html) class extends [`Uint8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array), so no conversion is needed. All properties and methods on `Uint8Array` are available on `Buffer`. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const buf = Buffer.alloc(64); buf instanceof Uint8Array; // => true ``` *** See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun. # Convert a DataView to a string Source: https://bun.com/docs/guides/binary/dataview-to-string If a [`DataView`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView) contains ASCII-encoded text, you can convert it to a string using the [`TextDecoder`](https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder) class. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const dv: DataView = ...; const decoder = new TextDecoder(); const str = decoder.decode(dv); ``` *** See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun. # Convert a Uint8Array to an ArrayBuffer Source: https://bun.com/docs/guides/binary/typedarray-to-arraybuffer A [`Uint8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) is a *typed array* class, meaning it is a mechanism for viewing data in an underlying `ArrayBuffer`. The underlying `ArrayBuffer` is accessible via the `buffer` property. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const arr = new Uint8Array(64); arr.buffer; // => ArrayBuffer(64) ``` *** The `Uint8Array` may be a view over a *subset* of the data in the underlying `ArrayBuffer`. In this case, the `buffer` property will return the entire buffer, and the `byteOffset` and `byteLength` properties will indicate the subset. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const arr = new Uint8Array(64, 16, 32); arr.buffer; // => ArrayBuffer(64) arr.byteOffset; // => 16 arr.byteLength; // => 32 ``` *** See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun. # Convert a Uint8Array to a Blob Source: https://bun.com/docs/guides/binary/typedarray-to-blob A [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) can be constructed from an array of "chunks", where each chunk is a string, binary data structure (including `Uint8Array`), or another `Blob`. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const arr = new Uint8Array([0x68, 0x65, 0x6c, 0x6c, 0x6f]); const blob = new Blob([arr]); console.log(await blob.text()); // => "hello" ``` *** See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun. # Convert a Uint8Array to a Buffer Source: https://bun.com/docs/guides/binary/typedarray-to-buffer The [`Buffer`](https://nodejs.org/api/buffer.html) class extends [`Uint8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) with a number of additional methods. Use `Buffer.from()` to create a `Buffer` instance from a `Uint8Array`. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const arr: Uint8Array = ... const buf = Buffer.from(arr); ``` *** See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun. # Convert a Uint8Array to a DataView Source: https://bun.com/docs/guides/binary/typedarray-to-dataview A [`Uint8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) is a *typed array* class, meaning it is a mechanism for viewing data in an underlying `ArrayBuffer`. The following snippet creates a \[`DataView`] instance over the same range of data as the `Uint8Array`. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const arr: Uint8Array = ... const dv = new DataView(arr.buffer, arr.byteOffset, arr.byteLength); ``` *** See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun. # Convert a Uint8Array to a ReadableStream Source: https://bun.com/docs/guides/binary/typedarray-to-readablestream The naive approach to creating a [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) from a [`Uint8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) is to use the [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) constructor and enqueue the entire array as a single chunk. For larger chunks, this may be undesirable as it isn't actually "streaming" the data. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const arr = new Uint8Array(64); const stream = new ReadableStream({ start(controller) { controller.enqueue(arr); controller.close(); }, }); ``` *** To stream the data in smaller chunks, first create a `Blob` instance from the `Uint8Array`. Then use the [`Blob.stream()`](https://developer.mozilla.org/en-US/docs/Web/API/Blob/stream) method to create a `ReadableStream` that streams the data in chunks of a specified size. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const arr = new Uint8Array(64); const blob = new Blob([arr]); const stream = blob.stream(); ``` *** The chunk size can be set by passing a number to the `.stream()` method. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const arr = new Uint8Array(64); const blob = new Blob([arr]); // set chunk size of 1024 bytes const stream = blob.stream(1024); ``` *** See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun. # Convert a Uint8Array to a string Source: https://bun.com/docs/guides/binary/typedarray-to-string Bun implements the Web-standard [`TextDecoder`](https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder) class for converting from binary data types like `Uint8Array` and strings. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const arr = new Uint8Array([104, 101, 108, 108, 111]); const decoder = new TextDecoder(); const str = decoder.decode(arr); // => "hello" ``` *** See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun. # Deploy a Bun application on Railway Source: https://bun.com/docs/guides/deployment/railway Deploy Bun applications to Railway with this step-by-step guide covering CLI and dashboard methods, optional PostgreSQL setup, and automatic SSL configuration. Railway is an infrastructure platform where you can provision infrastructure, develop with that infrastructure locally, and then deploy to the cloud. It enables instant deployments from GitHub with zero configuration, automatic SSL, and built-in database provisioning. This guide walks through deploying a Bun application with a PostgreSQL database (optional), which is exactly what the template below provides. You can either follow this guide step-by-step or simply deploy the pre-configured template with one click: Deploy on Railway *** **Prerequisites**: * A Bun application ready for deployment * A [Railway account](https://railway.app/) * Railway CLI (for CLI deployment method) * A GitHub account (for Dashboard deployment method) *** ## Method 1: Deploy via CLI Ensure sure you have the Railway CLI installed. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun install -g @railway/cli ``` Log into your Railway account. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} railway login ``` After successfully authenticating, initialize a new project. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} railway init ``` After initializing the project, add a new database and service. Step 4 is only necessary if your application uses a database. If you don't need PostgreSQL, skip to Step 5. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} # Add PostgreSQL database. Make sure to add this first! railway add --database postgres # Add your application service. railway add --service bun-react-db --variables DATABASE_URL=\${{Postgres.DATABASE_URL}} ``` After the services have been created and connected, deploy the application to Railway. By default, services are only accessible within Railway's private network. To make your app publicly accessible, you need to generate a public domain. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} # Deploy your application railway up # Generate public domain railway domain ``` Your app is now live! Railway auto-deploys on every GitHub push. *** ## Method 2: Deploy via Dashboard Create a new project 1. Go to [Railway Dashboard](http://railway.com/dashboard?utm_medium=integration\&utm_source=docs\&utm_campaign=bun) 2. Click **"+ New"** → **"GitHub repo"** 3. Choose your repository Add a PostgreSQL database, and connect this database to the service Step 2 is only necessary if your application uses a database. If you don't need PostgreSQL, skip to Step 3. 1. Click **"+ New"** → **"Database"** → **"Add PostgreSQL"** 2. After the database has been created, select your service (not the database) 3. Go to **"Variables"** tab 4. Click **"+ New Variable"** → **"Add Reference"** 5. Select `DATABASE_URL` from postgres Generate a public domain 1. Select your service 2. Go to **"Settings"** tab 3. Under **"Networking"**, click **"Generate Domain"** Your app is now live! Railway auto-deploys on every GitHub push. *** ## Configuration (Optional) By default, Railway uses [Nixpacks](https://docs.railway.com/guides/build-configuration#nixpacks-options) to automatically detect and build your Bun application with zero configuration. However, using the [Railpack](https://docs.railway.com/guides/build-configuration#railpack) application builder provides better Bun support, and will always support the latest version of Bun. The pre-configured templates use Railpack by default. To enable Railpack in a custom project, add the following to your `railway.json`: ```json railway.json icon="file-json" theme={"theme":{"light":"github-light","dark":"dracula"}} { "$schema": "https://railway.com/railway.schema.json", "build": { "builder": "RAILPACK" } } ``` For more build configuration settings, check out the [Railway documentation](https://docs.railway.com/guides/build-configuration). # Deploy a Bun application on Render Source: https://bun.com/docs/guides/deployment/render [Render](https://render.com/) is a cloud platform that lets you flexibly build, deploy, and scale your apps. It offers features like auto deploys from GitHub, a global CDN, private networks, automatic HTTPS setup, and managed PostgreSQL and Redis. Render supports Bun natively. You can deploy Bun apps as web services, background workers, cron jobs, and more. *** As an example, let's deploy a simple Express HTTP server to Render. Create a new GitHub repo named `myapp`. Git clone it locally. ```sh theme={"theme":{"light":"github-light","dark":"dracula"}} git clone git@github.com:my-github-username/myapp.git cd myapp ``` Add the Express library. ```sh theme={"theme":{"light":"github-light","dark":"dracula"}} bun add express ``` Define a simple server with Express: ```ts app.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import express from "express"; const app = express(); const port = process.env.PORT || 3001; app.get("/", (req, res) => { res.send("Hello World!"); }); app.listen(port, () => { console.log(`Listening on port ${port}...`); }); ``` Commit your changes and push to GitHub. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} git add app.ts bun.lock package.json git commit -m "Create simple Express app" git push origin main ``` In your [Render Dashboard](https://dashboard.render.com/), click `New` > `Web Service` and connect your `myapp` repo. In the Render UI, provide the following values during web service creation: | | | | ----------------- | ------------- | | **Runtime** | `Node` | | **Build Command** | `bun install` | | **Start Command** | `bun app.ts` | That's it! Your web service will be live at its assigned `onrender.com` URL as soon as the build finishes. You can view the [deploy logs](https://docs.render.com/logging#logs-for-an-individual-deploy-or-job) for details. Refer to [Render's documentation](https://docs.render.com/deploys) for a complete overview of deploying on Render. # Deploy a Bun application on Vercel Source: https://bun.com/docs/guides/deployment/vercel [Vercel](https://vercel.com/) is a cloud platform that lets you build, deploy, and scale your apps. The Bun runtime is in Beta; certain features (e.g., automatic source maps, byte-code caching, metrics on `node:http/https`) are not yet supported. `Bun.serve` is currently not supported on Vercel Functions. Use Bun with frameworks supported by Vercel, like Next.js, Express, Hono, or Nitro. *** To enable the Bun runtime for your Functions, add a `bunVersion` field in your `vercel.json` file: ```json vercel.json icon="file-json" theme={"theme":{"light":"github-light","dark":"dracula"}} { "bunVersion": "1.x" // [!code ++] } ``` Vercel automatically detects this configuration and runs your application on Bun. The value has to be `"1.x"`, Vercel handles the minor version internally. For best results, match your local Bun version with the version used by Vercel. **Currently, Bun version `1.2.23` is supported**. If you’re deploying a **Next.js** project (including ISR), update your `package.json` scripts to use the Bun runtime: ```json package.json icon="file-json" theme={"theme":{"light":"github-light","dark":"dracula"}} { "scripts": { "dev": "bun --bun next dev", // [!code ++] "build": "bun --bun next build" // [!code ++] } } ``` The `--bun` flag runs the Next.js CLI under Bun. Bundling (via Turbopack or Webpack) remains unchanged, but all commands execute within the Bun runtime. This ensures both local development and builds use Bun. Connect your repository to Vercel, or deploy from the CLI: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} # Using bunx (no global install) bunx vercel login bunx vercel deploy ``` Or install the Vercel CLI globally: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun i -g vercel vercel login vercel deploy ``` [Learn more in the Vercel Deploy CLI documentation →](https://vercel.com/docs/cli/deploy) To confirm your deployment uses Bun, log the Bun version: ```ts index.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} console.log("runtime", process.versions.bun); ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} runtime 1.2.23 ``` [See the Vercel Bun Runtime documentation for feature support →](https://vercel.com/docs/functions/runtimes/bun#feature-support) *** * [Fluid compute](https://vercel.com/docs/fluid-compute): Both Bun and Node.js runtimes run on Fluid compute and support the same core Vercel Functions features. * [Middleware](https://vercel.com/docs/routing-middleware): To run Routing Middleware with Bun, set the runtime to `nodejs`: ```ts middleware.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} export const config = { runtime: "nodejs" }; // [!code ++] ``` # Build an app with Astro and Bun Source: https://bun.com/docs/guides/ecosystem/astro Initialize a fresh Astro app with `bun create astro`. The `create-astro` package detects when you are using `bunx` and will automatically install dependencies using `bun`. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun create astro ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} ╭─────╮ Houston: │ ◠ ◡ ◠ We're glad to have you on board. ╰─────╯ astro v3.1.4 Launch sequence initiated. dir Where should we create your new project? ./fumbling-field tmpl How would you like to start your new project? Use blog template ✔ Template copied deps Install dependencies? Yes ✔ Dependencies installed ts Do you plan to write TypeScript? Yes use How strict should TypeScript be? Strict ✔ TypeScript customized git Initialize a new git repository? Yes ✔ Git initialized next Liftoff confirmed. Explore your project! Enter your project directory using cd ./fumbling-field Run `bun run dev` to start the dev server. CTRL+C to stop. Add frameworks like react or tailwind using astro add. Stuck? Join us at https://astro.build/chat ╭─────╮ Houston: │ ◠ ◡ ◠ Good luck out there, astronaut! 🚀 ╰─────╯ ``` *** Start the dev server with `bunx`. By default, Bun will run the dev server with Node.js. To use the Bun runtime instead, use the `--bun` flag. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bunx --bun astro dev ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} 🚀 astro v3.1.4 started in 200ms ┃ Local http://localhost:4321/ ┃ Network use --host to expose ``` *** Open [http://localhost:4321](http://localhost:4321) with your browser to see the result. Astro will hot-reload your app as you edit your source files. *** Refer to the [Astro docs](https://docs.astro.build/en/getting-started/) for complete documentation. # Create a Discord bot Source: https://bun.com/docs/guides/ecosystem/discordjs Discord.js works out of the box with Bun. Let's write a simple bot. First create a directory and initialize it with `bun init`. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} mkdir my-bot cd my-bot bun init ``` *** Now install Discord.js. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun add discord.js ``` *** Before we go further, we need to go to the [Discord developer portal](https://discord.com/developers/applications), login/signup, create a new *Application*, then create a new *Bot* within that application. Follow the [official guide](https://discordjs.guide/preparations/setting-up-a-bot-application.html#creating-your-bot) for step-by-step instructions. *** Once complete, you'll be presented with your bot's *private key*. Let's add this to a file called `.env.local`. Bun automatically reads this file and loads it into `process.env`. This is an example token that has already been invalidated. ```txt .env.local icon="settings" theme={"theme":{"light":"github-light","dark":"dracula"}} DISCORD_TOKEN=NzkyNzE1NDU0MTk2MDg4ODQy.X-hvzA.Ovy4MCQywSkoMRRclStW4xAYK7I ``` *** Be sure to add `.env.local` to your `.gitignore`! It is dangerous to check your bot's private key into version control. ```txt .gitignore icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} node_modules .env.local ``` *** Now let's actually write our bot in a new file called `bot.ts`. ```ts bot.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} // import discord.js import { Client, Events, GatewayIntentBits } from "discord.js"; // create a new Client instance const client = new Client({ intents: [GatewayIntentBits.Guilds] }); // listen for the client to be ready client.once(Events.ClientReady, c => { console.log(`Ready! Logged in as ${c.user.tag}`); }); // login with the token from .env.local client.login(process.env.DISCORD_TOKEN); ``` *** Now we can run our bot with `bun run`. It may take a several seconds for the client to initialize the first time you run the file. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun run bot.ts ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} Ready! Logged in as my-bot#1234 ``` *** You're up and running with a bare-bones Discord.js bot! This is a basic guide to setting up your bot with Bun; we recommend the [official discord.js docs](https://discordjs.guide/) for complete information on the `discord.js` API. # Containerize a Bun application with Docker Source: https://bun.com/docs/guides/ecosystem/docker This guide assumes you already have [Docker Desktop](https://www.docker.com/products/docker-desktop/) installed. [Docker](https://www.docker.com) is a platform for packaging and running an application as a lightweight, portable *container* that encapsulates all the necessary dependencies. *** To *containerize* our application, we define a `Dockerfile`. This file contains a list of instructions to initialize the container, copy our local project files into it, install dependencies, and starts the application. ```docker Dockerfile icon="docker" theme={"theme":{"light":"github-light","dark":"dracula"}} # use the official Bun image # see all versions at https://hub.docker.com/r/oven/bun/tags FROM oven/bun:1 AS base WORKDIR /usr/src/app # install dependencies into temp directory # this will cache them and speed up future builds FROM base AS install RUN mkdir -p /temp/dev COPY package.json bun.lock /temp/dev/ RUN cd /temp/dev && bun install --frozen-lockfile # install with --production (exclude devDependencies) RUN mkdir -p /temp/prod COPY package.json bun.lock /temp/prod/ RUN cd /temp/prod && bun install --frozen-lockfile --production # copy node_modules from temp directory # then copy all (non-ignored) project files into the image FROM base AS prerelease COPY --from=install /temp/dev/node_modules node_modules COPY . . # [optional] tests & build ENV NODE_ENV=production RUN bun test RUN bun run build # copy production dependencies and source code into final image FROM base AS release COPY --from=install /temp/prod/node_modules node_modules COPY --from=prerelease /usr/src/app/index.ts . COPY --from=prerelease /usr/src/app/package.json . # run the app USER bun EXPOSE 3000/tcp ENTRYPOINT [ "bun", "run", "index.ts" ] ``` *** Now that you have your docker image, let's look at `.dockerignore` which has the same syntax as `.gitignore`, here you need to specify the files/directories that must not go in any stage of the docker build. An example for a ignore file is ```txt .dockerignore icon="docker" theme={"theme":{"light":"github-light","dark":"dracula"}} node_modules Dockerfile* docker-compose* .dockerignore .git .gitignore README.md LICENSE .vscode Makefile helm-charts .env .editorconfig .idea coverage* ``` *** We'll now use `docker build` to convert this `Dockerfile` into a *Docker image*, a self-contained template containing all the dependencies and configuration required to run the application. The `-t` flag lets us specify a name for the image, and `--pull` tells Docker to automatically download the latest version of the base image (`oven/bun`). The initial build will take longer, as Docker will download all the base images and dependencies. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} docker build --pull -t bun-hello-world . ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} [+] Building 0.9s (21/21) FINISHED => [internal] load build definition from Dockerfile 0.0s => => transferring dockerfile: 37B 0.0s => [internal] load .dockerignore 0.0s => => transferring context: 35B 0.0s => [internal] load metadata for docker.io/oven/bun:1 0.8s => [auth] oven/bun:pull token for registry-1.docker.io 0.0s => [base 1/2] FROM docker.io/oven/bun:1@sha256:373265748d3cd3624cb3f3ee6004f45b1fc3edbd07a622aeeec17566d2756997 0.0s => [internal] load build context 0.0s => => transferring context: 155B 0.0s # ...lots of commands... => exporting to image 0.0s => => exporting layers 0.0s => => writing image sha256:360663f7fdcd6f11e8e94761d5592e2e4dfc8d167f034f15cd5a863d5dc093c4 0.0s => => naming to docker.io/library/bun-hello-world 0.0s ``` *** We've built a new *Docker image*. Now let's use that image to spin up an actual, running *container*. We'll use `docker run` to start a new container using the `bun-hello-world` image. It will be run in *detached* mode (`-d`) and we'll map the container's port 3000 to our local machine's port 3000 (`-p 3000:3000`). The `run` command prints a string representing the *container ID*. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} docker run -d -p 3000:3000 bun-hello-world ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} 7f03e212a15ede8644379bce11a13589f563d3909a9640446c5bbefce993678d ``` *** The container is now running in the background. Visit [localhost:3000](http://localhost:3000). You should see a `Hello, World!` message. *** To stop the container, we'll use `docker stop `. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} docker stop 7f03e212a15ede8644379bce11a13589f563d3909a9640446c5bbefce993678d ``` *** If you can't find the container ID, you can use `docker ps` to list all running containers. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} docker ps ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 7f03e212a15e bun-hello-world "bun run index.ts" 2 minutes ago Up 2 minutes 0.0.0.0:3000->3000/tcp flamboyant_cerf ``` *** That's it! Refer to the [Docker documentation](https://docs.docker.com/) for more advanced usage. # Use Drizzle ORM with Bun Source: https://bun.com/docs/guides/ecosystem/drizzle Drizzle is an ORM that supports both a SQL-like "query builder" API and an ORM-like [Queries API](https://orm.drizzle.team/docs/rqb). It supports the `bun:sqlite` built-in module. *** Let's get started by creating a fresh project with `bun init` and installing Drizzle. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun init -y bun add drizzle-orm bun add -D drizzle-kit ``` *** Then we'll connect to a SQLite database using the `bun:sqlite` module and create the Drizzle database instance. ```ts db.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import { drizzle } from "drizzle-orm/bun-sqlite"; import { Database } from "bun:sqlite"; const sqlite = new Database("sqlite.db"); export const db = drizzle(sqlite); ``` *** To see the database in action, add these lines to `index.ts`. ```ts index.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import { db } from "./db"; import { sql } from "drizzle-orm"; const query = sql`select "hello world" as text`; const result = db.get<{ text: string }>(query); console.log(result); ``` *** Then run `index.ts` with Bun. Bun will automatically create `sqlite.db` and execute the query. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun run index.ts ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} { text: "hello world" } ``` *** Lets give our database a proper schema. Create a `schema.ts` file and define a `movies` table. ```ts schema.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core"; export const movies = sqliteTable("movies", { id: integer("id").primaryKey(), title: text("name"), releaseYear: integer("release_year"), }); ``` *** We can use the `drizzle-kit` CLI to generate an initial SQL migration. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bunx drizzle-kit generate --dialect sqlite --schema ./schema.ts ``` *** This creates a new `drizzle` directory containing a `.sql` migration file and `meta` directory. ```txt File Tree icon="folder-tree" theme={"theme":{"light":"github-light","dark":"dracula"}} drizzle ├── 0000_ordinary_beyonder.sql └── meta ├── 0000_snapshot.json └── _journal.json ``` *** We can execute these migrations with a simple `migrate.ts` script. This script creates a new connection to a SQLite database that writes to `sqlite.db`, then executes all unexecuted migrations in the `drizzle` directory. ```ts migrate.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import { migrate } from "drizzle-orm/bun-sqlite/migrator"; import { drizzle } from "drizzle-orm/bun-sqlite"; import { Database } from "bun:sqlite"; const sqlite = new Database("sqlite.db"); const db = drizzle(sqlite); migrate(db, { migrationsFolder: "./drizzle" }); ``` *** We can run this script with `bun` to execute the migration. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun run migrate.ts ``` *** Now that we have a database, let's add some data to it. Create a `seed.ts` file with the following contents. ```ts seed.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import { db } from "./db"; import * as schema from "./schema"; await db.insert(schema.movies).values([ { title: "The Matrix", releaseYear: 1999, }, { title: "The Matrix Reloaded", releaseYear: 2003, }, { title: "The Matrix Revolutions", releaseYear: 2003, }, ]); console.log(`Seeding complete.`); ``` *** Then run this file. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun run seed.ts ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} Seeding complete. ``` *** We finally have a database with a schema and some sample data. Let's use Drizzle to query it. Replace the contents of `index.ts` with the following. ```ts index.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import * as schema from "./schema"; import { db } from "./db"; const result = await db.select().from(schema.movies); console.log(result); ``` *** Then run the file. You should see the three movies we inserted. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun run index.ts ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} [ { id: 1, title: "The Matrix", releaseYear: 1999 }, { id: 2, title: "The Matrix Reloaded", releaseYear: 2003 }, { id: 3, title: "The Matrix Revolutions", releaseYear: 2003 } ] ``` *** Refer to the [Drizzle website](https://orm.drizzle.team/docs/overview) for complete documentation. # Use EdgeDB with Bun Source: https://bun.com/docs/guides/ecosystem/edgedb EdgeDB is a graph-relational database powered by Postgres under the hood. It provides a declarative schema language, migrations system, and object-oriented query language, in addition to supporting raw SQL queries. It solves the object-relational mapping problem at the database layer, eliminating the need for an ORM library in your application code. *** First, [install EdgeDB](https://www.edgedb.com/install) if you haven't already. ```sh Linux/macOS terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} curl --proto '=https' --tlsv1.2 -sSf https://sh.edgedb.com | sh ``` ```sh Windows terminal icon="windows" theme={"theme":{"light":"github-light","dark":"dracula"}} iwr https://ps1.edgedb.com -useb | iex ``` *** Use `bun init` to create a fresh project. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} mkdir my-edgedb-app cd my-edgedb-app bun init -y ``` *** We'll use the EdgeDB CLI to initialize an EdgeDB instance for our project. This creates an `edgedb.toml` file in our project root. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} edgedb project init ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} No `edgedb.toml` found in `/Users/colinmcd94/Documents/bun/fun/examples/my-edgedb-app` or above Do you want to initialize a new project? [Y/n] > Y Specify the name of EdgeDB instance to use with this project [default: my_edgedb_app]: > my_edgedb_app Checking EdgeDB versions... Specify the version of EdgeDB to use with this project [default: x.y]: > x.y ┌─────────────────────┬────────────────────────────────────────────────────────────────────────┐ │ Project directory │ /Users/colinmcd94/Documents/bun/fun/examples/my-edgedb-app │ │ Project config │ /Users/colinmcd94/Documents/bun/fun/examples/my-edgedb-app/edgedb.toml │ │ Schema dir (empty) │ /Users/colinmcd94/Documents/bun/fun/examples/my-edgedb-app/dbschema │ │ Installation method │ portable package │ │ Version │ x.y+6d5921b │ │ Instance name │ my_edgedb_app │ └─────────────────────┴────────────────────────────────────────────────────────────────────────┘ Version x.y+6d5921b is already downloaded Initializing EdgeDB instance... Applying migrations... Everything is up to date. Revision initial Project initialized. To connect to my_edgedb_app, run `edgedb` ``` *** To see if the database is running, let's open a REPL and run a simple query. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} edgedb edgedb> select 1 + 1; ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} 2 ``` Then run `\quit` to exit the REPL. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} edgedb> \quit ``` *** With the project initialized, we can define a schema. The `edgedb project init` command already created a `dbschema/default.esdl` file to contain our schema. ```txt File Tree icon="folder-tree" theme={"theme":{"light":"github-light","dark":"dracula"}} dbschema ├── default.esdl └── migrations ``` *** Open that file and paste the following contents. ```ts default.esdl icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} module default { type Movie { required title: str; releaseYear: int64; } }; ``` *** Then generate and apply an initial migration. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} edgedb migration create ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} Created /Users/colinmcd94/Documents/bun/fun/examples/my-edgedb-app/dbschema/migrations/00001.edgeql, id: m1uwekrn4ni4qs7ul7hfar4xemm5kkxlpswolcoyqj3xdhweomwjrq ``` ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} edgedb migrate ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} Applied m1uwekrn4ni4qs7ul7hfar4xemm5kkxlpswolcoyqj3xdhweomwjrq (00001.edgeql) ``` *** With our schema applied, let's execute some queries using EdgeDB's JavaScript client library. We'll install the client library and EdgeDB's codegen CLI, and create a `seed.ts`.file. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun add edgedb bun add -D @edgedb/generate touch seed.ts ``` *** Paste the following code into `seed.ts`. The client auto-connects to the database. We insert a couple movies using the `.execute()` method. We will use EdgeQL's `for` expression to turn this bulk insert into a single optimized query. ```ts seed.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import { createClient } from "edgedb"; const client = createClient(); const INSERT_MOVIE = ` with movies := >>$movies for movie in array_unpack(movies) union ( insert Movie { title := movie.title, releaseYear := movie.year, } ) `; const movies = [ { title: "The Matrix", year: 1999 }, { title: "The Matrix Reloaded", year: 2003 }, { title: "The Matrix Revolutions", year: 2003 }, ]; await client.execute(INSERT_MOVIE, { movies }); console.log(`Seeding complete.`); process.exit(); ``` *** Then run this file with Bun. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun run seed.ts ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} Seeding complete. ``` *** EdgeDB implements a number of code generation tools for TypeScript. To query our newly seeded database in a typesafe way, we'll use `@edgedb/generate` to code-generate the EdgeQL query builder. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bunx @edgedb/generate edgeql-js ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} Generating query builder... Detected tsconfig.json, generating TypeScript files. To override this, use the --target flag. Run `npx @edgedb/generate --help` for full options. Introspecting database schema... Writing files to ./dbschema/edgeql-js Generation complete! 🤘 Checking the generated query builder into version control is not recommended. Would you like to update .gitignore to ignore the query builder directory? The following line will be added: dbschema/edgeql-js [y/n] (leave blank for "y") > y ``` *** In `index.ts`, we can import the generated query builder from `./dbschema/edgeql-js` and write a simple select query. ```ts index.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import { createClient } from "edgedb"; import e from "./dbschema/edgeql-js"; const client = createClient(); const query = e.select(e.Movie, () => ({ title: true, releaseYear: true, })); const results = await query.run(client); console.log(results); results; // { title: string, releaseYear: number | null }[] ``` *** Running the file with Bun, we can see the list of movies we inserted. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun run index.ts ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} [ { title: "The Matrix", releaseYear: 1999 }, { title: "The Matrix Reloaded", releaseYear: 2003 }, { title: "The Matrix Revolutions", releaseYear: 2003 } ] ``` *** For complete documentation, refer to the [EdgeDB docs](https://www.edgedb.com/docs). # Build an HTTP server using Elysia and Bun Source: https://bun.com/docs/guides/ecosystem/elysia [Elysia](https://elysiajs.com) is a Bun-first performance focused web framework that takes full advantage of Bun's HTTP, file system, and hot reloading APIs. Get started with `bun create`. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun create elysia myapp cd myapp bun run dev ``` *** To define a simple HTTP route and start a server with Elysia: ```ts server.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import { Elysia } from "elysia"; const app = new Elysia().get("/", () => "Hello Elysia").listen(8080); console.log(`🦊 Elysia is running at on port ${app.server?.port}...`); ``` *** Elysia is a full-featured server framework with Express-like syntax, type inference, middleware, file uploads, and plugins for JWT authentication, tRPC, and more. It's also is one of the [fastest Bun web frameworks](https://github.com/SaltyAom/bun-http-framework-benchmark). Refer to the Elysia [documentation](https://elysiajs.com/quick-start.html) for more information. # Build an HTTP server using Express and Bun Source: https://bun.com/docs/guides/ecosystem/express Express and other major Node.js HTTP libraries should work out of the box. Bun implements the [`node:http`](https://nodejs.org/api/http.html) and [`node:https`](https://nodejs.org/api/https.html) modules that these libraries rely on. Refer to the [Runtime > Node.js APIs](https://bun.com/docs/runtime/nodejs-apis#node-http) page for more detailed compatibility information. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun add express ``` *** To define a simple HTTP route and start a server with Express: ```ts server.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import express from "express"; const app = express(); const port = 8080; app.get("/", (req, res) => { res.send("Hello World!"); }); app.listen(port, () => { console.log(`Listening on port ${port}...`); }); ``` *** To start the server on `localhost`: ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun server.ts ``` # Build an HTTP server using Hono and Bun Source: https://bun.com/docs/guides/ecosystem/hono [Hono](https://github.com/honojs/hono) is a lightweight ultrafast web framework designed for the edge. ```ts server.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import { Hono } from "hono"; const app = new Hono(); app.get("/", c => c.text("Hono!")); export default app; ``` *** Use `create-hono` to get started with one of Hono's project templates. Select `bun` when prompted for a template. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun create hono myapp ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} ✔ Which template do you want to use? › bun cloned honojs/starter#main to /path/to/myapp ✔ Copied project files ``` ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} cd myapp bun install ``` *** Then start the dev server and visit [localhost:3000](http://localhost:3000). ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun run dev ``` *** Refer to Hono's guide on [getting started with Bun](https://hono.dev/getting-started/bun) for more information. # Read and write data to MongoDB using Mongoose and Bun Source: https://bun.com/docs/guides/ecosystem/mongoose MongoDB and Mongoose work out of the box with Bun. This guide assumes you've already installed MongoDB and are running it as background process/service on your development machine. Follow [this guide](https://www.mongodb.com/docs/manual/installation/) for details. *** Once MongoDB is running, create a directory and initialize it with `bun init`. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} mkdir mongoose-app cd mongoose-app bun init ``` *** Then add Mongoose as a dependency. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun add mongoose ``` *** In `schema.ts` we'll declare and export a simple `Animal` model. ```ts schema.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import * as mongoose from "mongoose"; const animalSchema = new mongoose.Schema( { title: { type: String, required: true }, sound: { type: String, required: true }, }, { methods: { speak() { console.log(`${this.sound}!`); }, }, }, ); export type Animal = mongoose.InferSchemaType; export const Animal = mongoose.model("Animal", animalSchema); ``` *** Now from `index.ts` we can import `Animal`, connect to MongoDB, and add some data to our database. ```ts index.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import * as mongoose from "mongoose"; import { Animal } from "./schema"; // connect to database await mongoose.connect("mongodb://127.0.0.1:27017/mongoose-app"); // create new Animal const cow = new Animal({ title: "Cow", sound: "Moo", }); await cow.save(); // saves to the database // read all Animals const animals = await Animal.find(); animals[0].speak(); // logs "Moo!" // disconnect await mongoose.disconnect(); ``` *** Let's run this with `bun run`. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun run index.ts ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} Moo! ``` *** This is a simple introduction to using Mongoose with TypeScript and Bun. As you build your application, refer to the official [MongoDB](https://docs.mongodb.com/) and [Mongoose](https://mongoosejs.com/docs/) sites for complete documentation. # Use Neon Postgres through Drizzle ORM Source: https://bun.com/docs/guides/ecosystem/neon-drizzle [Neon](https://neon.tech/) is a fully managed serverless Postgres, separating compute and storage to offer features like autoscaling, branching and bottomless storage. Neon can be used from Bun directly using the `@neondatabase/serverless` driver or through an ORM like `Drizzle`. Drizzle ORM supports both a SQL-like "query builder" API and an ORM-like [Queries API](https://orm.drizzle.team/docs/rqb). Get started by creating a project directory, initializing the directory using `bun init`, and installing Drizzle and the [Neon serverless driver](https://github.com/neondatabase/serverless/). ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} mkdir bun-drizzle-neon cd bun-drizzle-neon bun init -y bun add drizzle-orm @neondatabase/serverless bun add -D drizzle-kit ``` *** Create a `.env.local` file and add your [Neon Postgres connection string](https://neon.tech/docs/connect/connect-from-any-app) to it. ```txt .env.local icon="settings" theme={"theme":{"light":"github-light","dark":"dracula"}} DATABASE_URL=postgresql://usertitle:password@ep-adj-noun-guid.us-east-1.aws.neon.tech/neondb?sslmode=require ``` *** We will connect to the Neon database using the Neon serverless driver, wrapped in a Drizzle database instance. ```ts db.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import { neon } from "@neondatabase/serverless"; import { drizzle } from "drizzle-orm/neon-http"; // Bun automatically loads the DATABASE_URL from .env.local // Refer to: https://bun.com/docs/runtime/env for more information const sql = neon(process.env.DATABASE_URL!); export const db = drizzle(sql); ``` *** To see the database in action, add these lines to `index.ts`. ```ts index.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import { db } from "./db"; import { sql } from "drizzle-orm"; const query = sql`select 'hello world' as text`; const result = await db.execute(query); console.log(result.rows); ``` *** Then run `index.ts` with Bun. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun run index.ts ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} [ { text: "hello world", } ] ``` *** We can define a schema for our database using Drizzle ORM primitives. Create a `schema.ts` file and add this code. ```ts schema.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import { pgTable, integer, serial, text, timestamp } from "drizzle-orm/pg-core"; export const authors = pgTable("authors", { id: serial("id").primaryKey(), title: text("name").notNull(), bio: text("bio"), createdAt: timestamp("created_at").notNull().defaultNow(), }); ``` *** We then use the `drizzle-kit` CLI to generate an initial SQL migration. ```sh theme={"theme":{"light":"github-light","dark":"dracula"}} bunx drizzle-kit generate --dialect postgresql --schema ./schema.ts --out ./drizzle ``` *** This creates a new `drizzle` directory containing a `.sql` migration file and `meta` directory. ```txt File Tree icon="folder-tree" theme={"theme":{"light":"github-light","dark":"dracula"}} drizzle ├── 0000_aspiring_post.sql └── meta ├── 0000_snapshot.json └── _journal.json ``` *** We can execute these migrations with a simple `migrate.ts` script. This script creates a new connection to the Neon database and executes all unexecuted migrations in the `drizzle` directory. ```ts migrate.ts theme={"theme":{"light":"github-light","dark":"dracula"}} import { db } from "./db"; import { migrate } from "drizzle-orm/neon-http/migrator"; const main = async () => { try { await migrate(db, { migrationsFolder: "drizzle" }); console.log("Migration completed"); } catch (error) { console.error("Error during migration:", error); process.exit(1); } }; main(); ``` *** We can run this script with `bun` to execute the migration. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun run migrate.ts ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} Migration completed ``` *** We can now add some data to our database. Create a `seed.ts` file with the following contents. ```ts seed.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import { db } from "./db"; import * as schema from "./schema"; async function seed() { await db.insert(schema.authors).values([ { title: "J.R.R. Tolkien", bio: "The creator of Middle-earth and author of The Lord of the Rings.", }, { title: "George R.R. Martin", bio: "The author of the epic fantasy series A Song of Ice and Fire.", }, { title: "J.K. Rowling", bio: "The creator of the Harry Potter series.", }, ]); } async function main() { try { await seed(); console.log("Seeding completed"); } catch (error) { console.error("Error during seeding:", error); process.exit(1); } } main(); ``` *** Then run this file. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun run seed.ts ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} Seeding completed ``` *** We now have a database with a schema and sample data. We can use Drizzle to query it. Replace the contents of `index.ts` with the following. ```ts index.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import * as schema from "./schema"; import { db } from "./db"; const result = await db.select().from(schema.authors); console.log(result); ``` *** Then run the file. You should see the three authors we inserted. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun run index.ts ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} [ { id: 1, title: "J.R.R. Tolkien", bio: "The creator of Middle-earth and author of The Lord of the Rings.", createdAt: 2024-05-11T10:28:46.029Z, }, { id: 2, title: "George R.R. Martin", bio: "The author of the epic fantasy series A Song of Ice and Fire.", createdAt: 2024-05-11T10:28:46.029Z, }, { id: 3, title: "J.K. Rowling", bio: "The creator of the Harry Potter series.", createdAt: 2024-05-11T10:28:46.029Z, } ] ``` *** This example used the Neon serverless driver's SQL-over-HTTP functionality. Neon's serverless driver also exposes `Client` and `Pool` constructors to enable sessions, interactive transactions, and node-postgres compatibility. Refer to [Neon's documentation](https://neon.tech/docs/serverless/serverless-driver) for a complete overview. Refer to the [Drizzle website](https://orm.drizzle.team/docs/overview) for more documentation on using the Drizzle ORM. # Use Neon's Serverless Postgres with Bun Source: https://bun.com/docs/guides/ecosystem/neon-serverless-postgres [Neon](https://neon.tech/) is a fully managed serverless Postgres. Neon separates compute and storage to offer modern developer features such as autoscaling, branching, bottomless storage, and more. *** Get started by creating a project directory, initializing the directory using `bun init`, and adding the [Neon serverless driver](https://github.com/neondatabase/serverless/) as a project dependency. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} mkdir bun-neon-postgres cd bun-neon-postgres bun init -y bun add @neondatabase/serverless ``` *** Create a `.env.local` file and add your [Neon Postgres connection string](https://neon.tech/docs/connect/connect-from-any-app) to it. ```sh .env.local icon="settings" theme={"theme":{"light":"github-light","dark":"dracula"}} DATABASE_URL=postgresql://usertitle:password@ep-adj-noun-guid.us-east-1.aws.neon.tech/neondb?sslmode=require ``` *** Paste the following code into your project's `index.ts` file. ```ts index.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import { neon } from "@neondatabase/serverless"; // Bun automatically loads the DATABASE_URL from .env.local // Refer to: https://bun.com/docs/runtime/env for more information const sql = neon(process.env.DATABASE_URL); const rows = await sql`SELECT version()`; console.log(rows[0].version); ``` *** Start the program using `bun ./index.ts`. The Postgres version should be printed to the console. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun ./index.ts ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} PostgreSQL 16.2 on x86_64-pc-linux-gnu, compiled by gcc (Debian 10.2.1-6) 10.2.1 20210110, 64-bit ``` *** This example used the Neon serverless driver's SQL-over-HTTP functionality. Neon's serverless driver also exposes `Client` and `Pool` constructors to enable sessions, interactive transactions, and node-postgres compatibility. Refer to [Neon's documentation](https://neon.tech/docs/serverless/serverless-driver) for a complete overview of the serverless driver. # Build an app with Next.js and Bun Source: https://bun.com/docs/guides/ecosystem/nextjs Initialize a Next.js app with `create-next-app`. This will scaffold a new Next.js project and automatically install dependencies. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun create next-app ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} ✔ What is your project named? … my-app ✔ Would you like to use TypeScript with this project? … No / Yes ✔ Would you like to use ESLint with this project? … No / Yes ✔ Would you like to use Tailwind CSS? ... No / Yes ✔ Would you like to use `src/` directory with this project? … No / Yes ✔ Would you like to use App Router? (recommended) ... No / Yes ✔ What import alias would you like configured? … @/* Creating a new Next.js app in /path/to/my-app. ``` *** You can specify a starter template using the `--example` flag. ```sh theme={"theme":{"light":"github-light","dark":"dracula"}} bun create next-app --example with-supabase ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} ✔ What is your project named? … my-app ... ``` *** To start the dev server with Bun, run `bun --bun run dev` from the project root. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} cd my-app bun --bun run dev ``` *** To run the dev server with Node.js instead, omit `--bun`. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} cd my-app bun run dev ``` *** Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. Any changes you make to `(pages/app)/index.tsx` will be hot-reloaded in the browser. # Build an app with Nuxt and Bun Source: https://bun.com/docs/guides/ecosystem/nuxt Bun supports [Nuxt](https://nuxt.com) out of the box. Initialize a Nuxt app with official `nuxi` CLI. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bunx nuxi init my-nuxt-app ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} ✔ Which package manager would you like to use? bun ◐ Installing dependencies... bun install v1.3.1 (16b4bf34) + @nuxt/devtools@0.8.2 + nuxt@3.7.0 785 packages installed [2.67s] ✔ Installation completed. ✔ Types generated in .nuxt ✨ Nuxt project has been created with the v3 template. Next steps: › cd my-nuxt-app › Start development server with bun run dev ``` *** To start the dev server, run `bun --bun run dev` from the project root. This will execute the `nuxt dev` command (as defined in the `"dev"` script in `package.json`). The `nuxt` CLI uses Node.js by default; passing the `--bun` flag forces the dev server to use the Bun runtime instead. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} cd my-nuxt-app bun --bun run dev ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} nuxt dev Nuxi 3.6.5 Nuxt 3.6.5 with Nitro 2.5.2 > Local: http://localhost:3000/ > Network: http://192.168.0.21:3000/ > Network: http://[fd8a:d31d:481c:4883:1c64:3d90:9f83:d8a2]:3000/ ✔ Nuxt DevTools is enabled v0.8.0 (experimental) ℹ Vite client warmed up in 547ms ✔ Nitro built in 244 ms ``` *** Once the dev server spins up, open [http://localhost:3000](http://localhost:3000) to see the app. The app will render Nuxt's built-in `NuxtWelcome` template component. To start developing your app, replace `` in `app.vue` with your own UI. ![Demo Nuxt app running on localhost](https://github.com/oven-sh/bun/assets/3084745/2c683ecc-3298-4bb0-b8c0-cf4cfaea1daa) *** For production build, while the default preset is already compatible with Bun, you can also use [Bun preset](https://nitro.build/deploy/runtimes/bun) to generate better optimized builds. ```ts nuxt.config.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} export default defineNuxtConfig({ nitro: { preset: "bun", // [!code ++] }, }); ``` Some packages provide Bun-specific exports that Nitro will not bundle correctly using the default preset. In this case, you need to use Bun preset so that the packages will work correctly in production builds. After building with bun, run: ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun run ./.output/server/index.mjs ``` *** Refer to the [Nuxt website](https://nuxt.com/docs) for complete documentation. # Run Bun as a daemon with PM2 Source: https://bun.com/docs/guides/ecosystem/pm2 [PM2](https://pm2.keymetrics.io/) is a popular process manager that manages and runs your applications as daemons (background processes). It offers features like process monitoring, automatic restarts, and easy scaling. Using a process manager is common when deploying a Bun application on a cloud-hosted virtual private server (VPS), as it: * Keeps your Node.js application running continuously. * Ensure high availability and reliability of your application. * Monitor and manage multiple processes with ease. * Simplify the deployment process. *** You can use PM2 with Bun in two ways: as a CLI option or in a configuration file. ### With `--interpreter` To start your application with PM2 and Bun as the interpreter, open your terminal and run the following command: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} pm2 start --interpreter ~/.bun/bin/bun index.ts ``` *** ### With a configuration file Alternatively, you can create a PM2 configuration file. Create a file named `pm2.config.js` in your project directory and add the following content. ```js pm2.config.js icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} module.exports = { title: "app", // Name of your application script: "index.ts", // Entry point of your application interpreter: "bun", // Bun interpreter env: { PATH: `${process.env.HOME}/.bun/bin:${process.env.PATH}`, // Add "~/.bun/bin/bun" to PATH }, }; ``` *** After saving the file, you can start your application with PM2 ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} pm2 start pm2.config.js ``` *** That’s it! Your JavaScript/TypeScript web server is now running as a daemon with PM2 using Bun as the interpreter. # Use Prisma with Bun Source: https://bun.com/docs/guides/ecosystem/prisma **Note** — Prisma's dynamic subcommand loading system currently requires npm to be installed alongside Bun. This affects certain CLI commands like `prisma init`, `prisma migrate`, etc. Generated code works perfectly with Bun using the new `prisma-client` generator. Prisma works out of the box with Bun. First, create a directory and initialize it with `bun init`. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} mkdir prisma-app cd prisma-app bun init ``` Then install the Prisma CLI (`prisma`), Prisma Client (`@prisma/client`), and the LibSQL adapter as dependencies. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun add -d prisma bun add @prisma/client @prisma/adapter-libsql ``` We'll use the Prisma CLI with `bunx` to initialize our schema and migration directory. For simplicity we'll be using an in-memory SQLite database. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bunx --bun prisma init --datasource-provider sqlite ``` This creates a basic schema. We need to update it to use the new Rust-free client with Bun optimization. Open `prisma/schema.prisma` and modify the generator block, then add a simple `User` model. ```prisma prisma/schema.prisma icon="https://mintcdn.com/bun-1dd33a4e/ztkOKlOzC-ndb59O/icons/ecosystem/prisma.svg?fit=max&auto=format&n=ztkOKlOzC-ndb59O&q=85&s=e11053a2c03cc3eb38539358a21b28c9" theme={"theme":{"light":"github-light","dark":"dracula"}} generator client { provider = "prisma-client" // [!code ++] output = "./generated" // [!code ++] engineType = "client" // [!code ++] runtime = "bun" // [!code ++] } datasource db { provider = "sqlite" url = env("DATABASE_URL") } model User { // [!code ++] id Int @id @default(autoincrement()) // [!code ++] email String @unique // [!code ++] name String? // [!code ++] } // [!code ++] ``` Then generate and run initial migration. This will generate a `.sql` migration file in `prisma/migrations`, create a new SQLite instance, and execute the migration against the new instance. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bunx --bun prisma migrate dev --name init ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} Environment variables loaded from .env Prisma schema loaded from prisma/schema.prisma Datasource "db": SQLite database "dev.db" at "file:./dev.db" SQLite database dev.db created at file:./dev.db Applying migration `20251014141233_init` The following migration(s) have been created and applied from new schema changes: prisma/migrations/ └─ 20251014141233_init/ └─ migration.sql Your database is now in sync with your schema. ✔ Generated Prisma Client (6.17.1) to ./generated in 18ms ``` As indicated in the output, Prisma re-generates our *Prisma client* whenever we execute a new migration. The client provides a fully typed API for reading and writing from our database. You can manually re-generate the client with the Prisma CLI. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bunx --bun prisma generate ``` Now we need to create a Prisma client instance. Create a new file `prisma/db.ts` to initialize the PrismaClient with the LibSQL adapter. ```ts prisma/db.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import { PrismaClient } from "./generated/client"; import { PrismaLibSQL } from "@prisma/adapter-libsql"; const adapter = new PrismaLibSQL({ url: process.env.DATABASE_URL || "" }); export const prisma = new PrismaClient({ adapter }); ``` Let's write a simple script to create a new user, then count the number of users in the database. ```ts index.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import { prisma } from "./prisma/db"; // create a new user await prisma.user.create({ data: { name: "John Dough", email: `john-${Math.random()}@example.com`, }, }); // count the number of users const count = await prisma.user.count(); console.log(`There are ${count} users in the database.`); ``` Let's run this script with `bun run`. Each time we run it, a new user is created. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun run index.ts ``` ```txg theme={"theme":{"light":"github-light","dark":"dracula"}} Created john-0.12802932895402364@example.com There are 1 users in the database. ``` ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun run index.ts ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} Created john-0.8671308799782803@example.com There are 2 users in the database. ``` ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun run index.ts ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} Created john-0.4465968383115295@example.com There are 3 users in the database. ``` *** That's it! Now that you've set up Prisma using Bun, we recommend referring to the [official Prisma docs](https://www.prisma.io/docs/concepts/components/prisma-client) as you continue to develop your application. # Use Prisma Postgres with Bun Source: https://bun.com/docs/guides/ecosystem/prisma-postgres **Note** — At the moment Prisma needs Node.js to be installed to run certain generation code. Make sure Node.js is installed in the environment where you're running `bunx prisma` commands. First, create a directory and initialize it with `bun init`. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} mkdir prisma-postgres-app cd prisma-postgres-app bun init ``` Then install the Prisma CLI (`prisma`), Prisma Client (`@prisma/client`), and the accelerate extension as dependencies. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun add -d prisma bun add @prisma/client @prisma/extension-accelerate ``` We'll use the Prisma CLI with `bunx` to initialize our schema and migration directory. We'll be using PostgreSQL as our database. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bunx --bun prisma init --db ``` This creates a basic schema. We need to update it to use the new Rust-free client with Bun optimization. Open `prisma/schema.prisma` and modify the generator block, then add a simple `User` model. ```prisma prisma/schema.prisma icon="https://mintcdn.com/bun-1dd33a4e/ztkOKlOzC-ndb59O/icons/ecosystem/prisma.svg?fit=max&auto=format&n=ztkOKlOzC-ndb59O&q=85&s=e11053a2c03cc3eb38539358a21b28c9" theme={"theme":{"light":"github-light","dark":"dracula"}} generator client { provider = "prisma-client" output = "./generated" // [!code ++] engineType = "client" // [!code ++] runtime = "bun" // [!code ++] } datasource db { provider = "postgresql" url = env("DATABASE_URL") } model User { // [!code ++] id Int @id @default(autoincrement()) // [!code ++] email String @unique // [!code ++] name String? // [!code ++] } // [!code ++] ``` Set up your Postgres database URL in the `.env` file. ```env .env icon="settings" theme={"theme":{"light":"github-light","dark":"dracula"}} DATABASE_URL="postgresql://username:password@localhost:5432/mydb?schema=public" ``` Then generate and run initial migration. This will generate a `.sql` migration file in `prisma/migrations`, and execute the migration against your Postgres database. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bunx --bun prisma migrate dev --name init ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} Environment variables loaded from .env Prisma schema loaded from prisma/schema.prisma Datasource "db": PostgreSQL database "mydb", schema "public" at "localhost:5432" Applying migration `20250114141233_init` The following migration(s) have been created and applied from new schema changes: prisma/migrations/ └─ 20250114141233_init/ └─ migration.sql Your database is now in sync with your schema. ✔ Generated Prisma Client (6.17.1) to ./generated in 18ms ``` As indicated in the output, Prisma re-generates our *Prisma client* whenever we execute a new migration. The client provides a fully typed API for reading and writing from our database. You can manually re-generate the client with the Prisma CLI. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bunx --bun prisma generate ``` Now we need to create a Prisma client instance. Create a new file `prisma/db.ts` to initialize the PrismaClient with the Postgres adapter. ```ts prisma/db.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import { PrismaClient } from "./generated/client"; import { withAccelerate } from '@prisma/extension-accelerate' export const prisma = new PrismaClient().$extends(withAccelerate()) ``` Let's write a simple script to create a new user, then count the number of users in the database. ```ts index.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import { prisma } from "./prisma/db"; // create a new user await prisma.user.create({ data: { name: "John Dough", email: `john-${Math.random()}@example.com`, }, }); // count the number of users const count = await prisma.user.count(); console.log(`There are ${count} users in the database.`); ``` Let's run this script with `bun run`. Each time we run it, a new user is created. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun run index.ts ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} There are 1 users in the database. ``` ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun run index.ts ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} There are 2 users in the database. ``` ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun run index.ts ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} There are 3 users in the database. ``` *** That's it! Now that you've set up Prisma Postgres using Bun, we recommend referring to the [official Prisma Postgres docs](https://www.prisma.io/docs/postgres) as you continue to develop your application. # Build an app with Qwik and Bun Source: https://bun.com/docs/guides/ecosystem/qwik Initialize a new Qwik app with `bunx create-qwik`. The `create-qwik` package detects when you are using `bunx` and will automatically install dependencies using `bun`. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun create qwik ``` ```txts theme={"theme":{"light":"github-light","dark":"dracula"}} ............ .::: :--------:. .:::: .:-------:. .:::::. .:-------. ::::::. .:------. ::::::. :-----: ::::::. .:-----. :::::::. .-----. ::::::::.. ---:. .:::::::::. :-:. ..:::::::::::: ...:::: ┌ Let's create a Qwik App ✨ (v1.2.10) │ ◇ Where would you like to create your new project? (Use '.' or './' for current directory) │ ./my-app │ ● Creating new project in /path/to/my-app ... 🐇 │ ◇ Select a starter │ Basic App │ ◇ Would you like to install bun dependencies? │ Yes │ ◇ Initialize a new git repository? │ No │ ◇ Finishing the install. Wanna hear a joke? │ Yes │ ○ ────────────────────────────────────────────────────────╮ │ │ │ How do you know if there’s an elephant under your bed? │ │ Your head hits the ceiling! │ │ │ ├──────────────────────────────────────────────────────────╯ │ ◇ App Created 🐰 │ ◇ Installed bun dependencies 📋 │ ○ Result ─────────────────────────────────────────────╮ │ │ │ Success! Project created in my-app directory │ │ │ │ Integrations? Add Netlify, Cloudflare, Tailwind... │ │ bun qwik add │ │ │ │ Relevant docs: │ │ https://qwik.builder.io/docs/getting-started/ │ │ │ │ Questions? Start the conversation at: │ │ https://qwik.builder.io/chat │ │ https://twitter.com/QwikDev │ │ │ │ Presentations, Podcasts and Videos: │ │ https://qwik.builder.io/media/ │ │ │ │ Next steps: │ │ cd my-app │ │ bun start │ │ │ │ │ ├──────────────────────────────────────────────────────╯ │ └ Happy coding! 🎉 ``` *** Run `bun run dev` to start the development server. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun run dev ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} $ vite--mode ssr VITE v4.4.7 ready in 1190 ms ➜ Local: http://localhost:5173/ ➜ Network: use --host to expose ➜ press h to show help ``` *** Open [http://localhost:5173](http://localhost:5173) with your browser to see the result. Qwik will hot-reload your app as you edit your source files. ![Qwik screenshot](https://github.com/oven-sh/bun/assets/3084745/ec35f2f7-03dd-4c90-851e-fb4ad150bb28) *** Refer to the [Qwik docs](https://qwik.builder.io/docs/getting-started/) for complete documentation. # Build a React app with Bun Source: https://bun.com/docs/guides/ecosystem/react Bun supports `.jsx` and `.tsx` files out of the box. React just works with Bun. Create a new React app with `bun init --react`. This gives you a template with a simple React app and a simple API server together in one full-stack app. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} # Create a new React app bun init --react # Run the app in development mode bun dev # Build as a static site for production bun run build # Run the server in production bun start ``` *** ### Hot Reloading Run `bun dev` to start the app in development mode. This will start the API server and the React app with hot reloading. ### Full-Stack App Run `bun start` to start the API server and frontend together in one process. ### Static Site Run `bun run build` to build the app as a static site. This will create a `dist` directory with the built app and all the assets. ```txt File Tree icon="folder-tree" theme={"theme":{"light":"github-light","dark":"dracula"}} ├── src/ │ ├── index.tsx # Server entry point with API routes │ ├── frontend.tsx # React app entry point with HMR │ ├── App.tsx # Main React component │ ├── APITester.tsx # Component for testing API endpoints │ ├── index.html # HTML template │ ├── index.css # Styles │ └── *.svg # Static assets ├── package.json # Dependencies and scripts ├── tsconfig.json # TypeScript configuration ├── bunfig.toml # Bun configuration └── bun.lock # Lock file ``` # Build an app with Remix and Bun Source: https://bun.com/docs/guides/ecosystem/remix Currently the Remix development server (`remix dev`) relies on Node.js APIs that Bun does not yet implement. The guide below uses Bun to initialize a project and install dependencies, but it uses Node.js to run the dev server. *** Initialize a Remix app with `create-remix`. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun create remix ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} remix v1.19.3 💿 Let's build a better website... dir Where should we create your new project? ./my-app ◼ Using basic template See https://remix.run/docs/en/main/guides/templates#templates for more ✔ Template copied git Initialize a new git repository? Yes deps Install dependencies with bun? Yes ✔ Dependencies installed ✔ Git initialized done That's it! Enter your project directory using cd ./my-app Check out README.md for development and deploy instructions. ``` *** To start the dev server, run `bun run dev` from the project root. This will start the dev server using the `remix dev` command. Note that Node.js will be used to run the dev server. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} cd my-app bun run dev ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} $ remix dev 💿 remix dev info building... info built (263ms) Remix App Server started at http://localhost:3000 (http://172.20.0.143:3000) ``` *** Open [http://localhost:3000](http://localhost:3000) to see the app. Any changes you make to `app/routes/_index.tsx` will be hot-reloaded in the browser. ![Remix app running on localhost](https://github.com/oven-sh/bun/assets/3084745/c26f1059-a5d4-4c0b-9a88-d9902472fd77) *** To build and start your app, run `bun run build` ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun run build ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} $ remix build info building... (NODE_ENV=production) info built (158ms) ``` Then `bun run start` from the project root. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun start ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} $ remix-serve ./build/index.js [remix-serve] http://localhost:3000 (http://192.168.86.237:3000) ``` *** Read the [Remix docs](https://remix.run/) for more information on how to build apps with Remix. # Add Sentry to a Bun app Source: https://bun.com/docs/guides/ecosystem/sentry [Sentry](https://sentry.io) is a developer-first error tracking and performance monitoring platform. Sentry has a first-class SDK for Bun, `@sentry/bun`, that instruments your Bun application to automatically collect error and performance data. Don't already have an account and Sentry project established? Head over to [sentry.io](https://sentry.io/signup/), then return to this page. *** To start using Sentry with Bun, first install the Sentry Bun SDK. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun add @sentry/bun ``` *** Then, initialize the Sentry SDK with your Sentry DSN in your app's entry file. You can find your DSN in your Sentry project settings. ```js sentry.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import * as Sentry from "@sentry/bun"; // Ensure to call this before importing any other modules! Sentry.init({ dsn: "__SENTRY_DSN__", // Add Performance Monitoring by setting tracesSampleRate // We recommend adjusting this value in production tracesSampleRate: 1.0, }); ``` *** You can verify that Sentry is working by capturing a test error: ```js sentry.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} setTimeout(() => { try { foo(); } catch (e) { Sentry.captureException(e); } }, 99); ``` To view and resolve the recorded error, log into [sentry.io](https://sentry.io/) and open your project. Clicking on the error's title will open a page where you can see detailed information and mark it as resolved. *** To learn more about Sentry and using the Sentry Bun SDK, view the [Sentry documentation](https://docs.sentry.io/platforms/javascript/guides/bun). # Build an app with SolidStart and Bun Source: https://bun.com/docs/guides/ecosystem/solidstart SolidStart currently relies on Node.js APIs that Bun does not yet implement. The guide below uses Bun to initialize a project and install dependencies, but it uses Node.js to run the dev server. *** Initialize a SolidStart app with `create-solid`. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun create solid my-app ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} create-solid version 0.2.31 Welcome to the SolidStart setup wizard! There are definitely bugs and some feature might not work yet. If you encounter an issue, have a look at https://github.com/solidjs/solid-start/issues and open a new one, if it is not already tracked. ✔ Which template do you want to use? › todomvc ✔ Server Side Rendering? … yes ✔ Use TypeScript? … yes cloned solidjs/solid-start#main to /path/to/my-app/.solid-start ✔ Copied project files ``` *** As instructed by the `create-solid` CLI, let's install our dependencies. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} cd my-app bun install ``` *** Then run the development server. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun run dev # or, equivalently bunx solid-start dev ``` *** Open [localhost:3000](http://localhost:3000). Any changes you make to `src/routes/index.tsx` will be hot-reloaded automatically. ![SolidStart demo app](https://github.com/oven-sh/bun/assets/3084745/1e8043c4-49d1-498c-9add-c1eaab6c7167) *** Refer to the [SolidStart website](https://start.solidjs.com/getting-started/what-is-solidstart) for complete framework documentation. # Server-side render (SSR) a React component Source: https://bun.com/docs/guides/ecosystem/ssr-react To get started, install `react` & `react-dom`: ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} # Any package manager can be used bun add react react-dom ``` *** To render a React component to an HTML stream server-side (SSR): ```tsx ssr-react.tsx icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} import { renderToReadableStream } from "react-dom/server"; function Component(props: { message: string }) { return (

{props.message}

); } const stream = await renderToReadableStream(); ``` *** Combining this with `Bun.serve()`, we get a simple SSR HTTP server: ```tsx server.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} Bun.serve({ async fetch() { const stream = await renderToReadableStream(); return new Response(stream, { headers: { "Content-Type": "text/html" }, }); }, }); ``` *** React `19` and later includes an [SSR optimization](https://github.com/facebook/react/pull/25597) that takes advantage of Bun's "direct" `ReadableStream` implementation. If you run into an error like `export named 'renderToReadableStream' not found`, please make sure to install version `19` of `react` & `react-dom`, or import from `react-dom/server.browser` instead of `react-dom/server`. See [facebook/react#28941](https://github.com/facebook/react/issues/28941) for more information. # Build an HTTP server using StricJS and Bun Source: https://bun.com/docs/guides/ecosystem/stric [StricJS](https://github.com/bunsvr) is a Bun framework for building high-performance web applications and APIs. * **Fast** — Stric is one of the fastest Bun frameworks. See [benchmark](https://github.com/bunsvr/benchmark) for more details. * **Minimal** — The basic components like `@stricjs/router` and `@stricjs/utils` are under 50kB and require no external dependencies. * **Extensible** — Stric includes with a plugin system, dependency injection, and optional optimizations for handling requests. *** Use `bun init` to create an empty project. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} mkdir myapp cd myapp bun init bun add @stricjs/router @stricjs/utils ``` *** To implement a simple HTTP server with StricJS: ```ts index.ts icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} import { Router } from "@stricjs/router"; export default new Router().get("/", () => new Response("Hi")); ``` *** To serve static files from `/public`: ```ts index.ts icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} import { dir } from "@stricjs/utils"; export default new Router().get("/", () => new Response("Hi")).get("/*", dir("./public")); ``` *** Run the file in watch mode to start the development server. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun --watch run index.ts ``` *** For more info, see Stric's [documentation](https://stricjs.netlify.app). # Build an app with SvelteKit and Bun Source: https://bun.com/docs/guides/ecosystem/sveltekit Use `sv create my-app` to create a SvelteKit project with SvelteKit CLI. Answer the prompts to select a template and set up your development environment. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bunx sv create my-app ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} ┌ Welcome to the Svelte CLI! (v0.5.7) │ ◇ Which template would you like? │ SvelteKit demo │ ◇ Add type checking with Typescript? │ Yes, using Typescript syntax │ ◆ Project created │ ◇ What would you like to add to your project? │ none │ ◇ Which package manager do you want to install dependencies with? │ bun │ ◇ Successfully installed dependencies │ ◇ Project next steps ─────────────────────────────────────────────────────╮ │ │ │ 1: cd my-app │ │ 2: git init && git add -A && git commit -m "Initial commit" (optional) │ │ 3: bun run dev -- --open │ │ │ │ To close the dev server, hit Ctrl-C │ │ │ │ Stuck? Visit us at https://svelte.dev/chat │ │ │ ├──────────────────────────────────────────────────────────────────────────╯ │ └ You're all set! ``` *** Once the project is initialized, `cd` into the new project. You don't need to run 'bun install' since the dependencies are already installed. Then start the development server with `bun --bun run dev`. To run the dev server with Node.js instead of Bun, you can omit the `--bun` flag. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} cd my-app bun --bun run dev ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} $ vite dev Forced re-optimization of dependencies VITE v5.4.10 ready in 424 ms ➜ Local: http://localhost:5173/ ➜ Network: use --host to expose ➜ press h + enter to show help ``` *** Visit [http://localhost:5173](http://localhost:5173/) in a browser to see the template app. ![SvelteKit app running](https://github.com/oven-sh/bun/assets/3084745/7c76eae8-78f9-44fa-9f15-1bd3ca1a47c0) *** If you edit and save `src/routes/+page.svelte`, you should see your changes hot-reloaded in the browser. *** To build for production, you'll need to add the right SvelteKit adapter. Currently we recommend the `bun add -D svelte-adapter-bun`. Now, make the following changes to your `svelte.config.js`. ```ts svelte.config.js icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} import adapter from "@sveltejs/adapter-auto"; // [!code --] import adapter from "svelte-adapter-bun"; // [!code ++] import { vitePreprocess } from "@sveltejs/vite-plugin-svelte"; /** @type {import('@sveltejs/kit').Config} */ const config = { // Consult https://svelte.dev/docs/kit/integrations#preprocessors // for more information about preprocessors preprocess: vitePreprocess(), kit: { // adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list. // If your environment is not supported, or you settled on a specific environment, switch out the adapter. // See https://svelte.dev/docs/kit/adapters for more information about adapters. adapter: adapter(), }, }; export default config; ``` *** To build a production bundle: ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun --bun run build ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} $ vite build vite v5.4.10 building SSR bundle for production... "confetti" is imported from external module "@neoconfetti/svelte" but never used in "src/routes/sverdle/+page.svelte". ✓ 130 modules transformed. vite v5.4.10 building for production... ✓ 148 modules transformed. ... ✓ built in 231ms ... ✓ built in 899ms Run npm run preview to preview your production build locally. > Using svelte-adapter-bun ✔ Start server with: bun ./build/index.js ✔ done ``` # Run Bun as a daemon with systemd Source: https://bun.com/docs/guides/ecosystem/systemd [systemd](https://systemd.io) is an init system and service manager for Linux operating systems that manages the startup and control of system processes and services. *** To run a Bun application as a daemon using **systemd** you'll need to create a *service file* in `/lib/systemd/system/`. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} cd /lib/systemd/system touch my-app.service ``` *** Here is a typical service file that runs an application on system start. You can use this as a template for your own service. Replace `YOUR_USER` with the name of the user you want to run the application as. To run as `root`, replace `YOUR_USER` with `root`, though this is generally not recommended for security reasons. Refer to the [systemd documentation](https://www.freedesktop.org/software/systemd/man/systemd.service.html) for more information on each setting. ```ini my-app.service icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} [Unit] # describe the app Description=My App # start the app after the network is available After=network.target [Service] # usually you'll use 'simple' # one of https://www.freedesktop.org/software/systemd/man/systemd.service.html#Type= Type=simple # which user to use when starting the app User=YOUR_USER # path to your application's root directory WorkingDirectory=/home/YOUR_USER/path/to/my-app # the command to start the app # requires absolute paths ExecStart=/home/YOUR_USER/.bun/bin/bun run index.ts # restart policy # one of {no|on-success|on-failure|on-abnormal|on-watchdog|on-abort|always} Restart=always [Install] # start the app automatically WantedBy=multi-user.target ``` *** If your application starts a webserver, note that non-`root` users are not able to listen on ports 80 or 443 by default. To permanently allow Bun to listen on these ports when executed by a non-`root` user, use the following command. This step isn't necessary when running as `root`. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} setcap CAP_NET_BIND_SERVICE=+eip ~/.bun/bin/bun ``` *** With the service file configured, you can now *enable* the service. Once enabled, it will start automatically on reboot. This requires `sudo` permissions. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} systemctl enable my-app ``` *** To start the service without rebooting, you can manually *start* it. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} systemctl start my-app ``` *** Check the status of your application with `systemctl status`. If you've started your app successfully, you should see something like this: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} systemctl status my-app ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} ● my-app.service - My App Loaded: loaded (/lib/systemd/system/my-app.service; enabled; preset: enabled) Active: active (running) since Thu 2023-10-12 11:34:08 UTC; 1h 8min ago Main PID: 309641 (bun) Tasks: 3 (limit: 503) Memory: 40.9M CPU: 1.093s CGroup: /system.slice/my-app.service └─309641 /home/YOUR_USER/.bun/bin/bun run /home/YOUR_USER/application/index.ts ``` *** To update the service, edit the contents of the service file, then reload the daemon. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} systemctl daemon-reload ``` *** For a complete guide on the service unit configuration, you can check [this page](https://www.freedesktop.org/software/systemd/man/systemd.service.html). Or refer to this cheatsheet of common commands: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} systemctl daemon-reload # tell systemd that some files got changed systemctl enable my-app # enable the app (to allow auto-start) systemctl disable my-app # disable the app (turns off auto-start) systemctl start my-app # start the app if is stopped systemctl stop my-app # stop the app systemctl restart my-app # restart the app ``` # Build a frontend using Vite and Bun Source: https://bun.com/docs/guides/ecosystem/vite You can use Vite with Bun, but many projects get faster builds & drop hundreds of dependencies by switching to [HTML imports](/bundler/html-static). *** Vite works out of the box with Bun. Get started with one of Vite's templates. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun create vite my-app ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} ✔ Select a framework: › React ✔ Select a variant: › TypeScript + SWC Scaffolding project in /path/to/my-app... ``` *** Then `cd` into the project directory and install dependencies. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} cd my-app bun install ``` *** Start the development server with the `vite` CLI using `bunx`. The `--bun` flag tells Bun to run Vite's CLI using `bun` instead of `node`; by default Bun respects Vite's `#!/usr/bin/env node` [shebang line](https://en.wikipedia.org/wiki/Shebang_\(Unix\)). ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bunx --bun vite ``` *** To simplify this command, update the `"dev"` script in `package.json` to the following. ```json package.json icon="file-json" theme={"theme":{"light":"github-light","dark":"dracula"}} "scripts": { "dev": "vite", // [!code --] "dev": "bunx --bun vite", // [!code ++] "build": "vite build", "serve": "vite preview" }, // ... ``` *** Now you can start the development server with `bun run dev`. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun run dev ``` *** The following command will build your app for production. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bunx --bun vite build ``` *** This is a stripped down guide to get you started with Vite + Bun. For more information, see the [Vite documentation](https://vitejs.dev/guide/). # Extract links from a webpage using HTMLRewriter Source: https://bun.com/docs/guides/html-rewriter/extract-links ## Extract links from a webpage Bun's [HTMLRewriter](https://bun.com/docs/api/html-rewriter) API can be used to efficiently extract links from HTML content. It works by chaining together CSS selectors to match the elements, text, and attributes you want to process. This is a simple example of how to extract links from a webpage. You can pass `.transform` a `Response`, `Blob`, or `string`. ```ts extract-links.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} async function extractLinks(url: string) { const links = new Set(); const response = await fetch(url); const rewriter = new HTMLRewriter().on("a[href]", { element(el) { const href = el.getAttribute("href"); if (href) { links.add(href); } }, }); // Wait for the response to be processed await rewriter.transform(response).blob(); console.log([...links]); // ["https://bun.com", "/docs", ...] } // Extract all links from the Bun website await extractLinks("https://bun.com"); ``` *** ## Convert relative URLs to absolute When scraping websites, you often want to convert relative URLs (like `/docs`) to absolute URLs. Here's how to handle URL resolution: ```ts extract-links.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} async function extractLinksFromURL(url: string) { const response = await fetch(url); const links = new Set(); const rewriter = new HTMLRewriter().on("a[href]", { element(el) { const href = el.getAttribute("href"); if (href) { // Convert relative URLs to absolute // [!code ++] try { // [!code ++] const absoluteURL = new URL(href, url).href; // [!code ++] links.add(absoluteURL); // [!code ++] } catch { // [!code ++] links.add(href); } // [!code ++] } }, }); // Wait for the response to be processed await rewriter.transform(response).blob(); return [...links]; } const websiteLinks = await extractLinksFromURL("https://example.com"); ``` *** See [Docs > API > HTMLRewriter](https://bun.com/docs/api/html-rewriter) for complete documentation on HTML transformation with Bun. # Extract social share images and Open Graph tags Source: https://bun.com/docs/guides/html-rewriter/extract-social-meta ## Extract social share images and Open Graph tags Bun's [HTMLRewriter](https://bun.com/docs/api/html-rewriter) API can be used to efficiently extract social share images and Open Graph metadata from HTML content. This is particularly useful for building link preview features, social media cards, or web scrapers. We can use HTMLRewriter to match CSS selectors to HTML elements, text, and attributes we want to process. ```ts extract-social-meta.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} interface SocialMetadata { title?: string; description?: string; image?: string; url?: string; siteName?: string; type?: string; } async function extractSocialMetadata(url: string): Promise { const metadata: SocialMetadata = {}; const response = await fetch(url); const rewriter = new HTMLRewriter() // Extract Open Graph meta tags .on('meta[property^="og:"]', { element(el) { const property = el.getAttribute("property"); const content = el.getAttribute("content"); if (property && content) { // Convert "og:image" to "image" etc. const key = property.replace("og:", "") as keyof SocialMetadata; metadata[key] = content; } }, }) // Extract Twitter Card meta tags as fallback .on('meta[name^="twitter:"]', { element(el) { const name = el.getAttribute("name"); const content = el.getAttribute("content"); if (name && content) { const key = name.replace("twitter:", "") as keyof SocialMetadata; // Only use Twitter Card data if we don't have OG data if (!metadata[key]) { metadata[key] = content; } } }, }) // Fallback to regular meta tags .on('meta[name="description"]', { element(el) { const content = el.getAttribute("content"); if (content && !metadata.description) { metadata.description = content; } }, }) // Fallback to title tag .on("title", { text(text) { if (!metadata.title) { metadata.title = text.text; } }, }); // Process the response await rewriter.transform(response).blob(); // Convert relative image URLs to absolute if (metadata.image && !metadata.image.startsWith("http")) { try { metadata.image = new URL(metadata.image, url).href; } catch { // Keep the original URL if parsing fails } } return metadata; } ``` ```ts Example Usage icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} // Example usage const metadata = await extractSocialMetadata("https://bun.com"); console.log(metadata); // { // title: "Bun — A fast all-in-one JavaScript runtime", // description: "Bundle, transpile, install and run JavaScript & TypeScript projects — all in Bun. Bun is a fast all-in-one JavaScript runtime & toolkit designed for speed, complete with a bundler, test runner, and Node.js-compatible package manager.", // image: "https://bun.com/share.jpg", // type: "website", // ... // } ``` # Start a cluster of HTTP servers Source: https://bun.com/docs/guides/http/cluster Run multiple HTTP servers concurrently via the "reusePort" option to share the same port across multiple processes To run multiple HTTP servers concurrently, use the `reusePort` option in `Bun.serve()` which shares the same port across multiple processes. This automatically load balances incoming requests across multiple instances of Bun. ```ts server.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import { serve } from "bun"; const id = Math.random().toString(36).slice(2); serve({ port: process.env.PORT || 8080, development: false, // Share the same port across multiple processes // This is the important part! reusePort: true, async fetch(request) { return new Response("Hello from Bun #" + id + "!\n"); }, }); ``` *** **Linux only** — Windows and macOS ignore the `reusePort` option. This is an operating system limitation with `SO_REUSEPORT`, unfortunately. After saving the file, start your servers on the same port. Under the hood, this uses the Linux `SO_REUSEPORT` and `SO_REUSEADDR` socket options to ensure fair load balancing across multiple processes. [Learn more about `SO_REUSEPORT` and `SO_REUSEADDR`](https://lwn.net/Articles/542629/) ```ts cluster.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import { spawn } from "bun"; const cpus = navigator.hardwareConcurrency; // Number of CPU cores const buns = new Array(cpus); for (let i = 0; i < cpus; i++) { buns[i] = spawn({ cmd: ["bun", "./server.ts"], stdout: "inherit", stderr: "inherit", stdin: "inherit", }); } function kill() { for (const bun of buns) { bun.kill(); } } process.on("SIGINT", kill); process.on("exit", kill); ``` *** Bun has also implemented the `node:cluster` module, but this is a faster, simple, and limited alternative. # Send an HTTP request using fetch Source: https://bun.com/docs/guides/http/fetch Bun implements the Web-standard [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) API for sending HTTP requests. To send a simple `GET` request to a URL: ```ts fetch.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} const response = await fetch("https://bun.com"); const html = await response.text(); // HTML string ``` *** To send a `POST` request to an API endpoint. ```ts fetch.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} const response = await fetch("https://bun.com/api", { method: "POST", body: JSON.stringify({ message: "Hello from Bun!" }), headers: { "Content-Type": "application/json" }, }); const body = await response.json(); ``` # fetch with unix domain sockets in Bun Source: https://bun.com/docs/guides/http/fetch-unix In Bun, the `unix` option in `fetch()` lets you send HTTP requests over a [unix domain socket](https://en.wikipedia.org/wiki/Unix_domain_socket). ```ts fetch-unix.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} const unix = "/var/run/docker.sock"; const response = await fetch("http://localhost/info", { unix }); const body = await response.json(); console.log(body); // { ... } ``` *** The `unix` option is a string that specifies the local file path to a unix domain socket. The `fetch()` function will use the socket to send the request to the server instead of using a TCP network connection. `https` is also supported by using the `https://` protocol in the URL instead of `http://`. To send a `POST` request to an API endpoint over a unix domain socket: ```ts fetch-unix.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} const response = await fetch("https://hostname/a/path", { unix: "/var/run/path/to/unix.sock", method: "POST", body: JSON.stringify({ message: "Hello from Bun!" }), headers: { "Content-Type": "application/json", }, }); const body = await response.json(); ``` # Upload files via HTTP using FormData Source: https://bun.com/docs/guides/http/file-uploads To upload files via HTTP with Bun, use the [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) API. Let's start with a HTTP server that serves a simple HTML web form. ```ts index.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} const server = Bun.serve({ port: 4000, async fetch(req) { const url = new URL(req.url); // return index.html for root path if (url.pathname === "/") return new Response(Bun.file("index.html"), { headers: { "Content-Type": "text/html", }, }); return new Response("Not Found", { status: 404 }); }, }); console.log(`Listening on http://localhost:${server.port}`); ``` *** We can define our HTML form in another file, `index.html`. ```html index.html icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} Form
``` *** At this point, we can run the server and visit [`localhost:4000`](http://localhost:4000) to see our form. ```bash theme={"theme":{"light":"github-light","dark":"dracula"}} bun run index.ts Listening on http://localhost:4000 ``` *** Our form will send a `POST` request to the `/action` endpoint with the form data. Let's handle that request in our server. First we use the [`.formData()`](https://developer.mozilla.org/en-US/docs/Web/API/Request/formData) method on the incoming `Request` to asynchronously parse its contents to a `FormData` instance. Then we can use the [`.get()`](https://developer.mozilla.org/en-US/docs/Web/API/FormData/get) method to extract the value of the `name` and `profilePicture` fields. Here `name` corresponds to a `string` and `profilePicture` is a `Blob`. Finally, we write the `Blob` to disk using [`Bun.write()`](https://bun.com/docs/api/file-io#writing-files-bun-write). ```ts index.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} const server = Bun.serve({ port: 4000, async fetch(req) { const url = new URL(req.url); // return index.html for root path if (url.pathname === "/") return new Response(Bun.file("index.html"), { headers: { "Content-Type": "text/html", }, }); // parse formdata at /action // [!code ++] if (url.pathname === "/action") { // [!code ++] const formdata = await req.formData(); // [!code ++] const name = formdata.get("name"); // [!code ++] const profilePicture = formdata.get("profilePicture"); // [!code ++] if (!profilePicture) throw new Error("Must upload a profile picture."); // [!code ++] // write profilePicture to disk // [!code ++] await Bun.write("profilePicture.png", profilePicture); // [!code ++] return new Response("Success"); // [!code ++] } // [!code ++] return new Response("Not Found", { status: 404 }); }, }); ``` # Hot reload an HTTP server Source: https://bun.com/docs/guides/http/hot Bun supports the [`--hot`](https://bun.com/docs/runtime/hot#hot-mode) flag to run a file with hot reloading enabled. When any module or file changes, Bun re-runs the file. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun --hot run index.ts ``` *** Bun detects when you are running an HTTP server with `Bun.serve()`. It reloads your fetch handler when source files change, *without* restarting the `bun` process. This makes hot reloads nearly instantaneous. Note that this doesn't reload the page on your browser. ```ts index.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} Bun.serve({ port: 3000, fetch(req) { return new Response("Hello world"); }, }); ``` # Proxy HTTP requests using fetch() Source: https://bun.com/docs/guides/http/proxy In Bun, `fetch` supports sending requests through an HTTP or HTTPS proxy. This is useful on corporate networks or when you need to ensure a request is sent through a specific IP address. ```ts proxy.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} await fetch("https://example.com", { // The URL of the proxy server proxy: "https://usertitle:password@proxy.example.com:8080", }); ``` *** The `proxy` option is a URL string that specifies the proxy server. It can include the username and password if the proxy requires authentication. It can be `http://` or `https://`. *** You can also set the `$HTTP_PROXY` or `$HTTPS_PROXY` environment variable to the proxy URL. This is useful when you want to use the same proxy for all requests. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} HTTPS_PROXY=https://usertitle:password@proxy.example.com:8080 bun run index.ts ``` # Common HTTP server usage Source: https://bun.com/docs/guides/http/server This starts an HTTP server listening on port `3000`. It demonstrates basic routing with a number of common responses and also handles POST data from standard forms or as JSON. See [`Bun.serve`](https://bun.com/docs/api/http) for details. ```ts server.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} const server = Bun.serve({ async fetch(req) { const path = new URL(req.url).pathname; // respond with text/html if (path === "/") return new Response("Welcome to Bun!"); // redirect if (path === "/abc") return Response.redirect("/source", 301); // send back a file (in this case, *this* file) if (path === "/source") return new Response(Bun.file(import.meta.path)); // respond with JSON if (path === "/api") return Response.json({ some: "buns", for: "you" }); // receive JSON data to a POST request if (req.method === "POST" && path === "/api/post") { const data = await req.json(); console.log("Received JSON:", data); return Response.json({ success: true, data }); } // receive POST data from a form if (req.method === "POST" && path === "/form") { const data = await req.formData(); console.log(data.get("someField")); return new Response("Success"); } // 404s return new Response("Page not found", { status: 404 }); }, }); console.log(`Listening on ${server.url}`); ``` # Write a simple HTTP server Source: https://bun.com/docs/guides/http/simple This starts an HTTP server listening on port `3000`. It responds to all requests with a `Response` with status `200` and body `"Welcome to Bun!"`. See [`Bun.serve`](https://bun.com/docs/api/http) for details. ```ts server.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} const server = Bun.serve({ port: 3000, fetch(request) { return new Response("Welcome to Bun!"); }, }); console.log(`Listening on ${server.url}`); ``` # Stream a file as an HTTP Response Source: https://bun.com/docs/guides/http/stream-file This snippet reads a file from disk using [`Bun.file()`](https://bun.com/docs/api/file-io#reading-files-bun-file). This returns a `BunFile` instance, which can be passed directly into the `new Response` constructor. ```ts server.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} const path = "/path/to/file.txt"; const file = Bun.file(path); const resp = new Response(file); ``` *** The `Content-Type` is read from the file and automatically set on the `Response`. ```ts server.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} new Response(Bun.file("./package.json")).headers.get("Content-Type"); // => application/json;charset=utf-8 new Response(Bun.file("./test.txt")).headers.get("Content-Type"); // => text/plain;charset=utf-8 new Response(Bun.file("./index.tsx")).headers.get("Content-Type"); // => text/javascript;charset=utf-8 new Response(Bun.file("./img.png")).headers.get("Content-Type"); // => image/png ``` *** Putting it all together with [`Bun.serve()`](https://bun.com/docs/api/http#bun-serve). ```ts server.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} // static file server Bun.serve({ async fetch(req) { const path = new URL(req.url).pathname; const file = Bun.file(path); return new Response(file); }, }); ``` *** See [Docs > API > File I/O](https://bun.com/docs/api/file-io#writing-files-bun-write) for complete documentation of `Bun.write()`. # Streaming HTTP Server with Async Iterators Source: https://bun.com/docs/guides/http/stream-iterator In Bun, [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) objects can accept an async generator function as their body. This allows you to stream data to the client as it becomes available, rather than waiting for the entire response to be ready. ```ts stream-iterator.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} Bun.serve({ port: 3000, fetch(req) { return new Response( // An async generator function async function* () { yield "Hello, "; await Bun.sleep(100); yield "world!"; // you can also yield a TypedArray or Buffer yield new Uint8Array(["\n".charCodeAt(0)]); }, { headers: { "Content-Type": "text/plain" } }, ); }, }); ``` *** You can pass any async iterable directly to `Response`: ```ts stream-iterator.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} Bun.serve({ port: 3000, fetch(req) { return new Response( { [Symbol.asyncIterator]: async function* () { yield "Hello, "; await Bun.sleep(100); yield "world!"; }, }, { headers: { "Content-Type": "text/plain" } }, ); }, }); ``` # Streaming HTTP Server with Node.js Streams Source: https://bun.com/docs/guides/http/stream-node-streams-in-bun In Bun, [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) objects can accept a Node.js [`Readable`](https://nodejs.org/api/stream.html#stream_readable_streams). This works because Bun's `Response` object allows any async iterable as its body. Node.js streams are async iterables, so you can pass them directly to `Response`. ```ts server.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import { Readable } from "stream"; import { serve } from "bun"; serve({ port: 3000, fetch(req) { return new Response(Readable.from(["Hello, ", "world!"]), { headers: { "Content-Type": "text/plain" }, }); }, }); ``` # Configure TLS on an HTTP server Source: https://bun.com/docs/guides/http/tls Set the `tls` key to configure TLS. Both `key` and `cert` are required. The `key` should be the contents of your private key; `cert` should be the contents of your issued certificate. Use [`Bun.file()`](https://bun.com/docs/api/file-io#reading-files-bun-file) to read the contents. ```ts server.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} const server = Bun.serve({ fetch: request => new Response("Welcome to Bun!"), tls: { cert: Bun.file("cert.pem"), key: Bun.file("key.pem"), }, }); ``` *** By default Bun trusts the default Mozilla-curated list of well-known root CAs. To override this list, pass an array of certificates as `ca`. ```ts server.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} const server = Bun.serve({ fetch: request => new Response("Welcome to Bun!"), tls: { cert: Bun.file("cert.pem"), key: Bun.file("key.pem"), ca: [Bun.file("ca1.pem"), Bun.file("ca2.pem")], }, }); ``` # Guides Source: https://bun.com/docs/guides/index A collection of code samples and walkthroughs for performing common tasks with Bun. export const GuidesList = () => { const guidesData = { hero: { title: "Guides", blurb: "A collection of code samples and walkthroughs for performing common tasks with Bun." }, featured: [{ category: "Ecosystem", title: "Build a frontend using Vite and Bun", href: "/guides/ecosystem/vite", cta: "View guide" }, { category: "Runtime", title: "Install TypeScript declarations for Bun", href: "/guides/runtime/typescript", cta: "View guide" }, { category: "Streams", title: "Convert a ReadableStream to a string", href: "/guides/streams/to-string", cta: "View guide" }, { category: "HTTP", title: "Write a simple HTTP server", href: "/guides/http/simple", cta: "View guide" }, { category: "WebSocket", title: "Build a simple WebSocket server", href: "/guides/websocket/simple", cta: "View guide" }, { category: "Reading files", title: "Read a file as a string", href: "/guides/read-file/string", cta: "View guide" }], categories: [{ key: "deployment", title: "Deployment", icon: "rocket", items: [{ title: "Deploy Bun on Vercel", href: "/guides/deployment/vercel" }, { title: "Deploy Bun on Railway", href: "/guides/deployment/railway" }, { title: "Deploy Bun on Render", href: "/guides/deployment/render" }] }, { key: "binary", title: "Binary data", icon: "binary", items: [{ title: "Convert an ArrayBuffer to an array of numbers", href: "/guides/binary/arraybuffer-to-array" }, { title: "Convert an ArrayBuffer to a Blob", href: "/guides/binary/arraybuffer-to-blob" }, { title: "Convert an ArrayBuffer to a Buffer", href: "/guides/binary/arraybuffer-to-buffer" }, { title: "Convert an ArrayBuffer to a string", href: "/guides/binary/arraybuffer-to-string" }, { title: "Convert an ArrayBuffer to a Uint8Array", href: "/guides/binary/arraybuffer-to-typedarray" }, { title: "Convert a Blob to an ArrayBuffer", href: "/guides/binary/blob-to-arraybuffer" }, { title: "Convert a Blob to a DataView", href: "/guides/binary/blob-to-dataview" }, { title: "Convert a Blob to a ReadableStream", href: "/guides/binary/blob-to-stream" }, { title: "Convert a Blob to a string", href: "/guides/binary/blob-to-string" }, { title: "Convert a Blob to a Uint8Array", href: "/guides/binary/blob-to-typedarray" }, { title: "Convert a Buffer to an ArrayBuffer", href: "/guides/binary/buffer-to-arraybuffer" }, { title: "Convert a Buffer to a blob", href: "/guides/binary/buffer-to-blob" }, { title: "Convert a Buffer to a ReadableStream", href: "/guides/binary/buffer-to-readablestream" }, { title: "Convert a Buffer to a string", href: "/guides/binary/buffer-to-string" }, { title: "Convert a Buffer to a Uint8Array", href: "/guides/binary/buffer-to-typedarray" }, { title: "Convert a DataView to a string", href: "/guides/binary/dataview-to-string" }, { title: "Convert a Uint8Array to an ArrayBuffer", href: "/guides/binary/typedarray-to-arraybuffer" }, { title: "Convert a Uint8Array to a Blob", href: "/guides/binary/typedarray-to-blob" }, { title: "Convert a Uint8Array to a Buffer", href: "/guides/binary/typedarray-to-buffer" }, { title: "Convert a Uint8Array to a DataView", href: "/guides/binary/typedarray-to-dataview" }, { title: "Convert a Uint8Array to a ReadableStream", href: "/guides/binary/typedarray-to-readablestream" }, { title: "Convert a Uint8Array to a string", href: "/guides/binary/typedarray-to-string" }] }, { key: "ecosystem", title: "Ecosystem", icon: "puzzle", items: [{ title: "Use EdgeDB with Bun", href: "/guides/ecosystem/edgedb" }, { title: "Use Prisma ORM with Bun", href: "/guides/ecosystem/prisma" }, { title: "Use Prisma Postgres with Bun", href: "/guides/ecosystem/prisma-postgres" }, { title: "Create a Discord bot", href: "/guides/ecosystem/discordjs" }, { title: "Add Sentry to a Bun app", href: "/guides/ecosystem/sentry" }, { title: "Use Drizzle ORM with Bun", href: "/guides/ecosystem/drizzle" }, { title: "Build a React app with Bun", href: "/guides/ecosystem/react" }, { title: "Run Bun as a daemon with PM2", href: "/guides/ecosystem/pm2" }, { title: "Build an app with Nuxt and Bun", href: "/guides/ecosystem/nuxt" }, { title: "Build an app with Qwik and Bun", href: "/guides/ecosystem/qwik" }, { title: "Build an app with Astro and Bun", href: "/guides/ecosystem/astro" }, { title: "Build an app with Remix and Bun", href: "/guides/ecosystem/remix" }, { title: "Run Bun as a daemon with systemd", href: "/guides/ecosystem/systemd" }, { title: "Build an app with Next.js and Bun", href: "/guides/ecosystem/nextjs" }, { title: "Build an app with SvelteKit and Bun", href: "/guides/ecosystem/sveltekit" }, { title: "Build a frontend using Vite and Bun", href: "/guides/ecosystem/vite" }, { title: "Build an app with SolidStart and Bun", href: "/guides/ecosystem/solidstart" }, { title: "Use Neon Postgres through Drizzle ORM", href: "/guides/ecosystem/neon-drizzle" }, { title: "Build an HTTP server using Hono and Bun", href: "/guides/ecosystem/hono" }, { title: "Use Neon\'s Serverless Postgres with Bun", href: "/guides/ecosystem/neon-serverless-postgres" }, { title: "Build an HTTP server using Elysia and Bun", href: "/guides/ecosystem/elysia" }, { title: "Containerize a Bun application with Docker", href: "/guides/ecosystem/docker" }, { title: "Build an HTTP server using Express and Bun", href: "/guides/ecosystem/express" }, { title: "Server-side render (SSR) a React component", href: "/guides/ecosystem/ssr-react" }, { title: "Build an HTTP server using StricJS and Bun", href: "/guides/ecosystem/stric" }, { title: "Read and write data to MongoDB using Mongoose and Bun", href: "/guides/ecosystem/mongoose" }] }, { key: "htmlrewriter", title: "HTMLRewriter", icon: "file-code-2", items: [{ title: "Extract links from a webpage using HTMLRewriter", href: "/guides/html-rewriter/extract-links" }, { title: "Extract social share images and Open Graph tags", href: "/guides/html-rewriter/extract-social-meta" }] }, { key: "http", title: "HTTP", icon: "globe", items: [{ title: "Common HTTP server usage", href: "/guides/http/server" }, { title: "Hot reload an HTTP server", href: "/guides/http/hot" }, { title: "Write a simple HTTP server", href: "/guides/http/simple" }, { title: "Start a cluster of HTTP servers", href: "/guides/http/cluster" }, { title: "Configure TLS on an HTTP server", href: "/guides/http/tls" }, { title: "Send an HTTP request using fetch", href: "/guides/http/fetch" }, { title: "Proxy HTTP requests using fetch()", href: "/guides/http/proxy" }, { title: "Stream a file as an HTTP Response", href: "/guides/http/stream-file" }, { title: "Upload files via HTTP using FormData", href: "/guides/http/file-uploads" }, { title: "fetch with unix domain sockets in Bun", href: "/guides/http/fetch-unix" }, { title: "Streaming HTTP Server with Async Iterators", href: "/guides/http/stream-iterator" }, { title: "Streaming HTTP Server with Node.js Streams", href: "/guides/http/stream-node-streams-in-bun" }] }, { key: "install", title: "Package manager", icon: "package", items: [{ title: "Add a dependency", href: "/guides/install/add" }, { title: "Add a Git dependency", href: "/guides/install/add-git" }, { title: "Add a peer dependency", href: "/guides/install/add-peer" }, { title: "Add a tarball dependency", href: "/guides/install/add-tarball" }, { title: "Add a trusted dependency", href: "/guides/install/trusted" }, { title: "Add an optional dependency", href: "/guides/install/add-optional" }, { title: "Add a development dependency", href: "/guides/install/add-dev" }, { title: "Using bun install with Artifactory", href: "/guides/install/jfrog-artifactory" }, { title: "Generate a yarn-compatible lockfile", href: "/guides/install/yarnlock" }, { title: "Migrate from npm install to bun install", href: "/guides/install/from-npm-install-to-bun-install" }, { title: "Configuring a monorepo using workspaces", href: "/guides/install/workspaces" }, { title: "Install a package under a different name", href: "/guides/install/npm-alias" }, { title: "Configure git to diff Bun\'s lockb lockfile", href: "/guides/install/git-diff-bun-lockfile" }, { title: "Install dependencies with Bun in GitHub Actions", href: "/guides/install/cicd" }, { title: "Override the default npm registry for bun install", href: "/guides/install/custom-registry" }, { title: "Using bun install with an Azure Artifacts npm registry", href: "/guides/install/azure-artifacts" }, { title: "Configure a private registry for an organization scope with bun install", href: "/guides/install/registry-scope" }] }, { key: "processes", title: "Processes", icon: "cpu", items: [{ title: "Read from stdin", href: "/guides/process/stdin" }, { title: "Listen for CTRL+C", href: "/guides/process/ctrl-c" }, { title: "Listen to OS signals", href: "/guides/process/os-signals" }, { title: "Spawn a child process", href: "/guides/process/spawn" }, { title: "Parse command-line arguments", href: "/guides/process/argv" }, { title: "Read stderr from a child process", href: "/guides/process/spawn-stderr" }, { title: "Read stdout from a child process", href: "/guides/process/spawn-stdout" }, { title: "Get the process uptime in nanoseconds", href: "/guides/process/nanoseconds" }, { title: "Spawn a child process and communicate using IPC", href: "/guides/process/ipc" }] }, { key: "read-file", title: "Reading files", icon: "file", items: [{ title: "Read a JSON file", href: "/guides/read-file/json" }, { title: "Check if a file exists", href: "/guides/read-file/exists" }, { title: "Read a file to a Buffer", href: "/guides/read-file/buffer" }, { title: "Read a file as a string", href: "/guides/read-file/string" }, { title: "Get the MIME type of a file", href: "/guides/read-file/mime" }, { title: "Read a file to a Uint8Array", href: "/guides/read-file/uint8array" }, { title: "Read a file to an ArrayBuffer", href: "/guides/read-file/arraybuffer" }, { title: "Watch a directory for changes", href: "/guides/read-file/watch" }, { title: "Read a file as a ReadableStream", href: "/guides/read-file/stream" }] }, { key: "runtime", title: "Runtime", icon: "bot", items: [{ title: "Delete files", href: "/guides/runtime/delete-file" }, { title: "Delete directories", href: "/guides/runtime/delete-directory" }, { title: "Import a JSON file", href: "/guides/runtime/import-json" }, { title: "Import a TOML file", href: "/guides/runtime/import-toml" }, { title: "Import a YAML file", href: "/guides/runtime/import-yaml" }, { title: "Run a Shell Command", href: "/guides/runtime/shell" }, { title: "Re-map import paths", href: "/guides/runtime/tsconfig-paths" }, { title: "Set a time zone in Bun", href: "/guides/runtime/timezone" }, { title: "Set environment variables", href: "/guides/runtime/set-env" }, { title: "Import a HTML file as text", href: "/guides/runtime/import-html" }, { title: "Read environment variables", href: "/guides/runtime/read-env" }, { title: "Build-time constants with --define", href: "/guides/runtime/build-time-constants" }, { title: "Debugging Bun with the web debugger", href: "/guides/runtime/web-debugger" }, { title: "Install and run Bun in GitHub Actions", href: "/guides/runtime/cicd" }, { title: "Install TypeScript declarations for Bun", href: "/guides/runtime/typescript" }, { title: "Debugging Bun with the VS Code extension", href: "/guides/runtime/vscode-debugger" }, { title: "Inspect memory usage using V8 heap snapshots", href: "/guides/runtime/heap-snapshot" }, { title: "Define and replace static globals & constants", href: "/guides/runtime/define-constant" }, { title: "Codesign a single-file JavaScript executable on macOS", href: "/guides/runtime/codesign-macos-executable" }] }, { key: "streams", title: "Streams", icon: "waves", items: [{ title: "Convert a ReadableStream to JSON", href: "/guides/streams/to-json" }, { title: "Convert a Node.js Readable to JSON", href: "/guides/streams/node-readable-to-json" }, { title: "Convert a ReadableStream to a Blob", href: "/guides/streams/to-blob" }, { title: "Convert a Node.js Readable to a Blob", href: "/guides/streams/node-readable-to-blob" }, { title: "Convert a ReadableStream to a Buffer", href: "/guides/streams/to-buffer" }, { title: "Convert a ReadableStream to a string", href: "/guides/streams/to-string" }, { title: "Convert a Node.js Readable to a string", href: "/guides/streams/node-readable-to-string" }, { title: "Convert a ReadableStream to a Uint8Array", href: "/guides/streams/to-typedarray" }, { title: "Convert a ReadableStream to an ArrayBuffer", href: "/guides/streams/to-arraybuffer" }, { title: "Convert a Node.js Readable to an Uint8Array", href: "/guides/streams/node-readable-to-uint8array" }, { title: "Convert a Node.js Readable to an ArrayBuffer", href: "/guides/streams/node-readable-to-arraybuffer" }, { title: "Convert a ReadableStream to an array of chunks", href: "/guides/streams/to-array" }] }, { key: "test", title: "Test runner", icon: "test-tube", items: [{ title: "Mock functions in bun test", href: "/guides/test/mock-functions" }, { title: "Spy on methods in bun test", href: "/guides/test/spy-on" }, { title: "Using Testing Library with Bun", href: "/guides/test/testing-library" }, { title: "Update snapshots in bun test", href: "/guides/test/update-snapshots" }, { title: "Run tests in watch mode with Bun", href: "/guides/test/watch-mode" }, { title: "Use snapshot testing in bun test", href: "/guides/test/snapshot" }, { title: "Bail early with the Bun test runner", href: "/guides/test/bail" }, { title: "Skip tests with the Bun test runner", href: "/guides/test/skip-tests" }, { title: "Migrate from Jest to Bun's test runner", href: "/guides/test/migrate-from-jest" }, { title: "Run your tests with the Bun test runner", href: "/guides/test/run-tests" }, { title: "Set the system time in Bun's test runner", href: "/guides/test/mock-clock" }, { title: "Write browser DOM tests with Bun and happy-dom", href: "/guides/test/happy-dom" }, { title: "Set a per-test timeout with the Bun test runner", href: "/guides/test/timeout" }, { title: 'Mark a test as a "todo" with the Bun test runner', href: "/guides/test/todo-tests" }, { title: "Re-run tests multiple times with the Bun test runner", href: "/guides/test/rerun-each" }, { title: "Set a code coverage threshold with the Bun test runner", href: "/guides/test/coverage-threshold" }, { title: "Generate code coverage reports with the Bun test runner", href: "/guides/test/coverage" }, { title: "import, require, and test Svelte components with bun test", href: "/guides/test/svelte-test" }] }, { key: "utilities", title: "Utilities", icon: "wrench", items: [{ title: "Hash a password", href: "/guides/util/hash-a-password" }, { title: "Generate a UUID", href: "/guides/util/javascript-uuid" }, { title: "Escape an HTML string", href: "/guides/util/escape-html" }, { title: "Get the current Bun version", href: "/guides/util/version" }, { title: "Encode and decode base64 strings", href: "/guides/util/base64" }, { title: "Check if two objects are deeply equal", href: "/guides/util/deep-equals" }, { title: "Detect when code is executed with Bun", href: "/guides/util/detect-bun" }, { title: "Get the directory of the current file", href: "/guides/util/import-meta-dir" }, { title: "Get the file name of the current file", href: "/guides/util/import-meta-file" }, { title: "Convert a file URL to an absolute path", href: "/guides/util/file-url-to-path" }, { title: "Compress and decompress data with gzip", href: "/guides/util/gzip" }, { title: "Convert an absolute path to a file URL", href: "/guides/util/path-to-file-url" }, { title: "Get the path to an executable bin file", href: "/guides/util/which-path-to-executable-bin" }, { title: "Sleep for a fixed number of milliseconds", href: "/guides/util/sleep" }, { title: "Compress and decompress data with DEFLATE", href: "/guides/util/deflate" }, { title: "Get the absolute path of the current file", href: "/guides/util/import-meta-path" }, { title: "Check if the current file is the entrypoint", href: "/guides/util/entrypoint" }, { title: "Get the absolute path to the current entrypoint", href: "/guides/util/main" }] }, { key: "websocket", title: "WebSocket", icon: "radio", items: [{ title: "Build a simple WebSocket server", href: "/guides/websocket/simple" }, { title: "Enable compression for WebSocket messages", href: "/guides/websocket/compression" }, { title: "Build a publish-subscribe WebSocket server", href: "/guides/websocket/pubsub" }, { title: "Set per-socket contextual data on a WebSocket", href: "/guides/websocket/context" }] }, { key: "write-file", title: "Writing files", icon: "file-pen", items: [{ title: "Delete a file", href: "/guides/write-file/unlink" }, { title: "Write to stdout", href: "/guides/write-file/stdout" }, { title: "Write a Blob to a file", href: "/guides/write-file/blob" }, { title: "Write a file to stdout", href: "/guides/write-file/cat" }, { title: "Append content to a file", href: "/guides/write-file/append" }, { title: "Write a string to a file", href: "/guides/write-file/basic" }, { title: "Write a file incrementally", href: "/guides/write-file/filesink" }, { title: "Write a Response to a file", href: "/guides/write-file/response" }, { title: "Copy a file to another location", href: "/guides/write-file/file-cp" }, { title: "Write a ReadableStream to a file", href: "/guides/write-file/stream" }] }] }; return
{}

Featured

{guidesData.featured.map(g => )}
{}

All Guides

{guidesData.categories.map(category =>

{category.title}

{category.items.map(guide => )}
)}
; }; # Add a dependency Source: https://bun.com/docs/guides/install/add To add an npm package as a dependency, use `bun add`. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun add zod ``` *** This will add the package to `dependencies` in `package.json`. By default, the `^` range specifier will be used, to indicate that any future minor or patch versions are acceptable. ```json package.json icon="file-json" theme={"theme":{"light":"github-light","dark":"dracula"}} { "dependencies": { "zod": "^3.0.0" // [!code ++] } } ``` *** To "pin" to an exact version of the package, use `--exact`. This will add the package to `dependencies` without the `^`, pinning your project to the exact version you installed. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun add zod --exact ``` *** To specify an exact version or a tag: ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun add zod@3.0.0 bun add zod@next ``` *** See [Docs > Package manager](https://bun.com/docs/cli/install) for complete documentation of Bun's package manager. # Add a development dependency Source: https://bun.com/docs/guides/install/add-dev To add an npm package as a development dependency, use `bun add --development`. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun add zod --dev bun add zod -d # shorthand ``` *** This will add the package to `devDependencies` in `package.json`. ```json theme={"theme":{"light":"github-light","dark":"dracula"}} { "devDependencies": { "zod": "^3.0.0" // [!code ++] } } ``` *** See [Docs > Package manager](https://bun.com/docs/cli/install) for complete documentation of Bun's package manager. # Add a Git dependency Source: https://bun.com/docs/guides/install/add-git Bun supports directly adding GitHub repositories as dependencies of your project. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun add github:lodash/lodash ``` *** This will add the following line to your `package.json`: ```json package.json icon="file-json" theme={"theme":{"light":"github-light","dark":"dracula"}} { "dependencies": { "lodash": "github:lodash/lodash" } } ``` *** Bun supports a number of protocols for specifying Git dependencies. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun add git+https://github.com/lodash/lodash.git bun add git+ssh://github.com/lodash/lodash.git#4.17.21 bun add git@github.com:lodash/lodash.git bun add github:colinhacks/zod ``` *** See [Docs > Package manager](https://bun.com/docs/cli/install) for complete documentation of Bun's package manager. # Add an optional dependency Source: https://bun.com/docs/guides/install/add-optional To add an npm package as an optional dependency, use the `--optional` flag. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun add zod --optional ``` *** This will add the package to `optionalDependencies` in `package.json`. ```json package.json icon="file-json" theme={"theme":{"light":"github-light","dark":"dracula"}} { "optionalDependencies": { "zod": "^3.0.0" // [!code ++] } } ``` *** See [Docs > Package manager](https://bun.com/docs/cli/install) for complete documentation of Bun's package manager. # Add a peer dependency Source: https://bun.com/docs/guides/install/add-peer To add an npm package as a peer dependency, use the `--peer` flag. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun add @types/bun --peer ``` *** This will add the package to `peerDependencies` in `package.json`. ```json package.json icon="file-json" theme={"theme":{"light":"github-light","dark":"dracula"}} { "peerDependencies": { "@types/bun": "^1.3.1" // [!code ++] } } ``` *** Running `bun install` will install peer dependencies by default, unless marked optional in `peerDependenciesMeta`. ```json package.json icon="file-json" theme={"theme":{"light":"github-light","dark":"dracula"}} { "peerDependencies": { "@types/bun": "^1.3.1" }, "peerDependenciesMeta": { "@types/bun": { // [!code ++] "optional": true // [!code ++] } // [!code ++] } } ``` *** See [Docs > Package manager](https://bun.com/docs/cli/install) for complete documentation of Bun's package manager. # Add a tarball dependency Source: https://bun.com/docs/guides/install/add-tarball Bun's package manager can install any publicly available tarball URL as a dependency of your project. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun add zod@https://registry.npmjs.org/zod/-/zod-3.21.4.tgz ``` *** Running this command will download, extract, and install the tarball to your project's `node_modules` directory. It will also add the following line to your `package.json`: ```json package.json icon="file-json" theme={"theme":{"light":"github-light","dark":"dracula"}} { "dependencies": { "zod": "https://registry.npmjs.org/zod/-/zod-3.21.4.tgz" // [!code ++] } } ``` *** The package `"zod"` can now be imported as usual. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} import { z } from "zod"; ``` *** See [Docs > Package manager](https://bun.com/docs/cli/install) for complete documentation of Bun's package manager. # Using bun install with an Azure Artifacts npm registry Source: https://bun.com/docs/guides/install/azure-artifacts In [Azure Artifact's](https://learn.microsoft.com/en-us/azure/devops/artifacts/npm/npmrc?view=azure-devops\&tabs=windows%2Cclassic) instructions for `.npmrc`, they say to base64 encode the password. Do not do this for `bun install`. Bun will automatically base64 encode the password for you if needed. [Azure Artifacts](https://azure.microsoft.com/en-us/products/devops/artifacts) is a package management system for Azure DevOps. It allows you to host your own private npm registry, npm packages, and other types of packages as well. *** ### Configure with bunfig.toml *** To use it with `bun install`, add a `bunfig.toml` file to your project with the following contents. Make sure to replace `my-azure-artifacts-user` with your Azure Artifacts username, such as `jarred1234`. ```toml bunfig.toml icon="settings" theme={"theme":{"light":"github-light","dark":"dracula"}} [install.registry] url = "https://pkgs.dev.azure.com/my-azure-artifacts-user/_packaging/my-azure-artifacts-user/npm/registry" username = "my-azure-artifacts-user" # Bun v1.0.3+ supports using an environment variable here password = "$NPM_PASSWORD" ``` *** Then assign your Azure Personal Access Token to the `NPM_PASSWORD` environment variable. Bun [automatically reads](https://bun.com/docs/runtime/env) `.env` files, so create a file called `.env` in your project root. There is no need to base-64 encode this token! Bun will do this for you. ```txt .env icon="settings" theme={"theme":{"light":"github-light","dark":"dracula"}} NPM_PASSWORD= ``` *** ### Configure with environment variables *** To configure Azure Artifacts without `bunfig.toml`, you can set the `NPM_CONFIG_REGISTRY` environment variable. The URL should include `:username` and `:_password` as query parameters. Replace `` and `` with the apprropriate values. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} NPM_CONFIG_REGISTRY=https://pkgs.dev.azure.com/my-azure-artifacts-user/_packaging/my-azure-artifacts-user/npm/registry/:username=:_password= ``` *** ### Don't base64 encode the password *** In [Azure Artifact's](https://learn.microsoft.com/en-us/azure/devops/artifacts/npm/npmrc?view=azure-devops\&tabs=windows%2Cclassic) instructions for `.npmrc`, they say to base64 encode the password. Do not do this for `bun install`. Bun will automatically base64 encode the password for you if needed. **Tip** — If it ends with `==`, it probably is base64 encoded. *** To decode a base64-encoded password, open your browser console and run: ```js browser icon="computer" theme={"theme":{"light":"github-light","dark":"dracula"}} atob(""); ``` *** Alternatively, use the `base64` command line tool, but doing so means it may be saved in your terminal history which is not recommended: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} echo "base64-encoded-password" | base64 --decode ``` # Install dependencies with Bun in GitHub Actions Source: https://bun.com/docs/guides/install/cicd Use the official [`setup-bun`](https://github.com/oven-sh/setup-bun) GitHub Action to install `bun` in your GitHub Actions runner. ```yaml workflow.yml icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} title: my-workflow jobs: my-job: title: my-job runs-on: ubuntu-latest steps: # ... - uses: actions/checkout@v4 - uses: oven-sh/setup-bun@v2 // [!code ++] # run any `bun` or `bunx` command - run: bun install // [!code ++] ``` *** To specify a version of Bun to install: ```yaml workflow.yml icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} title: my-workflow jobs: my-job: title: my-job runs-on: ubuntu-latest steps: # ... - uses: oven-sh/setup-bun@v2 with: # [!code ++] version: "latest" # or "canary" # [!code ++] ``` *** Refer to the [README.md](https://github.com/oven-sh/setup-bun) for complete documentation of the `setup-bun` GitHub Action. # Override the default npm registry for bun install Source: https://bun.com/docs/guides/install/custom-registry The default registry is `registry.npmjs.org`. This can be globally configured in `bunfig.toml`. ```toml bunfig.toml icon="settings" theme={"theme":{"light":"github-light","dark":"dracula"}} [install] # set default registry as a string registry = "https://registry.npmjs.org" # if needed, set a token registry = { url = "https://registry.npmjs.org", token = "123456" } # if needed, set a username/password registry = "https://usertitle:password@registry.npmjs.org" ``` *** Your `bunfig.toml` can reference environment variables. Bun automatically loads environment variables from `.env.local`, `.env.[NODE_ENV]`, and `.env`. See [Docs > Environment variables](https://bun.com/docs/runtime/env) for more information. ```toml bunfig.toml icon="settings" theme={"theme":{"light":"github-light","dark":"dracula"}} [install] registry = { url = "https://registry.npmjs.org", token = "$npm_token" } ``` *** See [Docs > Package manager](https://bun.com/docs/cli/install) for complete documentation of Bun's package manager. # Migrate from npm install to bun install Source: https://bun.com/docs/guides/install/from-npm-install-to-bun-install `bun install` is a Node.js compatible npm client designed to be an incredibly fast successor to npm. We've put a lot of work into making sure that the migration path from `npm install` to `bun install` is as easy as running `bun install` instead of `npm install`. * **Designed for Node.js & Bun**: `bun install` installs a Node.js compatible `node_modules` folder. You can use it in place of `npm install` for Node.js projects without any code changes and without using Bun's runtime. * **Automatically converts `package-lock.json`** to bun's `bun.lock` lockfile format, preserving your existing resolved dependency versions without any manual work on your part. You can secretly use `bun install` in place of `npm install` at work without anyone noticing. * **`.npmrc` compatible**: bun install reads npm registry configuration from npm's `.npmrc`, so you can use the same configuration for both npm and Bun. * **Hardlinks**: On Windows and Linux, `bun install` uses hardlinks to conserve disk space and install times. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} # It only takes one command to migrate bun i # To add dependencies: bun i @types/bun # To add devDependencies: bun i -d @types/bun # To remove a dependency: bun rm @types/bun ``` *** ## Run package.json scripts faster Run scripts from package.json, executables from `node_modules/.bin` (sort of like `npx`), and JavaScript/TypeScript files (just like `node`) - all from a single simple command. | NPM | Bun | | ------------------ | ---------------- | | `npm run ``` *** Now you can `import` or `require` `*.svelte` files in your tests, and it will load the Svelte component as a JavaScript module. ```ts hello-svelte.test.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import { test, expect } from "bun:test"; import { render, fireEvent } from "@testing-library/svelte"; import Counter from "./Counter.svelte"; test("Counter increments when clicked", async () => { const { getByText, component } = render(Counter); const button = getByText("+1"); // Initial state expect(component.$$.ctx[0]).toBe(0); // initialCount is the first prop // Click the increment button await fireEvent.click(button); // Check the new state expect(component.$$.ctx[0]).toBe(1); }); ``` *** Use `bun test` to run your tests. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun test ``` # Using Testing Library with Bun Source: https://bun.com/docs/guides/test/testing-library You can use [Testing Library](https://testing-library.com/) with Bun's test runner. *** As a prerequisite to using Testing Library you will need to install [Happy Dom](https://github.com/capricorn86/happy-dom). ([see Bun's Happy DOM guide for more information](https://bun.sh/guides/test/happy-dom)). ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun add -D @happy-dom/global-registrator ``` *** Next you should install the Testing Library packages you are planning on using. For example, if you are setting up testing for React your installs may look like this. You will also need to install `@testing-library/jest-dom` to get matchers working later. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun add -D @testing-library/react @testing-library/dom @testing-library/jest-dom ``` *** Next you will need to create a preload script for Happy DOM and for Testing Library. For more details about the Happy DOM setup script see [Bun's Happy DOM guide](https://bun.sh/guides/test/happy-dom). ```ts happydom.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import { GlobalRegistrator } from "@happy-dom/global-registrator"; GlobalRegistrator.register(); ``` *** For Testing Library, you will want to extend Bun's `expect` function with Testing Library's matchers. Optionally, to better match the behavior of test-runners like Jest, you may want to run cleanup after each test. ```ts testing-library.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import { afterEach, expect } from "bun:test"; import { cleanup } from "@testing-library/react"; import * as matchers from "@testing-library/jest-dom/matchers"; expect.extend(matchers); // Optional: cleans up `render` after each test afterEach(() => { cleanup(); }); ``` *** Next, add these preload scripts to your `bunfig.toml` (you can also have everything in a single `preload.ts` script if you prefer). ```toml bunfig.toml icon="settings" theme={"theme":{"light":"github-light","dark":"dracula"}} [test] preload = ["./happydom.ts", "./testing-library.ts"] ``` *** If you are using TypeScript you will also need to make use of declaration merging in order to get the new matcher types to show up in your editor. To do this, create a type declaration file that extends `Matchers` like this. ```ts matchers.d.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import { TestingLibraryMatchers } from "@testing-library/jest-dom/matchers"; import { Matchers, AsymmetricMatchers } from "bun:test"; declare module "bun:test" { interface Matchers extends TestingLibraryMatchers {} interface AsymmetricMatchers extends TestingLibraryMatchers {} } ``` *** You should now be able to use Testing Library in your tests ```ts matchers.d.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import { test, expect } from "bun:test"; import { screen, render } from "@testing-library/react"; import { MyComponent } from "./myComponent"; test("Can use Testing Library", () => { render(MyComponent); const myComponent = screen.getByTestId("my-component"); expect(myComponent).toBeInTheDocument(); }); ``` *** Refer to the [Testing Library docs](https://testing-library.com/), [Happy DOM repo](https://github.com/capricorn86/happy-dom) and [Docs > Test runner > DOM](https://bun.sh/docs/test/dom) for complete documentation on writing browser tests with Bun. # Set a per-test timeout with the Bun test runner Source: https://bun.com/docs/guides/test/timeout Use the `--timeout` flag to set a timeout for each test in milliseconds. If any test exceeds this timeout, it will be marked as failed. The default timeout is `5000` (5 seconds). ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun test --timeout 3000 # 3 seconds ``` *** See [Docs > Test runner](https://bun.sh/docs/cli/test) for complete documentation of `bun test`. # Mark a test as a "todo" with the Bun test runner Source: https://bun.com/docs/guides/test/todo-tests To remind yourself to write a test later, use the `test.todo` function. There's no need to provide a test implementation. ```ts test.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import { test, expect } from "bun:test"; // write this later test.todo("unimplemented feature"); ``` *** The output of `bun test` indicates how many `todo` tests were encountered. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun test ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} test.test.ts: ✓ add [0.03ms] ✓ multiply [0.02ms] ✎ unimplemented feature 2 pass 1 todo 0 fail 2 expect() calls Ran 3 tests across 1 files. [74.00ms] ``` *** Optionally, you can provide a test implementation. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} import { test, expect } from "bun:test"; test.todo("unimplemented feature", () => { expect(Bun.isAwesome()).toBe(true); }); ``` *** If an implementation is provided, it will not be run unless the `--todo` flag is passed. If the `--todo` flag is passed, the test will be executed and *expected to fail* by test runner! If a todo test passes, the `bun test` run will return a non-zero exit code to signal the failure. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun test --todo ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} my.test.ts: ✗ unimplemented feature ^ this test is marked as todo but passes. Remove `.todo` or check that test is correct. 0 pass 1 fail 1 expect() calls $ echo $? 1 # this is the exit code of the previous command ``` *** See also: * [Skip a test](/guides/test/skip-tests) * [Docs > Test runner > Writing tests](https://bun.sh/docs/test/writing) # Update snapshots in `bun test` Source: https://bun.com/docs/guides/test/update-snapshots Bun's test runner supports Jest-style snapshot testing via `.toMatchSnapshot()`. ```ts snap.test.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import { test, expect } from "bun:test"; test("snapshot", () => { expect({ foo: "bar" }).toMatchSnapshot(); }); ``` *** The first time this test is executed, Bun will write a snapshot file to disk in a directory called `__snapshots__` that lives alongside the test file. ```txt File Tree icon="folder-tree" theme={"theme":{"light":"github-light","dark":"dracula"}} test ├── __snapshots__ │   └── snap.test.ts.snap └── snap.test.ts ``` *** To regenerate snapshots, use the `--update-snapshots` flag. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun test --update-snapshots ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} test/snap.test.ts: ✓ snapshot [0.86ms] 1 pass 0 fail snapshots: +1 added # the snapshot was regenerated 1 expect() calls Ran 1 tests across 1 files. [102.00ms] ``` *** See [Docs > Test Runner > Snapshots](https://bun.sh/docs/test/snapshots) for complete documentation on snapshots with the Bun test runner. # Run tests in watch mode with Bun Source: https://bun.com/docs/guides/test/watch-mode Use the `--watch` flag to run your tests in watch mode. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun test --watch ``` *** This will restart the running Bun process whenever a file change is detected. It's fast. In this example, the editor is configured to save the file on every keystroke. ![Running tests in watch mode in Bun](https://github.com/oven-sh/bun/assets/3084745/dc49a36e-ba82-416f-b960-1c883a924248) *** See [Docs > Test Runner](https://bun.sh/docs/cli/test) for complete documentation on the test runner. # Encode and decode base64 strings Source: https://bun.com/docs/guides/util/base64 Bun implements the Web-standard [`atob`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/atob) and [`btoa`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/btoa) functions for encoding and decoding base64 strings. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const data = "hello world"; const encoded = btoa(data); // => "aGVsbG8gd29ybGQ=" const decoded = atob(encoded); // => "hello world" ``` *** See [Docs > Web APIs](https://bun.com/docs/runtime/web-apis) for a complete breakdown of the Web APIs implemented in Bun. # Check if two objects are deeply equal Source: https://bun.com/docs/guides/util/deep-equals Check if two objects are deeply equal. This is used internally by `expect().toEqual()` in Bun's [test runner](https://bun.com/docs/test/writing). ```ts index.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} const a = { a: 1, b: 2, c: { d: 3 } }; const b = { a: 1, b: 2, c: { d: 3 } }; Bun.deepEquals(a, b); // true ``` *** Pass `true` as a third argument to enable strict mode. This is used internally by `expect().toStrictEqual()` in Bun's [test runner](https://bun.com/docs/test/writing). The following examples would return `true` in non-strict mode but `false` in strict mode. ```ts index.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} // undefined values Bun.deepEquals({}, { a: undefined }, true); // false // undefined in arrays Bun.deepEquals(["asdf"], ["asdf", undefined], true); // false // sparse arrays Bun.deepEquals([, 1], [undefined, 1], true); // false // object literals vs instances w/ same properties class Foo { a = 1; } Bun.deepEquals(new Foo(), { a: 1 }, true); // false ``` *** See [Docs > API > Utils](https://bun.com/docs/api/utils) for more useful utilities. # Compress and decompress data with DEFLATE Source: https://bun.com/docs/guides/util/deflate Use `Bun.deflateSync()` to compress a `Uint8Array` with DEFLATE. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const data = Buffer.from("Hello, world!"); const compressed = Bun.deflateSync("Hello, world!"); // => Uint8Array const decompressed = Bun.inflateSync(compressed); // => Uint8Array ``` *** See [Docs > API > Utils](https://bun.com/docs/api/utils) for more useful utilities. # Detect when code is executed with Bun Source: https://bun.com/docs/guides/util/detect-bun The recommended way to conditionally detect when code is being executed with `bun` is to check for the existence of the `Bun` global. This is similar to how you'd check for the existence of the `window` variable to detect when code is being executed in a browser. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} if (typeof Bun !== "undefined") { // this code will only run when the file is run with Bun } ``` *** In TypeScript environments, the previous approach will result in a type error unless `@types/bun` is installed. To avoid this, you can check `process.versions` instead. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} if (process.versions.bun) { // this code will only run when the file is run with Bun } ``` # Check if the current file is the entrypoint Source: https://bun.com/docs/guides/util/entrypoint Bun provides a handful of module-specific utilities on the [`import.meta`](https://bun.com/docs/api/import-meta) object. Use `import.meta.main` to check if the current file is the entrypoint of the current process. ```ts index.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} if (import.meta.main) { // this file is directly executed with `bun run` } else { // this file is being imported by another file } ``` *** See [Docs > API > import.meta](https://bun.com/docs/api/import-meta) for complete documentation. # Escape an HTML string Source: https://bun.com/docs/guides/util/escape-html The `Bun.escapeHTML()` utility can be used to escape HTML characters in a string. The following replacements are made. * `"` becomes `"""` * `&` becomes `"&"` * `'` becomes `"'"` * `<` becomes `"<"` * `>` becomes `">"` This function is optimized for large input. Non-string types will be converted to a string before escaping. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} Bun.escapeHTML(""); // <script>alert('Hello World!')</script> ``` *** See [Docs > API > Utils](https://bun.com/docs/api/utils) for more useful utilities. # Convert a file URL to an absolute path Source: https://bun.com/docs/guides/util/file-url-to-path Use `Bun.fileURLToPath()` to convert a `file://` URL to an absolute path. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} Bun.fileURLToPath("file:///path/to/file.txt"); // => "/path/to/file.txt" ``` *** See [Docs > API > Utils](https://bun.com/docs/api/utils) for more useful utilities. # Compress and decompress data with gzip Source: https://bun.com/docs/guides/util/gzip Use `Bun.gzipSync()` to compress a `Uint8Array` with gzip. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const data = Buffer.from("Hello, world!"); const compressed = Bun.gzipSync(data); // => Uint8Array const decompressed = Bun.gunzipSync(compressed); // => Uint8Array ``` *** See [Docs > API > Utils](https://bun.com/docs/api/utils) for more useful utilities. # Hash a password Source: https://bun.com/docs/guides/util/hash-a-password The `Bun.password.hash()` function provides a fast, built-in mechanism for securely hashing passwords in Bun. No third-party dependencies are required. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const password = "super-secure-pa$$word"; const hash = await Bun.password.hash(password); // => $argon2id$v=19$m=65536,t=2,p=1$tFq+9AVr1bfPxQdh6E8DQRhEXg/M/... ``` *** By default, this uses the [Argon2id](https://en.wikipedia.org/wiki/Argon2) algorithm. Pass a second argument to `Bun.password.hash()` to use a different algorithm or configure the hashing parameters. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const password = "super-secure-pa$$word"; // use argon2 (default) const argonHash = await Bun.password.hash(password, { memoryCost: 4, // memory usage in kibibytes timeCost: 3, // the number of iterations }); ``` *** Bun also implements the [bcrypt](https://en.wikipedia.org/wiki/Bcrypt) algorithm. Specify `algorithm: "bcrypt"` to use it. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} // use bcrypt const bcryptHash = await Bun.password.hash(password, { algorithm: "bcrypt", cost: 4, // number between 4-31 }); ``` *** Use `Bun.password.verify()` to verify a password. The algorithm and its parameters are stored in the hash itself, so re-specifying configuration is unnecessary. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const password = "super-secure-pa$$word"; const hash = await Bun.password.hash(password); const isMatch = await Bun.password.verify(password, hash); // => true ``` *** See [Docs > API > Hashing](https://bun.com/docs/api/hashing#bun-password) for complete documentation. # Get the directory of the current file Source: https://bun.com/docs/guides/util/import-meta-dir Bun provides a handful of module-specific utilities on the [`import.meta`](https://bun.com/docs/api/import-meta) object. ```ts /a/b/c.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import.meta.dir; // => "/a/b" ``` *** See [Docs > API > import.meta](https://bun.com/docs/api/import-meta) for complete documentation. # Get the file name of the current file Source: https://bun.com/docs/guides/util/import-meta-file Bun provides a handful of module-specific utilities on the [`import.meta`](https://bun.com/docs/api/import-meta) object. Use `import.meta.file` to retrieve the name of the current file. ```ts /a/b/c.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import.meta.file; // => "c.ts" ``` *** See [Docs > API > import.meta](https://bun.com/docs/api/import-meta) for complete documentation. # Get the absolute path of the current file Source: https://bun.com/docs/guides/util/import-meta-path Bun provides a handful of module-specific utilities on the [`import.meta`](https://bun.com/docs/api/import-meta) object. Use `import.meta.path` to retrieve the absolute path of the current file. ```ts /a/b/c.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import.meta.path; // => "/a/b/c.ts" ``` *** See [Docs > API > import.meta](https://bun.com/docs/api/import-meta) for complete documentation. # Generate a UUID Source: https://bun.com/docs/guides/util/javascript-uuid Use `crypto.randomUUID()` to generate a UUID v4. This API works in Bun, Node.js, and browsers. It requires no dependencies. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} crypto.randomUUID(); // => "123e4567-e89b-12d3-a456-426614174000" ``` *** In Bun, you can also use `Bun.randomUUIDv7()` to generate a [UUID v7](https://www.ietf.org/archive/id/draft-peabody-dispatch-new-uuid-format-01.html). ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} Bun.randomUUIDv7(); // => "0196a000-bb12-7000-905e-8039f5d5b206" ``` *** See [Docs > API > Utils](https://bun.com/docs/api/utils) for more useful utilities. # Get the absolute path to the current entrypoint Source: https://bun.com/docs/guides/util/main The `Bun.main` property contains the absolute path to the current entrypoint. ```ts foo.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} console.log(Bun.main); ``` ```ts index.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} import "./foo.ts"; ``` *** The printed path corresponds to the file that is executed with `bun run`. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun run index.ts ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} /path/to/index.ts ``` ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun run foo.ts ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} /path/to/foo.ts ``` *** See [Docs > API > Utils](https://bun.com/docs/api/utils) for more useful utilities. # Convert an absolute path to a file URL Source: https://bun.com/docs/guides/util/path-to-file-url Use `Bun.pathToFileURL()` to convert an absolute path to a `file://` URL. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} Bun.pathToFileURL("/path/to/file.txt"); // => "file:///path/to/file.txt" ``` *** See [Docs > API > Utils](https://bun.com/docs/api/utils) for more useful utilities. # Sleep for a fixed number of milliseconds Source: https://bun.com/docs/guides/util/sleep The `Bun.sleep` method provides a convenient way to create a void `Promise` that resolves in a fixed number of milliseconds. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} // sleep for 1 second await Bun.sleep(1000); ``` *** Internally, this is equivalent to the following snippet that uses [`setTimeout`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout). ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} await new Promise(resolve => setTimeout(resolve, ms)); ``` *** See [Docs > API > Utils](https://bun.com/docs/api/utils) for more useful utilities. # Get the current Bun version Source: https://bun.com/docs/guides/util/version Get the current version of Bun in a semver format. ```ts index.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} Bun.version; // => "1.3.1" ``` *** Get the exact `git` commit of [`oven-sh/bun`](https://github.com/oven-sh/bun) that was compiled to produce this Bun binary. ```ts index.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} Bun.revision; // => "49231b2cb9aa48497ab966fc0bb6b742dacc4994" ``` *** See [Docs > API > Utils](https://bun.com/docs/api/utils) for more useful utilities. # Get the path to an executable bin file Source: https://bun.com/docs/guides/util/which-path-to-executable-bin `Bun.which` is a utility function to find the absolute path of an executable file. It is similar to the `which` command in Unix-like systems. ```ts foo.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} Bun.which("sh"); // => "/bin/sh" Bun.which("notfound"); // => null Bun.which("bun"); // => "/home/user/.bun/bin/bun" ``` *** See [Docs > API > Utils](https://bun.com/docs/api/utils#bun-which) for complete documentation. # Enable compression for WebSocket messages Source: https://bun.com/docs/guides/websocket/compression Per-message compression can be enabled with the `perMessageDeflate` parameter. When set, all messages will be compressed using the [permessage-deflate](https://tools.ietf.org/html/rfc7692) WebSocket extension. ```ts server.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} Bun.serve({ // ... websocket: { // enable compression perMessageDeflate: true, }, }); ``` *** To enable compression for individual messages, pass `true` as the second parameter to `ws.send()`. ```ts server.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} Bun.serve({ // ... websocket: { async message(ws, message) { // send a compressed message ws.send(message, true); }, }, }); ``` # Set per-socket contextual data on a WebSocket Source: https://bun.com/docs/guides/websocket/context When building a WebSocket server, it's typically necessary to store some identifying information or context associated with each connected client. With \[Bun.serve()]\([https://bun.com/docs/api/websockets](https://bun.com/docs/api/websockets) contextual-data), this "contextual data" is set when the connection is initially upgraded by passing a `data` parameter in the `server.upgrade()` call. ```ts server.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} Bun.serve<{ socketId: number }>({ fetch(req, server) { const success = server.upgrade(req, { data: { socketId: Math.random(), }, }); if (success) return undefined; // handle HTTP request normally // ... }, websocket: { // define websocket handlers async message(ws, message) { // the contextual data is available as the `data` property // on the WebSocket instance console.log(`Received ${message} from ${ws.data.socketId}}`); }, }, }); ``` *** It's common to read cookies/headers from the incoming request to identify the connecting client. ```ts server.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} type WebSocketData = { createdAt: number; token: string; userId: string; }; // TypeScript: specify the type of `data` Bun.serve({ async fetch(req, server) { // use a library to parse cookies const cookies = parseCookies(req.headers.get("Cookie")); const token = cookies["X-Token"]; const user = await getUserFromToken(token); const upgraded = server.upgrade(req, { data: { createdAt: Date.now(), token: cookies["X-Token"], userId: user.id, }, }); if (upgraded) return undefined; }, websocket: { async message(ws, message) { // save the message to a database await saveMessageToDatabase({ message: String(message), userId: ws.data.userId, }); }, }, }); ``` # Build a publish-subscribe WebSocket server Source: https://bun.com/docs/guides/websocket/pubsub Bun's server-side `WebSocket` API provides a native pub-sub API. Sockets can be subscribed to a set of named channels using `socket.subscribe()`; messages can be published to a channel using `socket.publish(, )`. This code snippet implements a simple single-channel chat server. ```ts server.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} const server = Bun.serve<{ username: string }>({ fetch(req, server) { const cookies = req.headers.get("cookie"); const username = getUsernameFromCookies(cookies); const success = server.upgrade(req, { data: { username } }); if (success) return undefined; return new Response("Hello world"); }, websocket: { open(ws) { const msg = `${ws.data.username} has entered the chat`; ws.subscribe("the-group-chat"); server.publish("the-group-chat", msg); }, message(ws, message) { // the server re-broadcasts incoming messages to everyone server.publish("the-group-chat", `${ws.data.username}: ${message}`); }, close(ws) { const msg = `${ws.data.username} has left the chat`; server.publish("the-group-chat", msg); ws.unsubscribe("the-group-chat"); }, }, }); console.log(`Listening on ${server.hostname}:${server.port}`); ``` # Build a simple WebSocket server Source: https://bun.com/docs/guides/websocket/simple Start a simple WebSocket server using [`Bun.serve`](https://bun.com/docs/api/http). Inside `fetch`, we attempt to upgrade incoming `ws:` or `wss:` requests to WebSocket connections. ```ts server.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} const server = Bun.serve<{ authToken: string }>({ fetch(req, server) { const success = server.upgrade(req); if (success) { // Bun automatically returns a 101 Switching Protocols // if the upgrade succeeds return undefined; } // handle HTTP request normally return new Response("Hello world!"); }, websocket: { // this is called when a message is received async message(ws, message) { console.log(`Received ${message}`); // send back a message ws.send(`You said: ${message}`); }, }, }); console.log(`Listening on ${server.hostname}:${server.port}`); ``` # Append content to a file Source: https://bun.com/docs/guides/write-file/append Bun implements the `node:fs` module, which includes the `fs.appendFile` and `fs.appendFileSync` functions for appending content to files. *** You can use `fs.appendFile` to asynchronously append data to a file, creating the file if it does not yet exist. The content can be a string or a `Buffer`. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} import { appendFile } from "node:fs/promises"; await appendFile("message.txt", "data to append"); ``` *** To use the non-`Promise` API: ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} import { appendFile } from "node:fs"; appendFile("message.txt", "data to append", err => { if (err) throw err; console.log('The "data to append" was appended to file!'); }); ``` *** To specify the encoding of the content: ```js theme={"theme":{"light":"github-light","dark":"dracula"}} import { appendFile } from "node:fs"; appendFile("message.txt", "data to append", "utf8", callback); ``` *** To append the data synchronously, use `fs.appendFileSync`: ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} import { appendFileSync } from "node:fs"; appendFileSync("message.txt", "data to append", "utf8"); ``` *** See the [Node.js documentation](https://nodejs.org/api/fs.html#fspromisesappendfilepath-data-options) for more information. # Write a string to a file Source: https://bun.com/docs/guides/write-file/basic This code snippet writes a string to disk at a particular *absolute path*. It uses the fast [`Bun.write()`](https://bun.com/docs/api/file-io#writing-files-bun-write) API to efficiently write data to disk. The first argument is a *destination*; the second is the *data* to write. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const path = "/path/to/file.txt"; await Bun.write(path, "Lorem ipsum"); ``` *** Any relative paths will be resolved relative to the project root (the nearest directory containing a `package.json` file). ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const path = "./file.txt"; await Bun.write(path, "Lorem ipsum"); ``` *** You can pass a `BunFile` as the destination. `Bun.write()` will write the data to its associated path. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const path = Bun.file("./file.txt"); await Bun.write(path, "Lorem ipsum"); ``` *** `Bun.write()` returns the number of bytes written to disk. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const path = "./file.txt"; const bytes = await Bun.write(path, "Lorem ipsum"); // => 11 ``` *** See [Docs > API > File I/O](https://bun.com/docs/api/file-io#writing-files-bun-write) for complete documentation of `Bun.write()`. # Write a Blob to a file Source: https://bun.com/docs/guides/write-file/blob This code snippet writes a `Blob` to disk at a particular path. It uses the fast [`Bun.write()`](https://bun.com/docs/api/file-io#writing-files-bun-write) API to efficiently write data to disk. The first argument is a *destination*, like an absolute path or `BunFile` instance. The second argument is the *data* to write. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const path = "/path/to/file.txt"; await Bun.write(path, "Lorem ipsum"); ``` *** The `BunFile` class extends `Blob`, so you can pass a `BunFile` directly into `Bun.write()` as well. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const path = "./out.txt"; const data = Bun.file("./in.txt"); // write the contents of ./in.txt to ./out.txt await Bun.write(path, data); ``` *** See [Docs > API > File I/O](https://bun.com/docs/api/file-io#writing-files-bun-write) for complete documentation of `Bun.write()`. # Write a file to stdout Source: https://bun.com/docs/guides/write-file/cat Bun exposes `stdout` as a `BunFile` with the `Bun.stdout` property. This can be used as a destination for [`Bun.write()`](https://bun.com/docs/api/file-io#writing-files-bun-write). This code writes a file to `stdout` similar to the `cat` command in Unix. ```ts cat.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} const path = "/path/to/file.txt"; const file = Bun.file(path); await Bun.write(Bun.stdout, file); ``` *** See [Docs > API > File I/O](https://bun.com/docs/api/file-io#writing-files-bun-write) for complete documentation of `Bun.write()`. # Copy a file to another location Source: https://bun.com/docs/guides/write-file/file-cp This code snippet copies a file to another location on disk. It uses the fast [`Bun.write()`](https://bun.com/docs/api/file-io#writing-files-bun-write) API to efficiently write data to disk. The first argument is a *destination*, like an absolute path or `BunFile` instance. The second argument is the *data* to write. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const file = Bun.file("/path/to/original.txt"); await Bun.write("/path/to/copy.txt", file); ``` *** See [Docs > API > File I/O](https://bun.com/docs/api/file-io#writing-files-bun-write) for complete documentation of `Bun.write()`. # Write a file incrementally Source: https://bun.com/docs/guides/write-file/filesink Bun provides an API for incrementally writing to a file. This is useful for writing large files, or for writing to a file over a long period of time. Call `.writer()` on a `BunFile` to retrieve a `FileSink` instance. This instance can be used to efficiently buffer data and periodically "flush" it to disk. You can write & flush many times. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const file = Bun.file("/path/to/file.txt"); const writer = file.writer(); writer.write("lorem"); writer.write("ipsum"); writer.write("dolor"); writer.flush(); // continue writing & flushing ``` *** The `.write()` method can accept strings or binary data. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} w.write("hello"); w.write(Buffer.from("there")); w.write(new Uint8Array([0, 255, 128])); writer.flush(); ``` *** The `FileSink` will also auto-flush when its internal buffer is full. You can configure the buffer size with the `highWaterMark` option. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const file = Bun.file("/path/to/file.txt"); const writer = file.writer({ highWaterMark: 1024 * 1024 }); // 1MB ``` *** When you're done writing to the file, call `.end()` to auto-flush the buffer and close the file. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} writer.end(); ``` *** Full documentation: [FileSink](https://bun.com/docs/api/file-io#incremental-writing-with-filesink). # Write a Response to a file Source: https://bun.com/docs/guides/write-file/response This code snippet writes a `Response` to disk at a particular path. Bun will consume the `Response` body according to its `Content-Type` header. It uses the fast [`Bun.write()`](https://bun.com/docs/api/file-io#writing-files-bun-write) API to efficiently write data to disk. The first argument is a *destination*, like an absolute path or `BunFile` instance. The second argument is the *data* to write. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const result = await fetch("https://bun.com"); const path = "./file.txt"; await Bun.write(path, result); ``` *** See [Docs > API > File I/O](https://bun.com/docs/api/file-io#writing-files-bun-write) for complete documentation of `Bun.write()`. # Write to stdout Source: https://bun.com/docs/guides/write-file/stdout The `console.log` function writes to `stdout`. It will automatically append a line break at the end of the printed data. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} console.log("Lorem ipsum"); ``` *** For more advanced use cases, Bun exposes `stdout` as a `BunFile` via the `Bun.stdout` property. This can be used as a destination for [`Bun.write()`](https://bun.com/docs/api/file-io#writing-files-bun-write). ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} await Bun.write(Bun.stdout, "Lorem ipsum"); ``` *** See [Docs > API > File I/O](https://bun.com/docs/api/file-io#writing-files-bun-write) for complete documentation of `Bun.write()`. # Write a ReadableStream to a file Source: https://bun.com/docs/guides/write-file/stream To write a `ReadableStream` to disk, first create a `Response` instance from the stream. This `Response` can then be written to disk using [`Bun.write()`](https://bun.com/docs/api/file-io#writing-files-bun-write). ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const stream: ReadableStream = ...; const path = "./file.txt"; const response = new Response(stream); await Bun.write(path, response); ``` *** See [Docs > API > File I/O](https://bun.com/docs/api/file-io#writing-files-bun-write) for complete documentation of `Bun.write()`. # Delete a file Source: https://bun.com/docs/guides/write-file/unlink The `Bun.file()` function accepts a path and returns a `BunFile` instance. Use the `.delete()` method to delete the file. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const path = "/path/to/file.txt"; const file = Bun.file(path); await file.delete(); ``` *** See [Docs > API > File I/O](https://bun.com/docs/api/file-io#reading-files-bun-file) for complete documentation of `Bun.file()`. # Welcome to Bun Source: https://bun.com/docs/index Bun is an all-in-one toolkit for developing modern JavaScript/TypeScript applications. Get started with bun run } > A fast JavaScript runtime designed as a drop-in replacement for Node.js Get started with bun install } > Install packages up to 30x faster than npm with a global cache and workspaces Get started with bun test } > Jest-compatible, TypeScript-first tests with snapshots, DOM, and watch mode Get started with bun build } > Bundle TypeScript, JSX, React & CSS for both browsers and servers *** ## Get Started Bun ships as a single, dependency-free binary and includes a runtime, package manager, test runner, and bundler. New to Bun? Supported platforms and all install methods. Hello world in minutes with Bun.serve. *** ## What's Inside * Runtime: Execute JavaScript/TypeScript files and package scripts with near-zero overhead. * Package Manager: Fast installs, workspaces, overrides, and audits with `bun install`. * Test Runner: Jest-compatible, TypeScript-first tests with snapshots, DOM, and watch mode. * Bundler: Native bundling for JS/TS/JSX with splitting, plugins, and HTML imports. Explore each area using the cards above. Each section is structured with an overview, quick examples, reference, and best practices for fast scanning and deep dives. *** ## What is Bun? Bun is an all-in-one toolkit for JavaScript and TypeScript apps. It ships as a single executable called `bun`. At its core is the *Bun runtime*, a fast JavaScript runtime designed as **a drop-in replacement for Node.js**. It's written in Zig and powered by JavaScriptCore under the hood, dramatically reducing startup times and memory usage. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun run index.tsx # TS and JSX supported out of the box ``` The `bun` command-line tool also implements a test runner, script runner, and Node.js-compatible package manager, all significantly faster than existing tools and usable in existing Node.js projects with little to no changes necessary. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun run start # run the `start` script bun install # install a package bun build ./index.tsx # bundle a project for browsers bun test # run tests bunx cowsay 'Hello, world!' # execute a package ``` ## What is a runtime? JavaScript (or, more formally, ECMAScript) is just a *specification* for a programming language. Anyone can write a JavaScript *engine* that ingests a valid JavaScript program and executes it. The two most popular engines in use today are V8 (developed by Google) and JavaScriptCore (developed by Apple). Both are open source. But most JavaScript programs don't run in a vacuum. They need a way to access the outside world to perform useful tasks. This is where *runtimes* come in. They implement additional APIs that are then made available to the JavaScript programs they execute. ### Browsers Notably, browsers ship with JavaScript runtimes that implement a set of Web-specific APIs that are exposed via the global `window` object. Any JavaScript code executed by the browser can use these APIs to implement interactive or dynamic behavior in the context of the current webpage. ### Node.js Similarly, Node.js is a JavaScript runtime that can be used in non-browser environments, like servers. JavaScript programs executed by Node.js have access to a set of Node.js-specific [globals](https://nodejs.org/api/globals.html) like `Buffer`, `process`, and `__dirname` in addition to built-in modules for performing OS-level tasks like reading/writing files (`node:fs`) and networking (`node:net`, `node:http`). Node.js also implements a CommonJS-based module system and resolution algorithm that pre-dates JavaScript's native module system. Bun is designed as a faster, leaner, more modern replacement for Node.js. ## Design goals Bun is designed from the ground-up with today's JavaScript ecosystem in mind. * **Speed**. Bun processes start [4x faster than Node.js](https://twitter.com/jarredsumner/status/1499225725492076544) currently (try it yourself!) * **TypeScript & JSX support**. You can directly execute `.jsx`, `.ts`, and `.tsx` files; Bun's transpiler converts these to vanilla JavaScript before execution. * **ESM & CommonJS compatibility**. The world is moving towards ES modules (ESM), but millions of packages on npm still require CommonJS. Bun recommends ES modules, but supports CommonJS. * **Web-standard APIs**. Bun implements standard Web APIs like `fetch`, `WebSocket`, and `ReadableStream`. Bun is powered by the JavaScriptCore engine, which is developed by Apple for Safari, so some APIs like [`Headers`](https://developer.mozilla.org/en-US/Web/API/Headers) and [`URL`](https://developer.mozilla.org/en-US/Web/API/URL) directly use [Safari's implementation](https://github.com/oven-sh/bun/blob/HEAD/src/bun.js/bindings/webcore/JSFetchHeaders.cpp). * **Node.js compatibility**. In addition to supporting Node-style module resolution, Bun aims for full compatibility with built-in Node.js globals (`process`, `Buffer`) and modules (`path`, `fs`, `http`, etc.) *This is an ongoing effort that is not complete.* Refer to the [compatibility page](/runtime/nodejs-compat) for the current status. Bun is more than a runtime. The long-term goal is to be a cohesive, infrastructural toolkit for building apps with JavaScript/TypeScript, including a package manager, transpiler, bundler, script runner, test runner, and more. # Installation Source: https://bun.com/docs/installation Install Bun ## Overview Bun ships as a single, dependency-free executable. You can install it via script, package manager, or Docker across macOS, Linux, and Windows. After installation, verify with `bun --version` and `bun --revision`. ## Installation ```bash curl icon="globe" theme={"theme":{"light":"github-light","dark":"dracula"}} curl -fsSL https://bun.com/install | bash ``` **Linux users**  The `unzip` package is required to install Bun. Use `sudo apt install unzip` to install the unzip package. Kernel version 5.6 or higher is strongly recommended, but the minimum is 5.1. Use `uname -r` to check Kernel version. ```powershell PowerShell icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} powershell -c "irm bun.sh/install.ps1|iex" ``` Bun requires Windows 10 version 1809 or later. For support and discussion, please join the **#windows** channel on our [Discord](https://discord.gg/bun). ```bash npm icon="npm" theme={"theme":{"light":"github-light","dark":"dracula"}} npm install -g bun # the last `npm` command you'll ever need ``` ```bash Homebrew icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/homebrew.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=614be850c39990ccb245ec8f1fe1b1a1" theme={"theme":{"light":"github-light","dark":"dracula"}} brew install oven-sh/bun/bun ``` ```bash Scoop icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} scoop install bun ``` Bun provides a Docker image that supports both Linux x64 and arm64. ```bash Docker icon="docker" theme={"theme":{"light":"github-light","dark":"dracula"}} docker pull oven/bun docker run --rm --init --ulimit memlock=-1:-1 oven/bun ``` ### Image Variants There are also image variants for different operating systems: ```bash Docker icon="docker" theme={"theme":{"light":"github-light","dark":"dracula"}} docker pull oven/bun:debian docker pull oven/bun:slim docker pull oven/bun:distroless docker pull oven/bun:alpine ``` To check that Bun was installed successfully, open a new terminal window and run: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun --version # Output: 1.x.y # See the precise commit of `oven-sh/bun` that you're using bun --revision # Output: 1.x.y+b7982ac13189 ``` If you've installed Bun but are seeing a `command not found` error, you may have to manually add the installation directory (`~/.bun/bin`) to your `PATH`. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} echo $SHELL # /bin/zsh or /bin/bash or /bin/fish ``` * For bash: `~/.bashrc` * For zsh: `~/.zshrc` * For fish: `~/.config/fish/config.fish` Add this line to your configuration file: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} export BUN_INSTALL="$HOME/.bun" export PATH="$BUN_INSTALL/bin:$PATH" ``` ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} source ~/.bashrc # or ~/.zshrc ``` ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} & "$env:USERPROFILE\.bun\bin\bun" --version ``` If the command runs successfully but `bun --version` is not recognized, it means that bun is not in your system's PATH. To fix this, open a Powershell terminal and run the following command: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} [System.Environment]::SetEnvironmentVariable( "Path", [System.Environment]::GetEnvironmentVariable("Path", "User") + ";$env:USERPROFILE\.bun\bin", [System.EnvironmentVariableTarget]::User ) ``` After running the command, restart your terminal and test with `bun --version` ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun --version ``` *** ## Upgrading Once installed, the binary can upgrade itself: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun upgrade ``` **Homebrew users**
To avoid conflicts with Homebrew, use `brew upgrade bun` instead. **Scoop users**
To avoid conflicts with Scoop, use `scoop update bun` instead.
*** ## Canary Builds [-> View canary build](https://github.com/oven-sh/bun/releases/tag/canary) Bun automatically releases an (untested) canary build on every commit to main. To upgrade to the latest canary build: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} # Upgrade to latest canary bun upgrade --canary # Switch back to stable bun upgrade --stable ``` The canary build is useful for testing new features and bug fixes before they're released in a stable build. To help the Bun team fix bugs faster, canary builds automatically upload crash reports to Bun's team. *** ## Installing Older Versions Since Bun is a single binary, you can install older versions by re-running the installer script with a specific version. To install a specific version, pass the git tag to the install script: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} curl -fsSL https://bun.com/install | bash -s "bun-v1.3.1" ``` On Windows, pass the version number to the PowerShell install script: ```powershell PowerShell icon="windows" theme={"theme":{"light":"github-light","dark":"dracula"}} iex "& {$(irm https://bun.com/install.ps1)} -Version 1.3.1" ``` *** ## Direct Downloads To download Bun binaries directly, visit the [releases page on GitHub](https://github.com/oven-sh/bun/releases). ### Latest Version Downloads Standard Linux x64 binary For older CPUs without AVX2 Standard Windows binary For older CPUs without AVX2 Apple Silicon (M1/M2/M3) Intel Macs ARM64 Linux systems ### Musl Binaries For distributions without `glibc` (Alpine Linux, Void Linux): * [Linux x64 musl](https://github.com/oven-sh/bun/releases/latest/download/bun-linux-x64-musl.zip) * [Linux x64 musl baseline](https://github.com/oven-sh/bun/releases/latest/download/bun-linux-x64-musl-baseline.zip) * [Linux ARM64 musl](https://github.com/oven-sh/bun/releases/latest/download/bun-linux-aarch64-musl.zip) If you encounter an error like `bun: /lib/x86_64-linux-gnu/libm.so.6: version GLIBC_2.29 not found`, try using the musl binary. Bun's install script automatically chooses the correct binary for your system. *** ## CPU Requirements Bun has specific CPU requirements based on the binary you're using: **x64 binaries** target the Haswell CPU architecture (AVX and AVX2 instructions required) | Platform | Intel Requirement | AMD Requirement | | -------- | ------------------------------- | ------------------ | | x64 | Haswell (4th gen Core) or newer | Excavator or newer | **x64-baseline binaries** target the Nehalem architecture for older CPUs | Platform | Intel Requirement | AMD Requirement | | ------------ | ------------------------------- | ------------------ | | x64-baseline | Nehalem (1st gen Core) or newer | Bulldozer or newer | Baseline builds are slower than regular builds. Use them only if you encounter an "Illegal Instruction" error. Bun does not support CPUs older than the baseline target, which mandates the SSE4.2 extension. macOS requires version 13.0 or later. *** ## Uninstall To remove Bun from your system: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} rm -rf ~/.bun ``` ```powershell PowerShell icon="windows" theme={"theme":{"light":"github-light","dark":"dracula"}} powershell -c ~\.bun\uninstall.ps1 ``` ```bash npm icon="npm" theme={"theme":{"light":"github-light","dark":"dracula"}} npm uninstall -g bun ``` ```bash Homebrew icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/homebrew.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=614be850c39990ccb245ec8f1fe1b1a1" theme={"theme":{"light":"github-light","dark":"dracula"}} brew uninstall bun ``` ```bash Scoop icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} scoop uninstall bun ``` # bunx Source: https://bun.com/docs/pm/bunx Run packages from npm `bunx` is an alias for `bun x`. The `bunx` CLI will be auto-installed when you install `bun`. Use `bunx` to auto-install and run packages from `npm`. It's Bun's equivalent of `npx` or `yarn dlx`. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bunx cowsay "Hello world!" ``` ⚡️ **Speed** — With Bun's fast startup times, `bunx` is [roughly 100x faster](https://twitter.com/jarredsumner/status/1606163655527059458) than `npx` for locally installed packages. Packages can declare executables in the `"bin"` field of their `package.json`. These are known as *package executables* or *package binaries*. ```json package.json icon="file-json" theme={"theme":{"light":"github-light","dark":"dracula"}} { // ... other fields "name": "my-cli", "bin": { "my-cli": "dist/index.js" } } ``` These executables are commonly plain JavaScript files marked with a [shebang line](https://en.wikipedia.org/wiki/Shebang_\(Unix\)) to indicate which program should be used to execute them. The following file indicates that it should be executed with `node`. ```ts dist/index.js icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/javascript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=81efd0ad0d779debfa163bfd906ef6a6" theme={"theme":{"light":"github-light","dark":"dracula"}} #!/usr/bin/env node console.log("Hello world!"); ``` These executables can be run with `bunx`, ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bunx my-cli ``` As with `npx`, `bunx` will check for a locally installed package first, then fall back to auto-installing the package from `npm`. Installed packages will be stored in Bun's global cache for future use. ## Arguments and flags To pass additional command-line flags and arguments through to the executable, place them after the executable name. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bunx my-cli --foo bar ``` ## Shebangs By default, Bun respects shebangs. If an executable is marked with `#!/usr/bin/env node`, Bun will spin up a `node` process to execute the file. However, in some cases it may be desirable to run executables using Bun's runtime, even if the executable indicates otherwise. To do so, include the `--bun` flag. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bunx --bun my-cli ``` The `--bun` flag must occur *before* the executable name. Flags that appear *after* the name are passed through to the executable. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bunx --bun my-cli # good bunx my-cli --bun # bad ``` ## Package flag **`--package ` or `-p `** - Run binary from specific package. Useful when binary name differs from package name: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bunx -p renovate renovate-config-validator bunx --package @angular/cli ng ``` To force bun to always be used with a script, use a shebang. ```ts dist/index.js icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/javascript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=81efd0ad0d779debfa163bfd906ef6a6" theme={"theme":{"light":"github-light","dark":"dracula"}} #!/usr/bin/env bun ``` # Catalogs Source: https://bun.com/docs/pm/catalogs Share common dependency versions across multiple packages in a monorepo Catalogs in Bun provide a straightforward way to share common dependency versions across multiple packages in a monorepo. Rather than specifying the same versions repeatedly in each workspace package, you define them once in the root package.json and reference them consistently throughout your project. ## Overview Unlike traditional dependency management where each workspace package needs to independently specify versions, catalogs let you: 1. Define version catalogs in the root package.json 2. Reference these versions with a simple `catalog:` protocol 3. Update all packages simultaneously by changing the version in just one place This is especially useful in large monorepos where dozens of packages need to use the same version of key dependencies. ## How to Use Catalogs ### Directory Structure Example Consider a monorepo with the following structure: ``` my-monorepo/ ├── package.json ├── bun.lock └── packages/ ├── app/ │ └── package.json ├── ui/ │ └── package.json └── utils/ └── package.json ``` ### 1. Define Catalogs in Root package.json In your root-level `package.json`, add a `catalog` or `catalogs` field within the `workspaces` object: ```json package.json icon="file-json" theme={"theme":{"light":"github-light","dark":"dracula"}} { "name": "my-monorepo", "workspaces": { "packages": ["packages/*"], "catalog": { "react": "^19.0.0", "react-dom": "^19.0.0" }, "catalogs": { "testing": { "jest": "30.0.0", "testing-library": "14.0.0" } } } } ``` If you put `catalog` or `catalogs` at the top level of the `package.json` file, that will work too. ### 2. Reference Catalog Versions in Workspace Packages In your workspace packages, use the `catalog:` protocol to reference versions: ```json packages/app/package.json icon="file-json" theme={"theme":{"light":"github-light","dark":"dracula"}} { "name": "app", "dependencies": { "react": "catalog:", "react-dom": "catalog:", "jest": "catalog:testing" } } ``` ```json packages/ui/package.json icon="file-json" theme={"theme":{"light":"github-light","dark":"dracula"}} { "name": "ui", "dependencies": { "react": "catalog:", "react-dom": "catalog:" }, "devDependencies": { "jest": "catalog:testing", "testing-library": "catalog:testing" } } ``` ### 3. Run Bun Install Run `bun install` to install all dependencies according to the catalog versions. ## Catalog vs Catalogs Bun supports two ways to define catalogs: 1. **`catalog`** (singular): A single default catalog for commonly used dependencies ```json package.json icon="file-json" theme={"theme":{"light":"github-light","dark":"dracula"}} "catalog": { "react": "^19.0.0", "react-dom": "^19.0.0" } ``` Reference with simply `catalog:`: ```json packages/app/package.json icon="file-json" theme={"theme":{"light":"github-light","dark":"dracula"}} "dependencies": { "react": "catalog:" } ``` 2. **`catalogs`** (plural): Multiple named catalogs for grouping dependencies ```json package.json icon="file-json" theme={"theme":{"light":"github-light","dark":"dracula"}} "catalogs": { "testing": { "jest": "30.0.0" }, "ui": { "tailwind": "4.0.0" } } ``` Reference with `catalog:`: ```json packages/app/package.json icon="file-json" theme={"theme":{"light":"github-light","dark":"dracula"}} "dependencies": { "jest": "catalog:testing", "tailwind": "catalog:ui" } ``` ## Benefits of Using Catalogs * **Consistency**: Ensures all packages use the same version of critical dependencies * **Maintenance**: Update a dependency version in one place instead of across multiple package.json files * **Clarity**: Makes it obvious which dependencies are standardized across your monorepo * **Simplicity**: No need for complex version resolution strategies or external tools ## Real-World Example Here's a more comprehensive example for a React application: **Root package.json** ```json package.json icon="file-json" theme={"theme":{"light":"github-light","dark":"dracula"}} { "name": "react-monorepo", "workspaces": { "packages": ["packages/*"], "catalog": { "react": "^19.0.0", "react-dom": "^19.0.0", "react-router-dom": "^6.15.0" }, "catalogs": { "build": { "webpack": "5.88.2", "babel": "7.22.10" }, "testing": { "jest": "29.6.2", "react-testing-library": "14.0.0" } } }, "devDependencies": { "typescript": "5.1.6" } } ``` ```json packages/app/package.json icon="file-json" theme={"theme":{"light":"github-light","dark":"dracula"}} { "name": "app", "dependencies": { "react": "catalog:", "react-dom": "catalog:", "react-router-dom": "catalog:", "@monorepo/ui": "workspace:*", "@monorepo/utils": "workspace:*" }, "devDependencies": { "webpack": "catalog:build", "babel": "catalog:build", "jest": "catalog:testing", "react-testing-library": "catalog:testing" } } ``` ```json packages/ui/package.json icon="file-json" theme={"theme":{"light":"github-light","dark":"dracula"}} { "name": "@monorepo/ui", "dependencies": { "react": "catalog:", "react-dom": "catalog:" }, "devDependencies": { "jest": "catalog:testing", "react-testing-library": "catalog:testing" } } ``` ```json packages/utils/package.json icon="file-json" theme={"theme":{"light":"github-light","dark":"dracula"}} { "name": "@monorepo/utils", "dependencies": { "react": "catalog:" }, "devDependencies": { "jest": "catalog:testing" } } ``` ## Updating Versions To update versions across all packages, simply change the version in the root package.json: ```json package.json icon="file-json" theme={"theme":{"light":"github-light","dark":"dracula"}} "catalog": { "react": "^19.1.0", // Updated from ^19.0.0 "react-dom": "^19.1.0" // Updated from ^19.0.0 } ``` Then run `bun install` to update all packages. ## Lockfile Integration Bun's lockfile tracks catalog versions, making it easy to ensure consistent installations across different environments. The lockfile includes: * The catalog definitions from your package.json * The resolution of each cataloged dependency ```json bun.lock(excerpt) icon="file-json" theme={"theme":{"light":"github-light","dark":"dracula"}} { "lockfileVersion": 1, "workspaces": { "": { "name": "react-monorepo", }, "packages/app": { "name": "app", "dependencies": { "react": "catalog:", "react-dom": "catalog:", ... }, }, ... }, "catalog": { "react": "^19.0.0", "react-dom": "^19.0.0", ... }, "catalogs": { "build": { "webpack": "5.88.2", ... }, ... }, "packages": { ... } } ``` ## Limitations and Edge Cases * Catalog references must match a dependency defined in either `catalog` or one of the named `catalogs` * Empty strings and whitespace in catalog names are ignored (treated as default catalog) * Invalid dependency versions in catalogs will fail to resolve during `bun install` * Catalogs are only available within workspaces; they cannot be used outside the monorepo Bun's catalog system provides a powerful yet simple way to maintain consistency across your monorepo without introducing additional complexity to your workflow. ## Publishing When you run `bun publish` or `bun pm pack`, Bun automatically replaces `catalog:` references in your `package.json` with the resolved version numbers. The published package includes regular semver strings and no longer depends on your catalog definitions. # bun add Source: https://bun.com/docs/pm/cli/add Add packages to your project with Bun's fast package manager To add a particular package: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun add preact ``` To specify a version, version range, or tag: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun add zod@3.20.0 bun add zod@^3.0.0 bun add zod@latest ``` ## `--dev` **Alias** — `--development`, `-d`, `-D` To add a package as a dev dependency (`"devDependencies"`): ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun add --dev @types/react bun add -d @types/react ``` ## `--optional` To add a package as an optional dependency (`"optionalDependencies"`): ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun add --optional lodash ``` ## `--peer` To add a package as a peer dependency (`"peerDependencies"`): ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun add --peer @types/bun ``` ## `--exact` **Alias** — `-E` To add a package and pin to the resolved version, use `--exact`. This will resolve the version of the package and add it to your `package.json` with an exact version number instead of a version range. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun add react --exact bun add react -E ``` This will add the following to your `package.json`: ```json package.json icon="file-json" theme={"theme":{"light":"github-light","dark":"dracula"}} { "dependencies": { // without --exact "react": "^18.2.0", // this matches >= 18.2.0 < 19.0.0 // with --exact "react": "18.2.0" // this matches only 18.2.0 exactly } } ``` To view a complete list of options for this command: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun add --help ``` ## `--global` **Note** — This would not modify package.json of your current project folder. **Alias** - `bun add --global`, `bun add -g`, `bun install --global` and `bun install -g` To install a package globally, use the `-g`/`--global` flag. This will not modify the `package.json` of your current project. Typically this is used for installing command-line tools. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun add --global cowsay # or `bun add -g cowsay` cowsay "Bun!" ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} ______ < Bun! > ------ \ ^__^ \ (oo)\_______ (__)\ )\/\ ||----w | || || ``` ```toml bunfig.toml icon="settings" theme={"theme":{"light":"github-light","dark":"dracula"}} [install] # where `bun add --global` installs packages globalDir = "~/.bun/install/global" # where globally-installed package bins are linked globalBinDir = "~/.bun/bin" ``` ## Trusted dependencies Unlike other npm clients, Bun does not execute arbitrary lifecycle scripts for installed dependencies, such as `postinstall`. These scripts represent a potential security risk, as they can execute arbitrary code on your machine. To tell Bun to allow lifecycle scripts for a particular package, add the package to `trustedDependencies` in your package.json. ```json package.json icon="file-json" theme={"theme":{"light":"github-light","dark":"dracula"}} { "name": "my-app", "version": "1.0.0", "trustedDependencies": ["my-trusted-package"] // [!code ++] } ``` Bun reads this field and will run lifecycle scripts for `my-trusted-package`. ## Git dependencies To add a dependency from a public or private git repository: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun add git@github.com:moment/moment.git ``` To install private repositories, your system needs the appropriate SSH credentials to access the repository. Bun supports a variety of protocols, including [`github`](https://docs.npmjs.com/cli/v9/configuring-npm/package-json#github-urls), [`git`](https://docs.npmjs.com/cli/v9/configuring-npm/package-json#git-urls-as-dependencies), `git+ssh`, `git+https`, and many more. ```json package.json icon="file-json" theme={"theme":{"light":"github-light","dark":"dracula"}} { "dependencies": { "dayjs": "git+https://github.com/iamkun/dayjs.git", "lodash": "git+ssh://github.com/lodash/lodash.git#4.17.21", "moment": "git@github.com:moment/moment.git", "zod": "github:colinhacks/zod" } } ``` ## Tarball dependencies A package name can correspond to a publicly hosted `.tgz` file. During installation, Bun will download and install the package from the specified tarball URL, rather than from the package registry. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun add zod@https://registry.npmjs.org/zod/-/zod-3.21.4.tgz ``` This will add the following line to your `package.json`: ```json package.json icon="file-json" theme={"theme":{"light":"github-light","dark":"dracula"}} { "dependencies": { "zod": "https://registry.npmjs.org/zod/-/zod-3.21.4.tgz" } } ``` *** ## CLI Usage ```bash theme={"theme":{"light":"github-light","dark":"dracula"}} bun add <@version> ``` ### Dependency Management Don't install devDependencies. Alias: -p Exclude dev, optional, or peer dependencies from install Install globally. Alias: -g Add dependency to devDependencies. Alias: -d Add dependency to optionalDependencies Add dependency to peerDependencies Add the exact version instead of the ^ range. Alias: -E Only add dependencies to package.json if they are not already present ### Project Files & Lockfiles Write a yarn.lock file (yarn v1). Alias: -y Don't update package.json or save a lockfile Save to package.json (true by default) Disallow changes to lockfile Add to trustedDependencies in the project's package.json and install the package(s) Save a text-based lockfile Generate a lockfile without installing dependencies ### Installation Control Don't install anything Always request the latest versions from the registry & reinstall all dependencies. Alias:{" "} -f Skip verifying integrity of newly downloaded packages Skip lifecycle scripts in the project's package.json (dependency scripts are never run) Recursively analyze & install dependencies of files passed as arguments (using Bun's bundler). Alias: -a ### Network & Registry Provide a Certificate Authority signing certificate Same as --ca, but as a file path to the certificate Use a specific registry by default, overriding .npmrc, bunfig.toml, and environment variables Maximum number of concurrent network requests (default 48) ### Performance & Resource Platform-specific optimizations for installing dependencies. Possible values:{" "} clonefile (default), hardlink, symlink,{" "} copyfile Maximum number of concurrent jobs for lifecycle scripts (default 5) ### Caching Store & load cached data from a specific directory path Ignore manifest cache entirely ### Output & Logging Don't log anything Excessively verbose logging Disable the progress bar Don't print a summary ### Global Configuration & Context Specify path to config file (bunfig.toml). Alias: -c Set a specific current working directory ### Help Print this help menu. Alias: -h # bun audit Source: https://bun.com/docs/pm/cli/audit Check your installed packages for known security vulnerabilities Run the command in a project with a `bun.lock` file: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun audit ``` Bun sends the list of installed packages and versions to NPM, and prints a report of any vulnerabilities that were found. Packages installed from registries other than the default registry are skipped. If no vulnerabilities are found, the command prints: ``` No vulnerabilities found ``` When vulnerabilities are detected, each affected package is listed along with the severity, a short description and a link to the advisory. At the end of the report Bun prints a summary and hints for updating: ``` 3 vulnerabilities (1 high, 2 moderate) To update all dependencies to the latest compatible versions: bun update To update all dependencies to the latest versions (including breaking changes): bun update --latest ``` ### Filtering options **`--audit-level=`** - Only show vulnerabilities at this severity level or higher: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun audit --audit-level=high ``` **`--prod`** - Audit only production dependencies (excludes devDependencies): ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun audit --prod ``` **`--ignore `** - Ignore specific CVEs (can be used multiple times): ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun audit --ignore CVE-2022-25883 --ignore CVE-2023-26136 ``` ### `--json` Use the `--json` flag to print the raw JSON response from the registry instead of the formatted report: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun audit --json ``` ### Exit code `bun audit` will exit with code `0` if no vulnerabilities are found and `1` if the report lists any vulnerabilities. This will still happen even if `--json` is passed. # bun install Source: https://bun.com/docs/pm/cli/install Install packages with Bun's fast package manager ## Basic Usage ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun install react bun install react@19.1.1 # specific version bun install react@latest # specific tag ``` The `bun` CLI contains a Node.js-compatible package manager designed to be a dramatically faster replacement for `npm`, `yarn`, and `pnpm`. It's a standalone tool that will work in pre-existing Node.js projects; if your project has a `package.json`, `bun install` can help you speed up your workflow. **⚡️ 25x faster** — Switch from `npm install` to `bun install` in any Node.js project to make your installations up to 25x faster. ![Bun installation speed comparison](https://user-images.githubusercontent.com/709451/147004342-571b6123-17a9-49a2-8bfd-dcfc5204047e.png) The recommended minimum Linux Kernel version is 5.6. If you're on Linux kernel 5.1 - 5.5, `bun install` will work, but HTTP requests will be slow due to a lack of support for io\_uring's `connect()` operation. If you're using Ubuntu 20.04, here's how to install a [newer kernel](https://wiki.ubuntu.com/Kernel/LTSEnablementStack): ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} # If this returns a version >= 5.6, you don't need to do anything uname -r # Install the official Ubuntu hardware enablement kernel sudo apt install --install-recommends linux-generic-hwe-20.04 ``` To install all dependencies of a project: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun install ``` Running `bun install` will: * **Install** all `dependencies`, `devDependencies`, and `optionalDependencies`. Bun will install `peerDependencies` by default. * **Run** your project's `{pre|post}install` and `{pre|post}prepare` scripts at the appropriate time. For security reasons Bun *does not execute* lifecycle scripts of installed dependencies. * **Write** a `bun.lock` lockfile to the project root. *** ## Logging To modify logging verbosity: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun install --verbose # debug logging bun install --silent # no logging ``` *** ## Lifecycle scripts Unlike other npm clients, Bun does not execute arbitrary lifecycle scripts like `postinstall` for installed dependencies. Executing arbitrary scripts represents a potential security risk. To tell Bun to allow lifecycle scripts for a particular package, add the package to `trustedDependencies` in your package.json. ```json package.json icon="file-json" theme={"theme":{"light":"github-light","dark":"dracula"}} { "name": "my-app", "version": "1.0.0", "trustedDependencies": ["my-trusted-package"] // [!code ++] } ``` Then re-install the package. Bun will read this field and run lifecycle scripts for `my-trusted-package`. Lifecycle scripts will run in parallel during installation. To adjust the maximum number of concurrent scripts, use the `--concurrent-scripts` flag. The default is two times the reported cpu count or GOMAXPROCS. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun install --concurrent-scripts 5 ``` *** ## Workspaces Bun supports `"workspaces"` in package.json. For complete documentation refer to [Package manager > Workspaces](/pm/workspaces). ```json package.json icon="file-json" theme={"theme":{"light":"github-light","dark":"dracula"}} { "name": "my-app", "version": "1.0.0", "workspaces": ["packages/*"], // [!code ++] "dependencies": { "preact": "^10.5.13" } } ``` *** ## Installing dependencies for specific packages In a monorepo, you can install the dependencies for a subset of packages using the `--filter` flag. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} # Install dependencies for all workspaces except `pkg-c` bun install --filter '!pkg-c' # Install dependencies for only `pkg-a` in `./packages/pkg-a` bun install --filter './packages/pkg-a' ``` For more information on filtering with `bun install`, refer to [Package Manager > Filtering](/pm/filter#bun-install-and-bun-outdated) *** ## Overrides and resolutions Bun supports npm's `"overrides"` and Yarn's `"resolutions"` in `package.json`. These are mechanisms for specifying a version range for *metadependencies*—the dependencies of your dependencies. Refer to [Package manager > Overrides and resolutions](/pm/overrides) for complete documentation. ```json package.json file="file-json" theme={"theme":{"light":"github-light","dark":"dracula"}} { "name": "my-app", "dependencies": { "foo": "^2.0.0" }, "overrides": { // [!code ++] "bar": "~4.4.0" // [!code ++] } // [!code ++] } ``` *** ## Global packages To install a package globally, use the `-g`/`--global` flag. Typically this is used for installing command-line tools. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun install --global cowsay # or `bun install -g cowsay` cowsay "Bun!" ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} ______ < Bun! > ------ \ ^__^ \ (oo)\_______ (__)\ )\/\ ||----w | || || ``` *** ## Production mode To install in production mode (i.e. without `devDependencies` or `optionalDependencies`): ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun install --production ``` For reproducible installs, use `--frozen-lockfile`. This will install the exact versions of each package specified in the lockfile. If your `package.json` disagrees with `bun.lock`, Bun will exit with an error. The lockfile will not be updated. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun install --frozen-lockfile ``` For more information on Bun's lockfile `bun.lock`, refer to [Package manager > Lockfile](/pm/lockfile). *** ## Omitting dependencies To omit dev, peer, or optional dependencies use the `--omit` flag. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} # Exclude "devDependencies" from the installation. This will apply to the # root package and workspaces if they exist. Transitive dependencies will # not have "devDependencies". bun install --omit dev # Install only dependencies from "dependencies" bun install --omit=dev --omit=peer --omit=optional ``` *** ## Dry run To perform a dry run (i.e. don't actually install anything): ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun install --dry-run ``` *** ## Non-npm dependencies Bun supports installing dependencies from Git, GitHub, and local or remotely-hosted tarballs. For complete documentation refer to [Package manager > Git, GitHub, and tarball dependencies](/pm/cli/add). ```json package.json icon="file-json" theme={"theme":{"light":"github-light","dark":"dracula"}} { "dependencies": { "dayjs": "git+https://github.com/iamkun/dayjs.git", "lodash": "git+ssh://github.com/lodash/lodash.git#4.17.21", "moment": "git@github.com:moment/moment.git", "zod": "github:colinhacks/zod", "react": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", "bun-types": "npm:@types/bun" } } ``` *** ## Installation strategies Bun supports two package installation strategies that determine how dependencies are organized in `node_modules`: ### Hoisted installs (default for single projects) The traditional npm/Yarn approach that flattens dependencies into a shared `node_modules` directory: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun install --linker hoisted ``` ### Isolated installs A pnpm-like approach that creates strict dependency isolation to prevent phantom dependencies: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun install --linker isolated ``` Isolated installs create a central package store in `node_modules/.bun/` with symlinks in the top-level `node_modules`. This ensures packages can only access their declared dependencies. For complete documentation on isolated installs, refer to [Package manager > Isolated installs](/pm/isolated-installs). *** ## Minimum release age To protect against supply chain attacks where malicious packages are quickly published, you can configure a minimum age requirement for npm packages. Package versions published more recently than the specified threshold (in seconds) will be filtered out during installation. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} # Only install package versions published at least 3 days ago bun add @types/bun --minimum-release-age 259200 # seconds ``` You can also configure this in `bunfig.toml`: ```toml bunfig.toml icon="settings" theme={"theme":{"light":"github-light","dark":"dracula"}} [install] # Only install package versions published at least 3 days ago minimumReleaseAge = 259200 # seconds # Exclude trusted packages from the age gate minimumReleaseAgeExcludes = ["@types/node", "typescript"] ``` When the minimum age filter is active: * Only affects new package resolution - existing packages in `bun.lock` remain unchanged * All dependencies (direct and transitive) are filtered to meet the age requirement when being resolved * When versions are blocked by the age gate, a stability check detects rapid bugfix patterns * If multiple versions were published close together just outside your age gate, it extends the filter to skip those potentially unstable versions and selects an older, more mature version * Searches up to 7 days after the age gate, however if still finding rapid releases it ignores stability check * Exact version requests (like `package@1.1.1`) still respect the age gate but bypass the stability check * Versions without a `time` field are treated as passing the age check (npm registry should always provide timestamps) For more advanced security scanning, including integration with services & custom filtering, see [Package manager > Security Scanner API](/pm/security-scanner-api). *** ## Configuration The default behavior of `bun install` can be configured in `bunfig.toml`. The default values are shown below. ```toml bunfig.toml icon="settings" theme={"theme":{"light":"github-light","dark":"dracula"}} [install] # whether to install optionalDependencies optional = true # whether to install devDependencies dev = true # whether to install peerDependencies peer = true # equivalent to `--production` flag production = false # equivalent to `--save-text-lockfile` flag saveTextLockfile = false # equivalent to `--frozen-lockfile` flag frozenLockfile = false # equivalent to `--dry-run` flag dryRun = false # equivalent to `--concurrent-scripts` flag concurrentScripts = 16 # (cpu count or GOMAXPROCS) x2 # installation strategy: "hoisted" or "isolated" # default: "hoisted" (for single-project projects) # default: "isolated" (for monorepo projects) linker = "hoisted" # minimum age config minimumReleaseAge = 259200 # seconds minimumReleaseAgeExcludes = ["@types/node", "typescript"] ``` *** ## CI/CD Use the official [`oven-sh/setup-bun`](https://github.com/oven-sh/setup-bun) action to install `bun` in a GitHub Actions pipeline: ```yaml .github/workflows/release.yml icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} name: bun-types jobs: build: name: build-app runs-on: ubuntu-latest steps: - name: Checkout repo uses: actions/checkout@v4 - name: Install bun uses: oven-sh/setup-bun@v2 - name: Install dependencies run: bun install - name: Build app run: bun run build ``` For CI/CD environments that want to enforce reproducible builds, use `bun ci` to fail the build if the package.json is out of sync with the lockfile: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun ci ``` This is equivalent to `bun install --frozen-lockfile`. It installs exact versions from `bun.lock` and fails if `package.json` doesn't match the lockfile. To use `bun ci` or `bun install --frozen-lockfile`, you must commit `bun.lock` to version control. And instead of running `bun install`, run `bun ci`. ```yaml .github/workflows/release.yml icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} name: bun-types jobs: build: name: build-app runs-on: ubuntu-latest steps: - name: Checkout repo uses: actions/checkout@v4 - name: Install bun uses: oven-sh/setup-bun@v2 - name: Install dependencies run: bun ci - name: Build app run: bun run build ``` ## pnpm migration Bun automatically migrates projects from pnpm to bun. When a `pnpm-lock.yaml` file is detected and no `bun.lock` file exists, Bun will automatically migrate the lockfile to `bun.lock` during installation. The original `pnpm-lock.yaml` file remains unmodified. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun install ``` **Note**: Migration only runs when `bun.lock` is absent. There is currently no opt-out flag for pnpm migration. The migration process handles: ### Lockfile Migration * Converts `pnpm-lock.yaml` to `bun.lock` format * Preserves package versions and resolution information * Maintains dependency relationships and peer dependencies * Handles patched dependencies with integrity hashes ### Workspace Configuration When a `pnpm-workspace.yaml` file exists, Bun migrates workspace settings to your root `package.json`: ```yaml pnpm-workspace.yaml icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} packages: - "apps/*" - "packages/*" catalog: react: ^18.0.0 typescript: ^5.0.0 catalogs: build: webpack: ^5.0.0 babel: ^7.0.0 ``` The workspace packages list and catalogs are moved to the `workspaces` field in `package.json`: ```json package.json icon="file-json" theme={"theme":{"light":"github-light","dark":"dracula"}} { "workspaces": { "packages": ["apps/*", "packages/*"], "catalog": { "react": "^18.0.0", "typescript": "^5.0.0" }, "catalogs": { "build": { "webpack": "^5.0.0", "babel": "^7.0.0" } } } } ``` ### Catalog Dependencies Dependencies using pnpm's `catalog:` protocol are preserved: ```json package.json icon="file-json" theme={"theme":{"light":"github-light","dark":"dracula"}} { "dependencies": { "react": "catalog:", "webpack": "catalog:build" } } ``` ### Configuration Migration The following pnpm configuration is migrated from both `pnpm-lock.yaml` and `pnpm-workspace.yaml`: * **Overrides**: Moved from `pnpm.overrides` to root-level `overrides` in `package.json` * **Patched Dependencies**: Moved from `pnpm.patchedDependencies` to root-level `patchedDependencies` in `package.json` * **Workspace Overrides**: Applied from `pnpm-workspace.yaml` to root `package.json` ### Requirements * Requires pnpm lockfile version 7 or higher * Workspace packages must have a `name` field in their `package.json` * All catalog entries referenced by dependencies must exist in the catalogs definition After migration, you can safely remove `pnpm-lock.yaml` and `pnpm-workspace.yaml` files. *** ## CLI Usage ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun install @ ``` ### General Configuration Specify path to config file (bunfig.toml) Set a specific cwd ### Dependency Scope & Management Don't install devDependencies Don't update package.json or save a lockfile Save to package.json Exclude 'dev', 'optional', or 'peer' dependencies from install Only add dependencies to package.json if they are not already present ### Dependency Type & Versioning Add dependency to "devDependencies" Add dependency to "optionalDependencies" Add dependency to "peerDependencies" Add the exact version instead of the ^range ### Lockfile Control Write a yarn.lock file (yarn v1) Disallow changes to lockfile Save a text-based lockfile Generate a lockfile without installing dependencies ### Network & Registry Settings Provide a Certificate Authority signing certificate File path to Certificate Authority signing certificate Use a specific registry by default, overriding .npmrc, bunfig.toml and environment variables ### Installation Process Control Don't install anything Always request the latest versions from the registry & reinstall all dependencies Install globally Platform-specific optimizations: "clonefile", "hardlink", "symlink", "copyfile" Install packages for the matching workspaces Analyze & install all dependencies of files passed as arguments recursively ### Caching Options Store & load cached data from a specific directory path Ignore manifest cache entirely ### Output & Logging Don't log anything Excessively verbose logging Disable the progress bar Don't print a summary ### Security & Integrity Skip verifying integrity of newly downloaded packages Add to trustedDependencies in the project's package.json and install the package(s) ### Concurrency & Performance Maximum number of concurrent jobs for lifecycle scripts Maximum number of concurrent network requests ### Lifecycle Script Management Skip lifecycle scripts in the project's package.json (dependency scripts are never run) ### Help Information Print this help menu # bun link Source: https://bun.com/docs/pm/cli/link Link local packages for development Use `bun link` in a local directory to register the current package as a "linkable" package. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} cd /path/to/cool-pkg cat package.json bun link ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} bun link v1.x (7416672e) Success! Registered "cool-pkg" To use cool-pkg in a project, run: bun link cool-pkg Or add it in dependencies in your package.json file: "cool-pkg": "link:cool-pkg" ``` This package can now be "linked" into other projects using `bun link cool-pkg`. This will create a symlink in the `node_modules` directory of the target project, pointing to the local directory. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} cd /path/to/my-app bun link cool-pkg ``` In addition, the `--save` flag can be used to add `cool-pkg` to the `dependencies` field of your app's package.json with a special version specifier that tells Bun to load from the registered local directory instead of installing from `npm`: ```json package.json icon="file-json" theme={"theme":{"light":"github-light","dark":"dracula"}} { "name": "my-app", "version": "1.0.0", "dependencies": { "cool-pkg": "link:cool-pkg" // [!code ++] } } ``` *** # CLI Usage ```bash theme={"theme":{"light":"github-light","dark":"dracula"}} bun link ``` ### Installation Scope Install globally. Alias: -g ### Dependency Management Don't install devDependencies. Alias: -p Exclude dev, optional, or peer dependencies from install ### Project Files & Lockfiles Write a yarn.lock file (yarn v1). Alias: -y Disallow changes to lockfile Save a text-based lockfile Generate a lockfile without installing dependencies Don't update package.json or save a lockfile Save to package.json (true by default) Add to trustedDependencies in the project's package.json and install the package(s) ### Installation Control Always request the latest versions from the registry & reinstall all dependencies. Alias:{" "} -f Skip verifying integrity of newly downloaded packages Platform-specific optimizations for installing dependencies. Possible values:{" "} clonefile (default), hardlink, symlink,{" "} copyfile Linker strategy (one of isolated or hoisted) Don't install anything Skip lifecycle scripts in the project's package.json (dependency scripts are never run) ### Network & Registry Provide a Certificate Authority signing certificate Same as --ca, but as a file path to the certificate Use a specific registry by default, overriding .npmrc, bunfig.toml, and environment variables Maximum number of concurrent network requests (default 48) ### Performance & Resource Maximum number of concurrent jobs for lifecycle scripts (default 5) ### Caching Store & load cached data from a specific directory path Ignore manifest cache entirely ### Output & Logging Don't log anything Only show tarball name when packing Excessively verbose logging Disable the progress bar Don't print a summary ### Platform Targeting Override CPU architecture for optional dependencies (e.g., x64, arm64,{" "} \* for all) Override operating system for optional dependencies (e.g., linux, darwin , \* for all) ### Global Configuration & Context Specify path to config file (bunfig.toml). Alias: -c Set a specific current working directory ### Help Print this help menu. Alias: -h # bun outdated Source: https://bun.com/docs/pm/cli/outdated Check for outdated dependencies Use `bun outdated` to check for outdated dependencies in your project. This command displays a table of dependencies that have newer versions available. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun outdated ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} | Package | Current | Update | Latest | | ------------------------------ | ------- | --------- | ---------- | | @sinclair/typebox | 0.34.15 | 0.34.16 | 0.34.16 | | @types/bun (dev) | 1.2.0 | 1.2.23 | 1.2.23 | | eslint (dev) | 8.57.1 | 8.57.1 | 9.20.0 | | eslint-plugin-security (dev) | 2.1.1 | 2.1.1 | 3.0.1 | | eslint-plugin-sonarjs (dev) | 0.23.0 | 0.23.0 | 3.0.1 | | expect-type (dev) | 0.16.0 | 0.16.0 | 1.1.0 | | prettier (dev) | 3.4.2 | 3.5.0 | 3.5.0 | | tsup (dev) | 8.3.5 | 8.3.6 | 8.3.6 | | typescript (dev) | 5.7.2 | 5.7.3 | 5.7.3 | ``` ## Version Information The output table shows three version columns: * **Current**: The version currently installed * **Update**: The latest version that satisfies your package.json version range * **Latest**: The latest version published to the registry ### Dependency Filters `bun outdated` supports searching for outdated dependencies by package names and glob patterns. To check if specific dependencies are outdated, pass the package names as positional arguments: ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun outdated eslint-plugin-security eslint-plugin-sonarjs ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} | Package | Current | Update | Latest | | ------------------------------ | ------- | ------ | --------- | | eslint-plugin-security (dev) | 2.1.1 | 2.1.1 | 3.0.1 | | eslint-plugin-sonarjs (dev) | 0.23.0 | 0.23.0 | 3.0.1 | ``` You can also pass glob patterns to check for outdated packages: ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun outdated eslint* ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} | Package | Current | Update | Latest | | ------------------------------ | ------- | ------ | ---------- | | eslint (dev) | 8.57.1 | 8.57.1 | 9.20.0 | | eslint-plugin-security (dev) | 2.1.1 | 2.1.1 | 3.0.1 | | eslint-plugin-sonarjs (dev) | 0.23.0 | 0.23.0 | 3.0.1 | ``` For example, to check for outdated `@types/*` packages: ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun outdated '@types/*' ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} | Package | Current | Update | Latest | | ------------------ | ------- | ------ | ------ | | @types/bun (dev) | 1.2.0 | 1.2.23 | 1.2.23 | ``` Or to exclude all `@types/*` packages: ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun outdated '!@types/*' ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} | Package | Current | Update | Latest | | ------------------------------ | ------- | --------- | ---------- | | @sinclair/typebox | 0.34.15 | 0.34.16 | 0.34.16 | | eslint (dev) | 8.57.1 | 8.57.1 | 9.20.0 | | eslint-plugin-security (dev) | 2.1.1 | 2.1.1 | 3.0.1 | | eslint-plugin-sonarjs (dev) | 0.23.0 | 0.23.0 | 3.0.1 | | expect-type (dev) | 0.16.0 | 0.16.0 | 1.1.0 | | prettier (dev) | 3.4.2 | 3.5.0 | 3.5.0 | | tsup (dev) | 8.3.5 | 8.3.6 | 8.3.6 | | typescript (dev) | 5.7.2 | 5.7.3 | 5.7.3 | ``` ### Workspace Filters Use the `--filter` flag to check for outdated dependencies in a different workspace package: ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun outdated --filter='@monorepo/types' ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} | Package | Current | Update | Latest | | ------------------ | ------- | ------ | ------ | | tsup (dev) | 8.3.5 | 8.3.6 | 8.3.6 | | typescript (dev) | 5.7.2 | 5.7.3 | 5.7.3 | ``` You can pass multiple `--filter` flags to check multiple workspaces: ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun outdated --filter @monorepo/types --filter @monorepo/cli ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} | Package | Current | Update | Latest | | ------------------------------ | ------- | ------ | ---------- | | eslint (dev) | 8.57.1 | 8.57.1 | 9.20.0 | | eslint-plugin-security (dev) | 2.1.1 | 2.1.1 | 3.0.1 | | eslint-plugin-sonarjs (dev) | 0.23.0 | 0.23.0 | 3.0.1 | | expect-type (dev) | 0.16.0 | 0.16.0 | 1.1.0 | | tsup (dev) | 8.3.5 | 8.3.6 | 8.3.6 | | typescript (dev) | 5.7.2 | 5.7.3 | 5.7.3 | ``` You can also pass glob patterns to filter by workspace names: ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun outdated --filter='@monorepo/{types,cli}' ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} | Package | Current | Update | Latest | | ------------------------------ | ------- | ------ | ---------- | | eslint (dev) | 8.57.1 | 8.57.1 | 9.20.0 | | eslint-plugin-security (dev) | 2.1.1 | 2.1.1 | 3.0.1 | | eslint-plugin-sonarjs (dev) | 0.23.0 | 0.23.0 | 3.0.1 | | expect-type (dev) | 0.16.0 | 0.16.0 | 1.1.0 | | tsup (dev) | 8.3.5 | 8.3.6 | 8.3.6 | | typescript (dev) | 5.7.2 | 5.7.3 | 5.7.3 | ``` ### Catalog Dependencies `bun outdated` supports checking catalog dependencies defined in`package.json`: ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun outdated -r ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} ┌────────────────────┬─────────┬─────────┬─────────┬────────────────────────────────┐ │ Package │ Current │ Update │ Latest │ Workspace │ ├────────────────────┼─────────┼─────────┼─────────┼────────────────────────────────┤ │ body-parser │ 1.19.0 │ 1.19.0 │ 2.2.0 │ @test/shared │ ├────────────────────┼─────────┼─────────┼─────────┼────────────────────────────────┤ │ cors │ 2.8.0 │ 2.8.0 │ 2.8.5 │ @test/shared │ ├────────────────────┼─────────┼─────────┼─────────┼────────────────────────────────┤ │ chalk │ 4.0.0 │ 4.0.0 │ 5.6.2 │ @test/utils │ ├────────────────────┼─────────┼─────────┼─────────┼────────────────────────────────┤ │ uuid │ 8.0.0 │ 8.0.0 │ 13.0.0 │ @test/utils │ ├────────────────────┼─────────┼─────────┼─────────┼────────────────────────────────┤ │ axios │ 0.21.0 │ 0.21.0 │ 1.12.2 │ catalog (@test/app) │ ├────────────────────┼─────────┼─────────┼─────────┼────────────────────────────────┤ │ lodash │ 4.17.15 │ 4.17.15 │ 4.17.21 │ catalog (@test/app, @test/app) │ ├────────────────────┼─────────┼─────────┼─────────┼────────────────────────────────┤ │ react │ 17.0.0 │ 17.0.0 │ 19.1.1 │ catalog (@test/app) │ ├────────────────────┼─────────┼─────────┼─────────┼────────────────────────────────┤ │ react-dom │ 17.0.0 │ 17.0.0 │ 19.1.1 │ catalog (@test/app) │ ├────────────────────┼─────────┼─────────┼─────────┼────────────────────────────────┤ │ express │ 4.17.0 │ 4.17.0 │ 5.1.0 │ catalog (@test/shared) │ ├────────────────────┼─────────┼─────────┼─────────┼────────────────────────────────┤ │ moment │ 2.24.0 │ 2.24.0 │ 2.30.1 │ catalog (@test/utils) │ ├────────────────────┼─────────┼─────────┼─────────┼────────────────────────────────┤ │ @types/node (dev) │ 14.0.0 │ 14.0.0 │ 24.5.2 │ @test/shared │ ├────────────────────┼─────────┼─────────┼─────────┼────────────────────────────────┤ │ @types/react (dev) │ 17.0.0 │ 17.0.0 │ 19.1.15 │ catalog:testing (@test/app) │ ├────────────────────┼─────────┼─────────┼─────────┼────────────────────────────────┤ │ eslint (dev) │ 7.0.0 │ 7.0.0 │ 9.36.0 │ catalog:testing (@test/app) │ ├────────────────────┼─────────┼─────────┼─────────┼────────────────────────────────┤ │ typescript (dev) │ 4.9.5 │ 4.9.5 │ 5.9.2 │ catalog:build (@test/app) │ ├────────────────────┼─────────┼─────────┼─────────┼────────────────────────────────┤ │ jest (dev) │ 26.0.0 │ 26.0.0 │ 30.2.0 │ catalog:testing (@test/shared) │ ├────────────────────┼─────────┼─────────┼─────────┼────────────────────────────────┤ │ prettier (dev) │ 2.0.0 │ 2.0.0 │ 3.6.2 │ catalog:build (@test/utils) │ └────────────────────┴─────────┴─────────┴─────────┴────────────────────────────────┘ ``` *** ## CLI Usage ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun outdated ``` ### General Options Specify path to config file (bunfig.toml) Set a specific cwd Print this help menu Display outdated dependencies for each matching workspace ### Output & Logging Don't log anything Excessively verbose logging Disable the progress bar Don't print a summary ### Dependency Scope & Target Don't install devDependencies Exclude dev, optional, or peer dependencies from install Install globally ### Lockfile & Package.json Write a yarn.lock file (yarn v1) Don't update package.json or save a lockfile Save to package.json (true by default) Disallow changes to lockfile Save a text-based lockfile Generate a lockfile without installing dependencies Add to trustedDependencies in the project's package.json and install the package(s) ### Network & Registry Provide a Certificate Authority signing certificate Same as --ca, but as a file path to the certificate Use a specific registry by default, overriding .npmrc, bunfig.toml and environment variables Maximum number of concurrent network requests (default 48) ### Caching Store & load cached data from a specific directory path Ignore manifest cache entirely ### Execution Behavior Don't install anything Always request the latest versions from the registry & reinstall all dependencies Skip verifying integrity of newly downloaded packages Skip lifecycle scripts in the project's package.json (dependency scripts are never run) Platform-specific optimizations for installing dependencies. Possible values:{" "} clonefile (default), hardlink, symlink,{" "} copyfile Maximum number of concurrent jobs for lifecycle scripts (default 5) # bun patch Source: https://bun.com/docs/pm/cli/patch Persistently patch node_modules packages in a git-friendly way `bun patch` lets you persistently patch node\_modules in a maintainable, git-friendly way. Sometimes, you need to make a small change to a package in `node_modules/` to fix a bug or add a feature. `bun patch` makes it easy to do this without vendoring the entire package and reuse the patch across multiple installs, multiple projects, and multiple machines. Features: * Generates `.patch` files applied to dependencies in `node_modules` on install * `.patch` files can be committed to your repository, reused across multiple installs, projects, and machines * `"patchedDependencies"` in `package.json` keeps track of patched packages * `bun patch` lets you patch packages in `node_modules/` while preserving the integrity of Bun's [Global Cache](/pm/global-cache) * Test your changes locally before committing them with `bun patch --commit ` * To preserve disk space and keep `bun install` fast, patched packages are committed to the Global Cache and shared across projects where possible #### Step 1. Prepare the package for patching To get started, use `bun patch ` to prepare the package for patching: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} # you can supply the package name bun patch react # ...and a precise version in case multiple versions are installed bun patch react@17.0.2 # or the path to the package bun patch node_modules/react ``` Don't forget to call `bun patch `! This ensures the package folder in `node_modules/` contains a fresh copy of the package with no symlinks/hardlinks to Bun's cache. If you forget to do this, you might end up editing the package globally in the cache! #### Step 2. Test your changes locally `bun patch ` makes it safe to edit the `` in `node_modules/` directly, while preserving the integrity of Bun's [Global Cache](/pm/global-cache). This works by re-creating an unlinked clone of the package in `node_modules/` and diffing it against the original package in the Global Cache. #### Step 3. Commit your changes Once you're happy with your changes, run `bun patch --commit `. Bun will generate a patch file in `patches/`, update your `package.json` and lockfile, and Bun will start using the patched package: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} # you can supply the path to the patched package bun patch --commit node_modules/react # ... or the package name and optionally the version bun patch --commit react@17.0.2 # choose the directory to store the patch files bun patch --commit react --patches-dir=mypatches # `patch-commit` is available for compatibility with pnpm bun patch-commit react ``` *** # CLI Usage ```bash theme={"theme":{"light":"github-light","dark":"dracula"}} bun patch @ ``` ### Patch Generation Install a package containing modifications in dir The directory to put the patch file in (only if --commit is used) ### Dependency Management Don't install devDependencies. Alias: -p Skip lifecycle scripts in the project's package.json (dependency scripts are never run) Add to trustedDependencies in the project's package.json and install the package(s) Install globally. Alias: -g Exclude dev, optional, or peer dependencies from install ### Project Files & Lockfiles Write a yarn.lock file (yarn v1). Alias: -y Don't update package.json or save a lockfile Save to package.json (true by default) Disallow changes to lockfile Save a text-based lockfile Generate a lockfile without installing dependencies ### Installation Control Platform-specific optimizations for installing dependencies. Possible values:{" "} clonefile (default), hardlink, symlink,{" "} copyfile Linker strategy (one of isolated or hoisted) Don't install anything Always request the latest versions from the registry & reinstall all dependencies. Alias:{" "} -f Skip verifying integrity of newly downloaded packages ### Network & Registry Provide a Certificate Authority signing certificate Same as --ca, but as a file path to the certificate Use a specific registry by default, overriding .npmrc, bunfig.toml, and environment variables Maximum number of concurrent network requests (default 48) ### Performance & Resource Maximum number of concurrent jobs for lifecycle scripts (default 5) ### Caching Store & load cached data from a specific directory path Ignore manifest cache entirely ### Output & Logging Don't log anything Only show tarball name when packing Excessively verbose logging Disable the progress bar Don't print a summary ### Platform Targeting Override CPU architecture for optional dependencies (e.g., x64, arm64,{" "} \* for all) Override operating system for optional dependencies (e.g., linux, darwin , \* for all) ### Global Configuration & Context Specify path to config file (bunfig.toml). Alias: -c Set a specific current working directory ### Help Print this help menu. Alias: -h # bun pm Source: https://bun.com/docs/pm/cli/pm Package manager utilities The `bun pm` command group provides a set of utilities for working with Bun's package manager. ## pack To create a tarball of the current workspace: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun pm pack ``` This command creates a `.tgz` file containing all files that would be published to npm, following the same rules as `npm pack`. ## Examples Basic usage: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun pm pack # Creates my-package-1.0.0.tgz in current directory ``` Quiet mode for scripting: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} TARBALL=$(bun pm pack --quiet) echo "Created: $TARBALL" ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} Created: my-package-1.0.0.tgz ``` Custom destination: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun pm pack --destination ./dist # Saves tarball in ./dist/ directory ``` ## Options * `--dry-run`: Perform all tasks except writing the tarball to disk. Shows what would be included. * `--destination `: Specify the directory where the tarball will be saved. * `--filename `: Specify an exact file name for the tarball to be saved at. * `--ignore-scripts`: Skip running pre/postpack and prepare scripts. * `--gzip-level <0-9>`: Set a custom compression level for gzip, ranging from 0 to 9 (default is 9). * `--quiet`: Only output the tarball filename, suppressing verbose output. Ideal for scripts and automation. > **Note:** `--filename` and `--destination` cannot be used at the same time. ## Output Modes **Default output:** ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun pm pack ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} bun pack v1.2.19 packed 131B package.json packed 40B index.js my-package-1.0.0.tgz Total files: 2 Shasum: f2451d6eb1e818f500a791d9aace80b394258a90 Unpacked size: 171B Packed size: 249B ``` **Quiet output:** ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun pm pack --quiet ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} my-package-1.0.0.tgz ``` The `--quiet` flag is particularly useful for automation workflows where you need to capture the generated tarball filename for further processing. ## bin To print the path to the `bin` directory for the local project: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun pm bin ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} /path/to/current/project/node_modules/.bin ``` To print the path to the global `bin` directory: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun pm bin -g ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} <$HOME>/.bun/bin ``` ## ls To print a list of installed dependencies in the current project and their resolved versions, excluding their dependencies. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun pm ls ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} /path/to/project node_modules (135) ├── eslint@8.38.0 ├── react@18.2.0 ├── react-dom@18.2.0 ├── typescript@5.0.4 └── zod@3.21.4 ``` To print all installed dependencies, including nth-order dependencies. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun pm ls --all ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} /path/to/project node_modules (135) ├── @eslint-community/eslint-utils@4.4.0 ├── @eslint-community/regexpp@4.5.0 ├── @eslint/eslintrc@2.0.2 ├── @eslint/js@8.38.0 ├── @nodelib/fs.scandir@2.1.5 ├── @nodelib/fs.stat@2.0.5 ├── @nodelib/fs.walk@1.2.8 ├── acorn@8.8.2 ├── acorn-jsx@5.3.2 ├── ajv@6.12.6 ├── ansi-regex@5.0.1 ├── ... ``` ## whoami Print your npm username. Requires you to be logged in (`bunx npm login`) with credentials in either `bunfig.toml` or `.npmrc`: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun pm whoami ``` ## hash To generate and print the hash of the current lockfile: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun pm hash ``` To print the string used to hash the lockfile: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun pm hash-string ``` To print the hash stored in the current lockfile: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun pm hash-print ``` ## cache To print the path to Bun's global module cache: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun pm cache ``` To clear Bun's global module cache: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun pm cache rm ``` ## migrate To migrate another package manager's lockfile without installing anything: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun pm migrate ``` ## untrusted To print current untrusted dependencies with scripts: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun pm untrusted ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} ./node_modules/@biomejs/biome @1.8.3 » [postinstall]: node scripts/postinstall.js These dependencies had their lifecycle scripts blocked during install. ``` ## trust To run scripts for untrusted dependencies and add to `trustedDependencies`: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun pm trust ``` Options for the `trust` command: * `--all`: Trust all untrusted dependencies. ## default-trusted To print the default trusted dependencies list: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun pm default-trusted ``` see the current list on GitHub [here](https://github.com/oven-sh/bun/blob/main/src/install/default-trusted-dependencies.txt) ## version To display current package version and help: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun pm version ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} bun pm version v1.3.1 (ca7428e9) Current package version: v1.0.0 Increment: patch 1.0.0 → 1.0.1 minor 1.0.0 → 1.1.0 major 1.0.0 → 2.0.0 prerelease 1.0.0 → 1.0.1-0 prepatch 1.0.0 → 1.0.1-0 preminor 1.0.0 → 1.1.0-0 premajor 1.0.0 → 2.0.0-0 from-git Use version from latest git tag 1.2.3 Set specific version Options: --no-git-tag-version Skip git operations --allow-same-version Prevents throwing error if version is the same --message=, -m Custom commit message, use %s for version substitution --preid= Prerelease identifier (i.e beta → 1.0.1-beta.0) --force, -f Bypass dirty git history check Examples: bun pm version patch bun pm version 1.2.3 --no-git-tag-version bun pm version prerelease --preid beta --message "Release beta: %s" ``` To bump the version in `package.json`: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun pm version patch ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} v1.0.1 ``` Supports `patch`, `minor`, `major`, `premajor`, `preminor`, `prepatch`, `prerelease`, `from-git`, or specific versions like `1.2.3`. By default creates git commit and tag unless `--no-git-tag-version` was used to skip. ## pkg Manage `package.json` data with get, set, delete, and fix operations. All commands support dot and bracket notation: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} scripts.build # dot notation contributors[0] # array access workspaces.0 # dot with numeric index scripts[test:watch] # bracket for special chars ``` Examples: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} # set bun pm pkg get name # single property bun pm pkg get name version # multiple properties bun pm pkg get # entire package.json bun pm pkg get scripts.build # nested property # set bun pm pkg set name="my-package" # simple property bun pm pkg set scripts.test="jest" version=2.0.0 # multiple properties bun pm pkg set {"private":"true"} --json # JSON values with --json flag # delete bun pm pkg delete description # single property bun pm pkg delete scripts.test contributors[0] # multiple/nested # fix bun pm pkg fix # auto-fix common issues ``` # bun publish Source: https://bun.com/docs/pm/cli/publish Use `bun publish` to publish a package to the npm registry `bun publish` will automatically pack your package into a tarball, strip catalog and workspace protocols from the `package.json` (resolving versions if necessary), and publish to the registry specified in your configuration files. Both `bunfig.toml` and `.npmrc` files are supported. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} ## Publishing the package from the current working directory bun publish ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} bun publish v1.3.1 (ca7428e9) packed 203B package.json packed 224B README.md packed 30B index.ts packed 0.64KB tsconfig.json Total files: 4 Shasum: 79e2b4377b63f4de38dc7ea6e5e9dbee08311a69 Integrity: sha512-6QSNlDdSwyG/+[...]X6wXHriDWr6fA== Unpacked size: 1.1KB Packed size: 0.76KB Tag: latest Access: default Registry: http://localhost:4873/ + publish-1@1.0.0 ``` Alternatively, you can pack and publish your package separately by using `bun pm pack` followed by `bun publish` with the path to the output tarball. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun pm pack ... bun publish ./package.tgz ``` `bun publish` will not run lifecycle scripts (`prepublishOnly/prepack/prepare/postpack/publish/postpublish`) if a tarball path is provided. Scripts will only be run if the package is packed by `bun publish`. ### `--access` The `--access` flag can be used to set the access level of the package being published. The access level can be one of `public` or `restricted`. Unscoped packages are always public, and attempting to publish an unscoped package with `--access restricted` will result in an error. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun publish --access public ``` `--access` can also be set in the `publishConfig` field of your `package.json`. ```json package.json icon="file-json" theme={"theme":{"light":"github-light","dark":"dracula"}} { "publishConfig": { "access": "restricted" } } ``` ### `--tag` Set the tag of the package version being published. By default, the tag is `latest`. The initial version of a package is always given the `latest` tag in addition to the specified tag. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun publish --tag alpha ``` `--tag` can also be set in the `publishConfig` field of your `package.json`. ```json package.json icon="file-json" theme={"theme":{"light":"github-light","dark":"dracula"}} { "publishConfig": { "tag": "next" } } ``` ### `--dry-run` The `--dry-run` flag can be used to simulate the publish process without actually publishing the package. This is useful for verifying the contents of the published package without actually publishing the package. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun publish --dry-run ``` ### `--gzip-level` Specify the level of gzip compression to use when packing the package. Only applies to `bun publish` without a tarball path argument. Values range from `0` to `9` (default is `9`). ### `--auth-type` If you have 2FA enabled for your npm account, `bun publish` will prompt you for a one-time password. This can be done through a browser or the CLI. The `--auth-type` flag can be used to tell the npm registry which method you prefer. The possible values are `web` and `legacy`, with `web` being the default. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun publish --auth-type legacy ... This operation requires a one-time password. Enter OTP: 123456 ... ``` ### `--otp` Provide a one-time password directly to the CLI. If the password is valid, this will skip the extra prompt for a one-time password before publishing. Example usage: ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun publish --otp 123456 ``` `bun publish` respects the `NPM_CONFIG_TOKEN` environment variable which can be used when publishing in github actions or automated workflows. *** ## CLI Usage ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun publish dist ``` ### Publishing Options The `--access` flag can be used to set the access level of the package being published. The access level can be one of `public` or `restricted`. Unscoped packages are always public, and attempting to publish an unscoped package with `--access restricted` will result in an error. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun publish --access public ``` `--access` can also be set in the `publishConfig` field of your `package.json`. ```json package.json icon="file-json" theme={"theme":{"light":"github-light","dark":"dracula"}} { "publishConfig": { "access": "restricted" // [!code ++] } } ``` Set the tag of the package version being published. By default, the tag is `latest`. The initial version of a package is always given the `latest` tag in addition to the specified tag. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun publish --tag alpha ``` `--tag` can also be set in the `publishConfig` field of your `package.json`. ```json package.json file="file-json" theme={"theme":{"light":"github-light","dark":"dracula"}} { "publishConfig": { "tag": "next" // [!code ++] } } ``` The `--dry-run` flag can be used to simulate the publish process without actually publishing the package. This is useful for verifying the contents of the published package without actually publishing the package. ```sh theme={"theme":{"light":"github-light","dark":"dracula"}} bun publish --dry-run ``` Specify the level of gzip compression to use when packing the package. Only applies to `bun publish` without a tarball path argument. Values range from `0` to `9` (default is `9`). If you have 2FA enabled for your npm account, `bun publish` will prompt you for a one-time password. This can be done through a browser or the CLI. The `--auth-type` flag can be used to tell the npm registry which method you prefer. The possible values are `web` and `legacy`, with `web` being the default. ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun publish --auth-type legacy ... This operation requires a one-time password. Enter OTP: 123456 ... ``` Provide a one-time password directly to the CLI. If the password is valid, this will skip the extra prompt for a one-time password before publishing. Example usage: ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun publish --otp 123456 ``` `bun publish` respects the `NPM_CONFIG_TOKEN` environment variable which can be used when publishing in github actions or automated workflows. ### Registry Configuration #### Custom Registry Specify registry URL, overriding .npmrc and bunfig.toml ```bash theme={"theme":{"light":"github-light","dark":"dracula"}} bun publish --registry https://my-private-registry.com ``` #### SSL Certificates Provide Certificate Authority signing certificate Path to Certificate Authority certificate file ```bash Inline Certificate theme={"theme":{"light":"github-light","dark":"dracula"}} bun publish --ca "-----BEGIN CERTIFICATE-----..." ``` ```bash Certificate File theme={"theme":{"light":"github-light","dark":"dracula"}} bun publish --cafile ./ca-cert.pem ``` ### Publishing Options #### Dependency Management Don't install devDependencies Exclude dependency types: `dev`, `optional`, or `peer` Always request the latest versions from the registry & reinstall all dependencies #### Script Control Skip lifecycle scripts during packing and publishing Add packages to trustedDependencies and run their scripts **Lifecycle Scripts** — When providing a pre-built tarball, lifecycle scripts (prepublishOnly, prepack, etc.) are not executed. Scripts only run when Bun packs the package itself. #### File Management Don't update package.json or lockfile Disallow changes to lockfile Generate yarn.lock file (yarn v1 compatible) #### Performance Platform optimizations: `clonefile` (default), `hardlink`, `symlink`, or `copyfile` Maximum concurrent network requests Maximum concurrent lifecycle scripts #### Output Control Suppress all output Show detailed logging Hide progress bar Don't print publish summary # bun remove Source: https://bun.com/docs/pm/cli/remove Remove dependencies from your project ## Basic Usage ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun remove ts-node ``` *** ## CLI Usage ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun remove ``` ### General Information Print this help menu. Alias: -h ### Configuration Specify path to config file (bunfig.toml). Alias: -c ### Package.json Interaction Don't update package.json or save a lockfile Save to package.json (true by default) Add to trustedDependencies in the project's package.json and install the package(s) ### Lockfile Behavior Write a yarn.lock file (yarn v1). Alias: -y Disallow changes to lockfile Save a text-based lockfile Generate a lockfile without installing dependencies ### Dependency Filtering Don't install devDependencies. Alias: -p Exclude dev, optional, or peer dependencies from install ### Network & Registry Provide a Certificate Authority signing certificate Same as --ca, but as a file path to the certificate Use a specific registry by default, overriding .npmrc, bunfig.toml and environment variables ### Execution Control & Validation Don't install anything Always request the latest versions from the registry & reinstall all dependencies. Alias:{" "} -f Skip verifying integrity of newly downloaded packages ### Output & Logging Don't log anything Excessively verbose logging Disable the progress bar Don't print a summary ### Caching Store & load cached data from a specific directory path Ignore manifest cache entirely ### Script Execution Skip lifecycle scripts in the project's package.json (dependency scripts are never run) Maximum number of concurrent jobs for lifecycle scripts (default 5) ### Scope & Path Install globally. Alias: -g Set a specific cwd ### Advanced & Performance Platform-specific optimizations for installing dependencies. Possible values:{" "} clonefile (default), hardlink, symlink,{" "} copyfile Maximum number of concurrent network requests (default 48) # bun update Source: https://bun.com/docs/pm/cli/update Update dependencies to latest versions To upgrade your Bun CLI version, see [`bun upgrade`](/installation#upgrading). To update all dependencies to the latest version: ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun update ``` To update a specific dependency to the latest version: ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun update [package] ``` ## `--interactive` For a more controlled update experience, use the `--interactive` flag to select which packages to update: ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun update --interactive bun update -i ``` This launches an interactive terminal interface that shows all outdated packages with their current and target versions. You can then select which packages to update. ### Interactive Interface The interface displays packages grouped by dependency type: ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun update --interactive bun update -i ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} ? Select packages to update - Space to toggle, Enter to confirm, a to select all, n to select none, i to invert, l to toggle latest dependencies Current Target Latest □ react 17.0.2 18.2.0 18.3.1 □ lodash 4.17.20 4.17.21 4.17.21 devDependencies Current Target Latest □ typescript 4.8.0 5.0.0 5.3.3 □ @types/node 16.11.7 18.0.0 20.11.5 optionalDependencies Current Target Latest □ some-optional-package 1.0.0 1.1.0 1.2.0 ``` **Sections:** * Packages are grouped under section headers: `dependencies`, `devDependencies`, `peerDependencies`, `optionalDependencies` * Each section shows column headers aligned with the package data **Columns:** * **Package**: Package name (may have suffix like ` dev`, ` peer`, ` optional` for clarity) * **Current**: Currently installed version * **Target**: Version that would be installed (respects semver constraints) * **Latest**: Latest available version ### Keyboard Controls **Selection:** * **Space**: Toggle package selection * **Enter**: Confirm selections and update * **a/A**: Select all packages * **n/N**: Select none * **i/I**: Invert selection **Navigation:** * **↑/↓ Arrow keys** or **j/k**: Move cursor * **l/L**: Toggle between target and latest version for current package **Exit:** * **Ctrl+C** or **Ctrl+D**: Cancel without updating ### Visual Indicators * **☑** Selected packages (will be updated) * **□** Unselected packages * **>** Current cursor position * **Colors**: Red (major), yellow (minor), green (patch) version changes * **Underlined**: Currently selected update target ### Package Grouping Packages are organized in sections by dependency type: * **dependencies** - Regular runtime dependencies * **devDependencies** - Development dependencies * **peerDependencies** - Peer dependencies * **optionalDependencies** - Optional dependencies Within each section, individual packages may have additional suffixes (` dev`, ` peer`, ` optional`) for extra clarity. ## `--recursive` Use the `--recursive` flag with `--interactive` to update dependencies across all workspaces in a monorepo: ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun update --interactive --recursive bun update -i -r ``` This displays an additional "Workspace" column showing which workspace each dependency belongs to. ## `--latest` By default, `bun update` will update to the latest version of a dependency that satisfies the version range specified in your `package.json`. To update to the latest version, regardless of if it's compatible with the current version range, use the `--latest` flag: ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun update --latest ``` In interactive mode, you can toggle individual packages between their target version (respecting semver) and latest version using the **l** key. For example, with the following `package.json`: ```json package.json icon="file-json" theme={"theme":{"light":"github-light","dark":"dracula"}} { "dependencies": { "react": "^17.0.2" } } ``` * `bun update` would update to a version that matches `17.x`. * `bun update --latest` would update to a version that matches `18.x` or later. *** ## CLI Usage ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun update ``` ### Update Strategy Always request the latest versions from the registry & reinstall all dependencies. Alias:{" "} -f Update packages to their latest versions ### Dependency Scope Don't install devDependencies. Alias: -p Install globally. Alias: -g Exclude dev, optional, or peer dependencies from install ### Project File Management Write a yarn.lock file (yarn v1). Alias: -y Don't update package.json or save a lockfile Save to package.json (true by default) Disallow changes to lockfile Save a text-based lockfile Generate a lockfile without installing dependencies ### Network & Registry Provide a Certificate Authority signing certificate Same as --ca, but as a file path to the certificate Use a specific registry by default, overriding .npmrc, bunfig.toml and environment variables Maximum number of concurrent network requests (default 48) ### Caching Store & load cached data from a specific directory path Ignore manifest cache entirely ### Output & Logging Don't log anything Excessively verbose logging Disable the progress bar Don't print a summary ### Script Execution Skip lifecycle scripts in the project's package.json (dependency scripts are never run) Maximum number of concurrent jobs for lifecycle scripts (default 5) ### Installation Controls Skip verifying integrity of newly downloaded packages Add to trustedDependencies in the project's package.json and install the package(s) Platform-specific optimizations for installing dependencies. Possible values:{" "} clonefile (default), hardlink, symlink,{" "} copyfile ### General & Environment Specify path to config file (bunfig.toml). Alias: -c Don't install anything Set a specific cwd Print this help menu. Alias: -h # bun why Source: https://bun.com/docs/pm/cli/why Explain why a package is installed The `bun why` command explains why a package is installed in your project by showing the dependency chain that led to its installation. ## Usage ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun why ``` ## Arguments * ``: The name of the package to explain. Supports glob patterns like `@org/*` or `*-lodash`. ## Options * `--top`: Show only the top-level dependencies instead of the complete dependency tree. * `--depth `: Maximum depth of the dependency tree to display. ## Examples Check why a specific package is installed: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun why react ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} react@18.2.0 └─ my-app@1.0.0 (requires ^18.0.0) ``` Check why all packages with a specific pattern are installed: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun why "@types/*" ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} @types/react@18.2.15 └─ dev my-app@1.0.0 (requires ^18.0.0) @types/react-dom@18.2.7 └─ dev my-app@1.0.0 (requires ^18.0.0) ``` Show only top-level dependencies: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun why express --top ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} express@4.18.2 └─ my-app@1.0.0 (requires ^4.18.2) ``` Limit the dependency tree depth: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun why express --depth 2 ``` ```txt theme={"theme":{"light":"github-light","dark":"dracula"}} express@4.18.2 └─ express-pollyfill@1.20.1 (requires ^4.18.2) └─ body-parser@1.20.1 (requires ^1.20.1) └─ accepts@1.3.8 (requires ^1.3.8) └─ (deeper dependencies hidden) ``` ## Understanding the Output The output shows: * The package name and version being queried * The dependency chain that led to its installation * The type of dependency (dev, peer, optional, or production) * The version requirement specified in each package's dependencies For nested dependencies, the command shows the complete dependency tree by default, with indentation indicating the relationship hierarchy. # bun --filter Source: https://bun.com/docs/pm/filter Select packages by pattern in a monorepo using the --filter flag The `--filter` (or `-F`) flag is used for selecting packages by pattern in a monorepo. Patterns can be used to match package names or package paths, with full glob syntax support. Currently `--filter` is supported by `bun install` and `bun outdated`, and can also be used to run scripts for multiple packages at once. *** ## Matching ### Package Name `--filter ` Name patterns select packages based on the package name, as specified in `package.json`. For example, if you have packages `pkg-a`, `pkg-b` and `other`, you can match all packages with `*`, only `pkg-a` and `pkg-b` with `pkg*`, and a specific package by providing the full name of the package. ### Package Path `--filter ./` Path patterns are specified by starting the pattern with `./`, and will select all packages in directories that match the pattern. For example, to match all packages in subdirectories of `packages`, you can use `--filter './packages/**'`. To match a package located in `packages/foo`, use `--filter ./packages/foo`. *** ## `bun install` and `bun outdated` Both `bun install` and `bun outdated` support the `--filter` flag. `bun install` by default will install dependencies for all packages in the monorepo. To install dependencies for specific packages, use `--filter`. Given a monorepo with workspaces `pkg-a`, `pkg-b`, and `pkg-c` under `./packages`: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} # Install dependencies for all workspaces except `pkg-c` bun install --filter '!pkg-c' # Install dependencies for packages in `./packages` (`pkg-a`, `pkg-b`, `pkg-c`) bun install --filter './packages/*' # Save as above, but exclude the root package.json bun install --filter '!./' --filter './packages/*' ``` Similarly, `bun outdated` will display outdated dependencies for all packages in the monorepo, and `--filter` can be used to restrict the command to a subset of the packages: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} # Display outdated dependencies for workspaces starting with `pkg-` bun outdated --filter 'pkg-*' # Display outdated dependencies for only the root package.json bun outdated --filter './' ``` For more information on both these commands, see [`bun install`](/pm/cli/install) and [`bun outdated`](/pm/cli/outdated). *** ## Running scripts with `--filter` Use the `--filter` flag to execute scripts in multiple packages at once: ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun --filter ``` It will output a new HTML file with the bundled assets: ```html dist/output.html theme={"theme":{"light":"github-light","dark":"dracula"}} Local image External image ``` Under the hood, it uses [`lol-html`](https://github.com/cloudflare/lol-html) to extract script and link tags as entrypoints, and other assets as external. Currently, the list of selectors is: * `audio[src]` * `iframe[src]` * `img[src]` * `img[srcset]` * `link:not([rel~='stylesheet']):not([rel~='modulepreload']):not([rel~='manifest']):not([rel~='icon']):not([rel~='apple-touch-icon'])[href]` * `link[as='font'][href], link[type^='font/'][href]` * `link[as='image'][href]` * `link[as='style'][href]` * `link[as='video'][href], link[as='audio'][href]` * `link[as='worker'][href]` * `link[rel='icon'][href], link[rel='apple-touch-icon'][href]` * `link[rel='manifest'][href]` * `link[rel='stylesheet'][href]` * `script[src]` * `source[src]` * `source[srcset]` * `video[poster]` * `video[src]` **HTML Loader Behavior in Different Contexts** The `html` loader behaves differently depending on how it's used: 1. **Static Build:** When you run `bun build ./index.html`, Bun produces a static site with all assets bundled and hashed. 2. **Runtime:** When you run `bun run server.ts` (where `server.ts` imports an HTML file), Bun bundles assets on-the-fly during development, enabling features like hot module replacement. 3. **Full-stack Build:** When you run `bun build --target=bun server.ts` (where `server.ts` imports an HTML file), the import resolves to a manifest object that `Bun.serve` uses to efficiently serve pre-bundled assets in production. ### `sh` loader **Bun Shell loader**. Default for `.sh` files This loader is used to parse [Bun Shell](/runtime/shell) scripts. It's only supported when starting Bun itself, so it's not available in the bundler or in the runtime. ```sh theme={"theme":{"light":"github-light","dark":"dracula"}} bun run ./script.sh ``` ### `file` **File loader**. Default for all unrecognized file types. The file loader resolves the import as a *path/URL* to the imported file. It's commonly used for referencing media or font assets. ```ts logo.ts theme={"theme":{"light":"github-light","dark":"dracula"}} import logo from "./logo.svg"; console.log(logo); ``` *In the runtime*, Bun checks that the `logo.svg` file exists and converts it to an absolute path to the location of `logo.svg` on disk. ```bash theme={"theme":{"light":"github-light","dark":"dracula"}} bun run logo.ts /path/to/project/logo.svg ``` *In the bundler*, things are slightly different. The file is copied into `outdir` as-is, and the import is resolved as a relative path pointing to the copied file. ```ts Output theme={"theme":{"light":"github-light","dark":"dracula"}} var logo = "./logo.svg"; console.log(logo); ``` If a value is specified for `publicPath`, the import will use value as a prefix to construct an absolute path/URL. | Public path | Resolved import | | ---------------------------- | ---------------------------------- | | `""` (default) | `/logo.svg` | | `"/assets"` | `/assets/logo.svg` | | `"https://cdn.example.com/"` | `https://cdn.example.com/logo.svg` | The location and file name of the copied file is determined by the value of [`naming.asset`](/bundler#naming). This loader is copied into the `outdir` as-is. The name of the copied file is determined using the value of `naming.asset`. If you're using TypeScript, you may get an error like this: ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} // TypeScript error // Cannot find module './logo.svg' or its corresponding type declarations. ``` This can be fixed by creating `*.d.ts` file anywhere in your project (any name will work) with the following contents: ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} declare module "*.svg" { const content: string; export default content; } ``` This tells TypeScript that any default imports from `.svg` should be treated as a string. # Glob Source: https://bun.com/docs/runtime/glob Use Bun's fast native implementation of file globbing ## Quickstart **Scan a directory for files matching `*.ts`**: ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} import { Glob } from "bun"; const glob = new Glob("**/*.ts"); // Scans the current working directory and each of its sub-directories recursively for await (const file of glob.scan(".")) { console.log(file); // => "index.ts" } ``` **Match a string against a glob pattern**: ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} import { Glob } from "bun"; const glob = new Glob("*.ts"); glob.match("index.ts"); // => true glob.match("index.js"); // => false ``` `Glob` is a class which implements the following interface: ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} class Glob { scan(root: string | ScanOptions): AsyncIterable; scanSync(root: string | ScanOptions): Iterable; match(path: string): boolean; } interface ScanOptions { /** * The root directory to start matching from. Defaults to `process.cwd()` */ cwd?: string; /** * Allow patterns to match entries that begin with a period (`.`). * * @default false */ dot?: boolean; /** * Return the absolute path for entries. * * @default false */ absolute?: boolean; /** * Indicates whether to traverse descendants of symbolic link directories. * * @default false */ followSymlinks?: boolean; /** * Throw an error when symbolic link is broken * * @default false */ throwErrorOnBrokenSymlink?: boolean; /** * Return only files. * * @default true */ onlyFiles?: boolean; } ``` ## Supported Glob Patterns Bun supports the following glob patterns: ### `?` - Match any single character ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const glob = new Glob("???.ts"); glob.match("foo.ts"); // => true glob.match("foobar.ts"); // => false ``` ### `*` - Matches zero or more characters, except for path separators (`/` or `\`) ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const glob = new Glob("*.ts"); glob.match("index.ts"); // => true glob.match("src/index.ts"); // => false ``` ### `**` - Match any number of characters including `/` ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const glob = new Glob("**/*.ts"); glob.match("index.ts"); // => true glob.match("src/index.ts"); // => true glob.match("src/index.js"); // => false ``` ### `[ab]` - Matches one of the characters contained in the brackets, as well as character ranges ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const glob = new Glob("ba[rz].ts"); glob.match("bar.ts"); // => true glob.match("baz.ts"); // => true glob.match("bat.ts"); // => false ``` You can use character ranges (e.g `[0-9]`, `[a-z]`) as well as the negation operators `^` or `!` to match anything *except* the characters contained within the braces (e.g `[^ab]`, `[!a-z]`) ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const glob = new Glob("ba[a-z][0-9][^4-9].ts"); glob.match("bar01.ts"); // => true glob.match("baz83.ts"); // => true glob.match("bat22.ts"); // => true glob.match("bat24.ts"); // => false glob.match("ba0a8.ts"); // => false ``` ### `{a,b,c}` - Match any of the given patterns ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const glob = new Glob("{a,b,c}.ts"); glob.match("a.ts"); // => true glob.match("b.ts"); // => true glob.match("c.ts"); // => true glob.match("d.ts"); // => false ``` These match patterns can be deeply nested (up to 10 levels), and contain any of the wildcards from above. ### `!` - Negates the result at the start of a pattern ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const glob = new Glob("!index.ts"); glob.match("index.ts"); // => false glob.match("foo.ts"); // => true ``` ### `\` - Escapes any of the special characters above ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const glob = new Glob("\\!index.ts"); glob.match("!index.ts"); // => true glob.match("index.ts"); // => false ``` ## Node.js `fs.glob()` compatibility Bun also implements Node.js's `fs.glob()` functions with additional features: ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} import { glob, globSync, promises } from "node:fs"; // Array of patterns const files = await promises.glob(["**/*.ts", "**/*.js"]); // Exclude patterns const filtered = await promises.glob("**/*", { exclude: ["node_modules/**", "*.test.*"], }); ``` All three functions (`fs.glob()`, `fs.globSync()`, `fs.promises.glob()`) support: * Array of patterns as the first argument * `exclude` option to filter results # Globals Source: https://bun.com/docs/runtime/globals Use Bun's global objects Bun implements the following globals. | Global | Source | Notes | | ----------------------------------------------------------------------------------------------------------------------- | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController) | Web | | | [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) | Web | | | [`alert`](https://developer.mozilla.org/en-US/docs/Web/API/Window/alert) | Web | Intended for command-line tools | | [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) | Web | | | [`Buffer`](https://nodejs.org/api/buffer.html#class-buffer) | Node.js | See [Node.js > `Buffer`](/runtime/nodejs-compat#node-buffer) | | `Bun` | Bun | Subject to change as additional APIs are added | | [`ByteLengthQueuingStrategy`](https://developer.mozilla.org/en-US/docs/Web/API/ByteLengthQueuingStrategy) | Web | | | [`confirm`](https://developer.mozilla.org/en-US/docs/Web/API/Window/confirm) | Web | Intended for command-line tools | | [`__dirname`](https://nodejs.org/api/globals.html#__dirname) | Node.js | | | [`__filename`](https://nodejs.org/api/globals.html#__filename) | Node.js | | | [`atob()`](https://developer.mozilla.org/en-US/docs/Web/API/atob) | Web | | | [`btoa()`](https://developer.mozilla.org/en-US/docs/Web/API/btoa) | Web | | | `BuildMessage` | Bun | | | [`clearImmediate()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/clearImmediate) | Web | | | [`clearInterval()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/clearInterval) | Web | | | [`clearTimeout()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/clearTimeout) | Web | | | [`console`](https://developer.mozilla.org/en-US/docs/Web/API/console) | Web | | | [`CountQueuingStrategy`](https://developer.mozilla.org/en-US/docs/Web/API/CountQueuingStrategy) | Web | | | [`Crypto`](https://developer.mozilla.org/en-US/docs/Web/API/Crypto) | Web | | | [`crypto`](https://developer.mozilla.org/en-US/docs/Web/API/crypto) | Web | | | [`CryptoKey`](https://developer.mozilla.org/en-US/docs/Web/API/CryptoKey) | Web | | | [`CustomEvent`](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent) | Web | | | [`Event`](https://developer.mozilla.org/en-US/docs/Web/API/Event) | Web | Also [`ErrorEvent`](https://developer.mozilla.org/en-US/docs/Web/API/ErrorEvent) [`CloseEvent`](https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent) [`MessageEvent`](https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent). | | [`EventTarget`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget) | Web | | | [`exports`](https://nodejs.org/api/globals.html#exports) | Node.js | | | [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/fetch) | Web | | | [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) | Web | | | [`global`](https://nodejs.org/api/globals.html#global) | Node.js | See [Node.js > `global`](/runtime/nodejs-compat#global). | | [`globalThis`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/globalThis) | Cross-platform | Aliases to `global` | | [`Headers`](https://developer.mozilla.org/en-US/docs/Web/API/Headers) | Web | | | [`HTMLRewriter`](/runtime/html-rewriter) | Cloudflare | | | [`JSON`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON) | Web | | | [`MessageEvent`](https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent) | Web | | | [`module`](https://nodejs.org/api/globals.html#module) | Node.js | | | [`performance`](https://developer.mozilla.org/en-US/docs/Web/API/performance) | Web | | | [`process`](https://nodejs.org/api/process.html) | Node.js | See [Node.js > `process`](/runtime/nodejs-compat#node-process) | | [`prompt`](https://developer.mozilla.org/en-US/docs/Web/API/Window/prompt) | Web | Intended for command-line tools | | [`queueMicrotask()`](https://developer.mozilla.org/en-US/docs/Web/API/queueMicrotask) | Web | | | [`ReadableByteStreamController`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableByteStreamController) | Web | | | [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) | Web | | | [`ReadableStreamDefaultController`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStreamDefaultController) | Web | | | [`ReadableStreamDefaultReader`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStreamDefaultReader) | Web | | | [`reportError`](https://developer.mozilla.org/en-US/docs/Web/API/reportError) | Web | | | [`require()`](https://nodejs.org/api/globals.html#require) | Node.js | | | `ResolveMessage` | Bun | | | [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) | Web | | | [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) | Web | | | [`setImmediate()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/setImmediate) | Web | | | [`setInterval()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/setInterval) | Web | | | [`setTimeout()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/setTimeout) | Web | | | [`ShadowRealm`](https://github.com/tc39/proposal-shadowrealm) | Web | Stage 3 proposal | | [`SubtleCrypto`](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto) | Web | | | [`DOMException`](https://developer.mozilla.org/en-US/docs/Web/API/DOMException) | Web | | | [`TextDecoder`](https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder) | Web | | | [`TextEncoder`](https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder) | Web | | | [`TransformStream`](https://developer.mozilla.org/en-US/docs/Web/API/TransformStream) | Web | | | [`TransformStreamDefaultController`](https://developer.mozilla.org/en-US/docs/Web/API/TransformStreamDefaultController) | Web | | | [`URL`](https://developer.mozilla.org/en-US/docs/Web/API/URL) | Web | | | [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams) | Web | | | [`WebAssembly`](https://nodejs.org/api/globals.html#webassembly) | Web | | | [`WritableStream`](https://developer.mozilla.org/en-US/docs/Web/API/WritableStream) | Web | | | [`WritableStreamDefaultController`](https://developer.mozilla.org/en-US/docs/Web/API/WritableStreamDefaultController) | Web | | | [`WritableStreamDefaultWriter`](https://developer.mozilla.org/en-US/docs/Web/API/WritableStreamDefaultWriter) | Web | | # Hashing Source: https://bun.com/docs/runtime/hashing Bun provides a set of utility functions for hashing and verifying passwords with various cryptographically secure algorithms Bun implements the `createHash` and `createHmac` functions from [`node:crypto`](https://nodejs.org/api/crypto.html) in addition to the Bun-native APIs documented below. *** ## `Bun.password` `Bun.password` is a collection of utility functions for hashing and verifying passwords with various cryptographically secure algorithms. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const password = "super-secure-pa$$word"; const hash = await Bun.password.hash(password); // => $argon2id$v=19$m=65536,t=2,p=1$tFq+9AVr1bfPxQdh6E8DQRhEXg/M/SqYCNu6gVdRRNs$GzJ8PuBi+K+BVojzPfS5mjnC8OpLGtv8KJqF99eP6a4 const isMatch = await Bun.password.verify(password, hash); // => true ``` The second argument to `Bun.password.hash` accepts a params object that lets you pick and configure the hashing algorithm. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const password = "super-secure-pa$$word"; // use argon2 (default) const argonHash = await Bun.password.hash(password, { algorithm: "argon2id", // "argon2id" | "argon2i" | "argon2d" memoryCost: 4, // memory usage in kibibytes timeCost: 3, // the number of iterations }); // use bcrypt const bcryptHash = await Bun.password.hash(password, { algorithm: "bcrypt", cost: 4, // number between 4-31 }); ``` The algorithm used to create the hash is stored in the hash itself. When using `bcrypt`, the returned hash is encoded in [Modular Crypt Format](https://passlib.readthedocs.io/en/stable/modular_crypt_format.html) for compatibility with most existing `bcrypt` implementations; with `argon2` the result is encoded in the newer [PHC format](https://github.com/P-H-C/phc-string-format/blob/master/phc-sf-spec.md). The `verify` function automatically detects the algorithm based on the input hash and use the correct verification method. It can correctly infer the algorithm from both PHC- or MCF-encoded hashes. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const password = "super-secure-pa$$word"; const hash = await Bun.password.hash(password, { /* config */ }); const isMatch = await Bun.password.verify(password, hash); // => true ``` Synchronous versions of all functions are also available. Keep in mind that these functions are computationally expensive, so using a blocking API may degrade application performance. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const password = "super-secure-pa$$word"; const hash = Bun.password.hashSync(password, { /* config */ }); const isMatch = Bun.password.verifySync(password, hash); // => true ``` ### Salt When you use `Bun.password.hash`, a salt is automatically generated and included in the hash. ### bcrypt - Modular Crypt Format In the following [Modular Crypt Format](https://passlib.readthedocs.io/en/stable/modular_crypt_format.html) hash (used by `bcrypt`): Input: ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} await Bun.password.hash("hello", { algorithm: "bcrypt", }); ``` Output: ```sh theme={"theme":{"light":"github-light","dark":"dracula"}} $2b$10$Lyj9kHYZtiyfxh2G60TEfeqs7xkkGiEFFDi3iJGc50ZG/XJ1sxIFi; ``` The format is composed of: * `bcrypt`: `$2b` * `rounds`: `$10` - rounds (log10 of the actual number of rounds) * `salt`: `$Lyj9kHYZtiyfxh2G60TEfeqs7xkkGiEFFDi3iJGc50ZG/XJ1sxIFi` * `hash`: `$GzJ8PuBi+K+BVojzPfS5mjnC8OpLGtv8KJqF99eP6a4` By default, the bcrypt library truncates passwords longer than 72 bytes. In Bun, if you pass `Bun.password.hash` a password longer than 72 bytes and use the `bcrypt` algorithm, the password will be hashed via SHA-512 before being passed to bcrypt. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} await Bun.password.hash("hello".repeat(100), { algorithm: "bcrypt", }); ``` So instead of sending bcrypt a 500-byte password silently truncated to 72 bytes, Bun will hash the password using SHA-512 and send the hashed password to bcrypt (only if it exceeds 72 bytes). This is a more secure default behavior. ### argon2 - PHC format In the following [PHC format](https://github.com/P-H-C/phc-string-format/blob/master/phc-sf-spec.md) hash (used by `argon2`): Input: ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} await Bun.password.hash("hello", { algorithm: "argon2id", }); ``` Output: ```sh theme={"theme":{"light":"github-light","dark":"dracula"}} $argon2id$v=19$m=65536,t=2,p=1$xXnlSvPh4ym5KYmxKAuuHVlDvy2QGHBNuI6bJJrRDOs$2YY6M48XmHn+s5NoBaL+ficzXajq2Yj8wut3r0vnrwI ``` The format is composed of: * `algorithm`: `$argon2id` * `version`: `$v=19` * `memory cost`: `65536` * `iterations`: `t=2` * `parallelism`: `p=1` * `salt`: `$xXnlSvPh4ym5KYmxKAuuHVlDvy2QGHBNuI6bJJrRDOs` * `hash`: `$2YY6M48XmHn+s5NoBaL+ficzXajq2Yj8wut3r0vnrwI` *** ## `Bun.hash` `Bun.hash` is a collection of utilities for *non-cryptographic* hashing. Non-cryptographic hashing algorithms are optimized for speed of computation over collision-resistance or security. The standard `Bun.hash` functions uses [Wyhash](https://github.com/wangyi-fudan/wyhash) to generate a 64-bit hash from an input of arbitrary size. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} Bun.hash("some data here"); // 11562320457524636935n ``` The input can be a string, `TypedArray`, `DataView`, `ArrayBuffer`, or `SharedArrayBuffer`. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const arr = new Uint8Array([1, 2, 3, 4]); Bun.hash("some data here"); Bun.hash(arr); Bun.hash(arr.buffer); Bun.hash(new DataView(arr.buffer)); ``` Optionally, an integer seed can be specified as the second parameter. For 64-bit hashes seeds above `Number.MAX_SAFE_INTEGER` should be given as BigInt to avoid loss of precision. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} Bun.hash("some data here", 1234); // 15724820720172937558n ``` Additional hashing algorithms are available as properties on `Bun.hash`. The API is the same for each, only changing the return type from number for 32-bit hashes to bigint for 64-bit hashes. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} Bun.hash.wyhash("data", 1234); // equivalent to Bun.hash() Bun.hash.crc32("data", 1234); Bun.hash.adler32("data", 1234); Bun.hash.cityHash32("data", 1234); Bun.hash.cityHash64("data", 1234); Bun.hash.xxHash32("data", 1234); Bun.hash.xxHash64("data", 1234); Bun.hash.xxHash3("data", 1234); Bun.hash.murmur32v3("data", 1234); Bun.hash.murmur32v2("data", 1234); Bun.hash.murmur64v2("data", 1234); Bun.hash.rapidhash("data", 1234); ``` *** ## `Bun.CryptoHasher` `Bun.CryptoHasher` is a general-purpose utility class that lets you incrementally compute a hash of string or binary data using a range of cryptographic hash algorithms. The following algorithms are supported: * `"blake2b256"` * `"blake2b512"` * `"md4"` * `"md5"` * `"ripemd160"` * `"sha1"` * `"sha224"` * `"sha256"` * `"sha384"` * `"sha512"` * `"sha512-224"` * `"sha512-256"` * `"sha3-224"` * `"sha3-256"` * `"sha3-384"` * `"sha3-512"` * `"shake128"` * `"shake256"` ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const hasher = new Bun.CryptoHasher("sha256"); hasher.update("hello world"); hasher.digest(); // Uint8Array(32) [ , , ... ] ``` Once initialized, data can be incrementally fed to to the hasher using `.update()`. This method accepts `string`, `TypedArray`, and `ArrayBuffer`. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const hasher = new Bun.CryptoHasher("sha256"); hasher.update("hello world"); hasher.update(new Uint8Array([1, 2, 3])); hasher.update(new ArrayBuffer(10)); ``` If a `string` is passed, an optional second parameter can be used to specify the encoding (default `'utf-8'`). The following encodings are supported: | Category | Encodings | | -------------------------- | ------------------------------------------- | | Binary encodings | `"base64"` `"base64url"` `"hex"` `"binary"` | | Character encodings | `"utf8"` `"utf-8"` `"utf16le"` `"latin1"` | | Legacy character encodings | `"ascii"` `"binary"` `"ucs2"` `"ucs-2"` | ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} hasher.update("hello world"); // defaults to utf8 hasher.update("hello world", "hex"); hasher.update("hello world", "base64"); hasher.update("hello world", "latin1"); ``` After the data has been feed into the hasher, a final hash can be computed using `.digest()`. By default, this method returns a `Uint8Array` containing the hash. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const hasher = new Bun.CryptoHasher("sha256"); hasher.update("hello world"); hasher.digest(); // => Uint8Array(32) [ 185, 77, 39, 185, 147, ... ] ``` The `.digest()` method can optionally return the hash as a string. To do so, specify an encoding: ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} hasher.digest("base64"); // => "uU0nuZNNPgilLlLX2n2r+sSE7+N6U4DukIj3rOLvzek=" hasher.digest("hex"); // => "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9" ``` Alternatively, the method can write the hash into a pre-existing `TypedArray` instance. This may be desirable in some performance-sensitive applications. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const arr = new Uint8Array(32); hasher.digest(arr); console.log(arr); // => Uint8Array(32) [ 185, 77, 39, 185, 147, ... ] ``` ### HMAC in `Bun.CryptoHasher` `Bun.CryptoHasher` can be used to compute HMAC digests. To do so, pass the key to the constructor. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const hasher = new Bun.CryptoHasher("sha256", "secret-key"); hasher.update("hello world"); console.log(hasher.digest("hex")); // => "095d5a21fe6d0646db223fdf3de6436bb8dfb2fab0b51677ecf6441fcf5f2a67" ``` When using HMAC, a more limited set of algorithms are supported: * `"blake2b512"` * `"md5"` * `"sha1"` * `"sha224"` * `"sha256"` * `"sha384"` * `"sha512-224"` * `"sha512-256"` * `"sha512"` Unlike the non-HMAC `Bun.CryptoHasher`, the HMAC `Bun.CryptoHasher` instance is not reset after `.digest()` is called, and attempting to use the same instance again will throw an error. Other methods like `.copy()` and `.update()` are supported (as long as it's before `.digest()`), but methods like `.digest()` that finalize the hasher are not. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const hasher = new Bun.CryptoHasher("sha256", "secret-key"); hasher.update("hello world"); const copy = hasher.copy(); copy.update("!"); console.log(copy.digest("hex")); // => "3840176c3d8923f59ac402b7550404b28ab11cb0ef1fa199130a5c37864b5497" console.log(hasher.digest("hex")); // => "095d5a21fe6d0646db223fdf3de6436bb8dfb2fab0b51677ecf6441fcf5f2a67" ``` # HTMLRewriter Source: https://bun.com/docs/runtime/html-rewriter Use Bun's HTMLRewriter to transform HTML documents with CSS selectors HTMLRewriter lets you use CSS selectors to transform HTML documents. It works with `Request`, `Response`, as well as `string`. Bun's implementation is based on Cloudflare's [lol-html](https://github.com/cloudflare/lol-html). *** ## Usage A common usecase is rewriting URLs in HTML content. Here's an example that rewrites image sources and link URLs to use a CDN domain: ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} // Replace all images with a rickroll const rewriter = new HTMLRewriter().on("img", { element(img) { // Famous rickroll video thumbnail img.setAttribute("src", "https://img.youtube.com/vi/dQw4w9WgXcQ/maxresdefault.jpg"); // Wrap the image in a link to the video img.before('', { html: true, }); img.after("", { html: true }); // Add some fun alt text img.setAttribute("alt", "Definitely not a rickroll"); }, }); // An example HTML document const html = ` `; const result = rewriter.transform(html); console.log(result); ``` This replaces all images with a thumbnail of Rick Astley and wraps each `` in a link, producing a diff like this: ```html theme={"theme":{"light":"github-light","dark":"dracula"}} // [!code --] // [!code --] // [!code --] // [!code ++] Definitely not a rickroll // [!code ++] // [!code ++] // [!code ++] Definitely not a rickroll // [!code ++] // [!code ++] // [!code ++] Definitely not a rickroll // [!code ++] // [!code ++] ``` Now every image on the page will be replaced with a thumbnail of Rick Astley, and clicking any image will lead to [a very famous video](https://www.youtube.com/watch?v=dQw4w9WgXcQ). ### Input types HTMLRewriter can transform HTML from various sources. The input is automatically handled based on its type: ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} // From Response rewriter.transform(new Response("
content
")); // From string rewriter.transform("
content
"); // From ArrayBuffer rewriter.transform(new TextEncoder().encode("
content
").buffer); // From Blob rewriter.transform(new Blob(["
content
"])); // From File rewriter.transform(Bun.file("index.html")); ``` Note that Cloudflare Workers implementation of HTMLRewriter only supports `Response` objects. ### Element Handlers The `on(selector, handlers)` method allows you to register handlers for HTML elements that match a CSS selector. The handlers are called for each matching element during parsing: ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} rewriter.on("div.content", { // Handle elements element(element) { element.setAttribute("class", "new-content"); element.append("

New content

", { html: true }); }, // Handle text nodes text(text) { text.replace("new text"); }, // Handle comments comments(comment) { comment.remove(); }, }); ``` The handlers can be asynchronous and return a Promise. Note that async operations will block the transformation until they complete: ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} rewriter.on("div", { async element(element) { await Bun.sleep(1000); element.setInnerContent("replace", { html: true }); }, }); ``` ### CSS Selector Support The `on()` method supports a wide range of CSS selectors: ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} // Tag selectors rewriter.on("p", handler); // Class selectors rewriter.on("p.red", handler); // ID selectors rewriter.on("h1#header", handler); // Attribute selectors rewriter.on("p[data-test]", handler); // Has attribute rewriter.on('p[data-test="one"]', handler); // Exact match rewriter.on('p[data-test="one" i]', handler); // Case-insensitive rewriter.on('p[data-test="one" s]', handler); // Case-sensitive rewriter.on('p[data-test~="two"]', handler); // Word match rewriter.on('p[data-test^="a"]', handler); // Starts with rewriter.on('p[data-test$="1"]', handler); // Ends with rewriter.on('p[data-test*="b"]', handler); // Contains rewriter.on('p[data-test|="a"]', handler); // Dash-separated // Combinators rewriter.on("div span", handler); // Descendant rewriter.on("div > span", handler); // Direct child // Pseudo-classes rewriter.on("p:nth-child(2)", handler); rewriter.on("p:first-child", handler); rewriter.on("p:nth-of-type(2)", handler); rewriter.on("p:first-of-type", handler); rewriter.on("p:not(:first-child)", handler); // Universal selector rewriter.on("*", handler); ``` ### Element Operations Elements provide various methods for manipulation. All modification methods return the element instance for chaining: ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} rewriter.on("div", { element(el) { // Attributes el.setAttribute("class", "new-class").setAttribute("data-id", "123"); const classAttr = el.getAttribute("class"); // "new-class" const hasId = el.hasAttribute("id"); // boolean el.removeAttribute("class"); // Content manipulation el.setInnerContent("New content"); // Escapes HTML by default el.setInnerContent("

HTML content

", { html: true }); // Parses HTML el.setInnerContent(""); // Clear content // Position manipulation el.before("Content before").after("Content after").prepend("First child").append("Last child"); // HTML content insertion el.before("before", { html: true }) .after("after", { html: true }) .prepend("first", { html: true }) .append("last", { html: true }); // Removal el.remove(); // Remove element and contents el.removeAndKeepContent(); // Remove only the element tags // Properties console.log(el.tagName); // Lowercase tag name console.log(el.namespaceURI); // Element's namespace URI console.log(el.selfClosing); // Whether element is self-closing (e.g.
) console.log(el.canHaveContent); // Whether element can contain content (false for void elements like
) console.log(el.removed); // Whether element was removed // Attributes iteration for (const [name, value] of el.attributes) { console.log(name, value); } // End tag handling el.onEndTag(endTag => { endTag.before("Before end tag"); endTag.after("After end tag"); endTag.remove(); // Remove the end tag console.log(endTag.name); // Tag name in lowercase }); }, }); ``` ### Text Operations Text handlers provide methods for text manipulation. Text chunks represent portions of text content and provide information about their position in the text node: ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} rewriter.on("p", { text(text) { // Content console.log(text.text); // Text content console.log(text.lastInTextNode); // Whether this is the last chunk console.log(text.removed); // Whether text was removed // Manipulation text.before("Before text").after("After text").replace("New text").remove(); // HTML content insertion text .before("before", { html: true }) .after("after", { html: true }) .replace("replace", { html: true }); }, }); ``` ### Comment Operations Comment handlers allow comment manipulation with similar methods to text nodes: ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} rewriter.on("*", { comments(comment) { // Content console.log(comment.text); // Comment text comment.text = "New comment text"; // Set comment text console.log(comment.removed); // Whether comment was removed // Manipulation comment.before("Before comment").after("After comment").replace("New comment").remove(); // HTML content insertion comment .before("before", { html: true }) .after("after", { html: true }) .replace("replace", { html: true }); }, }); ``` ### Document Handlers The `onDocument(handlers)` method allows you to handle document-level events. These handlers are called for events that occur at the document level rather than within specific elements: ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} rewriter.onDocument({ // Handle doctype doctype(doctype) { console.log(doctype.name); // "html" console.log(doctype.publicId); // public identifier if present console.log(doctype.systemId); // system identifier if present }, // Handle text nodes text(text) { console.log(text.text); }, // Handle comments comments(comment) { console.log(comment.text); }, // Handle document end end(end) { end.append("", { html: true }); }, }); ``` ### Response Handling When transforming a Response: * The status code, headers, and other response properties are preserved * The body is transformed while maintaining streaming capabilities * Content-encoding (like gzip) is handled automatically * The original response body is marked as used after transformation * Headers are cloned to the new response ## Error Handling HTMLRewriter operations can throw errors in several cases: * Invalid selector syntax in `on()` method * Invalid HTML content in transformation methods * Stream errors when processing Response bodies * Memory allocation failures * Invalid input types (e.g., passing Symbol) * Body already used errors Errors should be caught and handled appropriately: ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} try { const result = rewriter.transform(input); // Process result } catch (error) { console.error("HTMLRewriter error:", error); } ``` *** ## See also You can also read the [Cloudflare documentation](https://developers.cloudflare.com/workers/runtime-apis/html-rewriter/), which this API is intended to be compatible with. # Cookies Source: https://bun.com/docs/runtime/http/cookies Work with cookies in HTTP requests and responses using Bun's built-in Cookie API. Bun provides a built-in API for working with cookies in HTTP requests and responses. The `BunRequest` object includes a `cookies` property that provides a `CookieMap` for easily accessing and manipulating cookies. When using `routes`, `Bun.serve()` automatically tracks `request.cookies.set` and applies them to the response. ## Reading cookies Read cookies from incoming requests using the `cookies` property on the `BunRequest` object: ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} Bun.serve({ routes: { "/profile": req => { // Access cookies from the request const userId = req.cookies.get("user_id"); const theme = req.cookies.get("theme") || "light"; return Response.json({ userId, theme, message: "Profile page", }); }, }, }); ``` ## Setting cookies To set cookies, use the `set` method on the `CookieMap` from the `BunRequest` object. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} Bun.serve({ routes: { "/login": req => { const cookies = req.cookies; // Set a cookie with various options cookies.set("user_id", "12345", { maxAge: 60 * 60 * 24 * 7, // 1 week httpOnly: true, secure: true, path: "/", }); // Add a theme preference cookie cookies.set("theme", "dark"); // Modified cookies from the request are automatically applied to the response return new Response("Login successful"); }, }, }); ``` `Bun.serve()` automatically tracks modified cookies from the request and applies them to the response. ## Deleting cookies To delete a cookie, use the `delete` method on the `request.cookies` (`CookieMap`) object: ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} Bun.serve({ routes: { "/logout": req => { // Delete the user_id cookie req.cookies.delete("user_id", { path: "/", }); return new Response("Logged out successfully"); }, }, }); ``` Deleted cookies become a `Set-Cookie` header on the response with the `maxAge` set to `0` and an empty `value`. # Error Handling Source: https://bun.com/docs/runtime/http/error-handling Learn how to handle errors in Bun's development server To activate development mode, set `development: true`. ```ts title="server.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} Bun.serve({ development: true, // [!code ++] fetch(req) { throw new Error("woops!"); }, }); ``` In development mode, Bun will surface errors in-browser with a built-in error page. Bun's built-in 500 page ### `error` callback To handle server-side errors, implement an `error` handler. This function should return a `Response` to serve to the client when an error occurs. This response will supersede Bun's default error page in `development` mode. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} Bun.serve({ fetch(req) { throw new Error("woops!"); }, error(error) { return new Response(`
${error}\n${error.stack}
`, { headers: { "Content-Type": "text/html", }, }); }, }); ``` [Learn more about debugging in Bun](/runtime/debugger) # Metrics Source: https://bun.com/docs/runtime/http/metrics Monitor server activity with built-in metrics ### `server.pendingRequests` and `server.pendingWebSockets` Monitor server activity with built-in counters: ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const server = Bun.serve({ fetch(req, server) { return new Response( `Active requests: ${server.pendingRequests}\n` + `Active WebSockets: ${server.pendingWebSockets}`, ); }, }); ``` ### `server.subscriberCount(topic)` Get count of subscribers for a WebSocket topic: ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const server = Bun.serve({ fetch(req, server) { const chatUsers = server.subscriberCount("chat"); return new Response(`${chatUsers} users in chat`); }, websocket: { message(ws) { ws.subscribe("chat"); }, }, }); ``` # Routing Source: https://bun.com/docs/runtime/http/routing Define routes in `Bun.serve` using static paths, parameters, and wildcards You can add routes to `Bun.serve()` by using the `routes` property (for static paths, parameters, and wildcards) or by handling unmatched requests with the [`fetch`](#fetch) method. `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/main/src/bun.js/bindings/decodeURIComponentSIMD.cpp#L21-L271) and [JavaScriptCore structure caching](https://github.com/oven-sh/bun/blob/main/src/bun.js/bindings/ServerRouteList.cpp#L100-L101) to push the performance limits of what modern hardware allows. ## Basic Setup ```ts title="server.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} Bun.serve({ routes: { "/": () => new Response("Home"), "/api": () => Response.json({ success: true }), "/users": async () => Response.json({ users: [] }), }, fetch() { return new Response("Unmatched route"); }, }); ``` 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`. This makes it easier to use the same code for both sending & receiving HTTP requests. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} // Simplified for brevity interface BunRequest extends Request { params: Record; readonly cookies: CookieMap; } ``` ## Asynchronous Routes ### Async/await You can use async/await in route handlers to return a `Promise`. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} import { sql, serve } from "bun"; serve({ port: 3001, routes: { "/api/version": async () => { const [version] = await sql`SELECT version()`; return Response.json(version); }, }, }); ``` ### Promise You can also return a `Promise` from a route handler. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} import { sql, serve } from "bun"; serve({ routes: { "/api/version": () => { return new Promise(resolve => { setTimeout(async () => { const [version] = await sql`SELECT version()`; resolve(Response.json(version)); }, 100); }); }, }, }); ``` *** ## Route precedence Routes are matched in order of specificity: 1. Exact routes (`/users/all`) 2. Parameter routes (`/users/:id`) 3. Wildcard routes (`/users/*`) 4. Global catch-all (`/*`) ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} Bun.serve({ routes: { // Most specific first "/api/users/me": () => new Response("Current user"), "/api/users/:id": req => new Response(`User ${req.params.id}`), "/api/*": () => new Response("API catch-all"), "/*": () => new Response("Global catch-all"), }, }); ``` *** ## Type-safe route parameters TypeScript parses route parameters when passed as a string literal, so that your editor will show autocomplete when accessing `request.params`. ```ts title="index.ts" theme={"theme":{"light":"github-light","dark":"dracula"}} import type { BunRequest } from "bun"; Bun.serve({ routes: { // TypeScript knows the shape of params when passed as a string literal "/orgs/:orgId/repos/:repoId": req => { const { orgId, repoId } = req.params; return Response.json({ orgId, repoId }); }, "/orgs/:orgId/repos/:repoId/settings": ( // optional: you can explicitly pass a type to BunRequest: req: BunRequest<"/orgs/:orgId/repos/:repoId/settings">, ) => { const { orgId, repoId } = req.params; return Response.json({ orgId, repoId }); }, }, }); ``` Percent-encoded route parameter values are automatically decoded. Unicode characters are supported. Invalid unicode is replaced with the unicode replacement character `&0xFFFD;`. ### Static responses 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: ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} Bun.serve({ routes: { // Health checks "/health": new Response("OK"), "/ready": new Response("Ready", { headers: { // Pass custom headers "X-Ready": "1", }, }), // Redirects "/blog": Response.redirect("https://bun.com/blog"), // API responses "/api/config": Response.json({ version: "1.0.0", env: "production", }), }, }); ``` 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. Static route responses are cached for the lifetime of the server object. To reload static routes, call `server.reload(options)`. ### File Responses vs Static Responses When serving files in routes, there are two distinct behaviors depending on whether you buffer the file content or serve it directly: ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} Bun.serve({ routes: { // Static route - content is buffered in memory at startup "/logo.png": new Response(await Bun.file("./logo.png").bytes()), // File route - content is read from filesystem on each request "/download.zip": new Response(Bun.file("./download.zip")), }, }); ``` **Static routes** (`new Response(await file.bytes())`) buffer content in memory at startup: * **Zero filesystem I/O** during requests - content served entirely from memory * **ETag support** - Automatically generates and validates ETags for caching * **If-None-Match** - Returns `304 Not Modified` when client ETag matches * **No 404 handling** - Missing files cause startup errors, not runtime 404s * **Memory usage** - Full file content stored in RAM * **Best for**: Small static assets, API responses, frequently accessed files **File routes** (`new Response(Bun.file(path))`) read from filesystem per request: * **Filesystem reads** on each request - checks file existence and reads content * **Built-in 404 handling** - Returns `404 Not Found` if file doesn't exist or becomes inaccessible * **Last-Modified support** - Uses file modification time for `If-Modified-Since` headers * **If-Modified-Since** - Returns `304 Not Modified` when file hasn't changed since client's cached version * **Range request support** - Automatically handles partial content requests with `Content-Range` headers * **Streaming transfers** - Uses buffered reader with backpressure handling for efficient memory usage * **Memory efficient** - Only buffers small chunks during transfer, not entire file * **Best for**: Large files, dynamic content, user uploads, files that change frequently *** ## Streaming files To stream a file, return a `Response` object with a `BunFile` object as the body. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} Bun.serve({ fetch(req) { return new Response(Bun.file("./hello.txt")); }, }); ``` ⚡️ **Speed** — Bun automatically uses the [`sendfile(2)`](https://man7.org/linux/man-pages/man2/sendfile.2.html) system call when possible, enabling zero-copy file transfers in the kernel—the fastest way to send files. You can send part of a file using the [`slice(start, end)`](https://developer.mozilla.org/en-US/docs/Web/API/Blob/slice) method on the `Bun.file` object. This automatically sets the `Content-Range` and `Content-Length` headers on the `Response` object. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} Bun.serve({ fetch(req) { // parse `Range` header const [start = 0, end = Infinity] = req.headers .get("Range") // Range: bytes=0-100 .split("=") // ["Range: bytes", "0-100"] .at(-1) // "0-100" .split("-") // ["0", "100"] .map(Number); // [0, 100] // return a slice of the file const bigFile = Bun.file("./big-video.mp4"); return new Response(bigFile.slice(start, end)); }, }); ``` *** ## `fetch` request handler 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`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise). ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} Bun.serve({ fetch(req) { const url = new URL(req.url); if (url.pathname === "/") return new Response("Home page!"); if (url.pathname === "/blog") return new Response("Blog!"); return new Response("404!"); }, }); ``` The `fetch` handler supports async/await: ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} import { sleep, serve } from "bun"; serve({ async fetch(req) { const start = performance.now(); await sleep(10); const end = performance.now(); return new Response(`Slept for ${end - start}ms`); }, }); ``` Promise-based responses are also supported: ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} Bun.serve({ fetch(req) { // Forward the request to another server. return fetch("https://example.com"); }, }); ``` You can also access the `Server` object from the `fetch` handler. It's the second argument passed to the `fetch` function. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} // `server` is passed in as the second argument to `fetch`. const server = Bun.serve({ fetch(req, server) { const ip = server.requestIP(req); return new Response(`Your IP is ${ip}`); }, }); ``` # Server Source: https://bun.com/docs/runtime/http/server Use `Bun.serve` to start a high-performance HTTP server in Bun ## Basic Setup ```ts title="index.ts" icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} const server = Bun.serve({ // `routes` requires Bun v1.2.3+ routes: { // Static routes "/api/status": new Response("OK"), // Dynamic routes "/users/:id": req => { return new Response(`Hello User ${req.params.id}!`); }, // Per-HTTP method handlers "/api/posts": { GET: () => new Response("List posts"), POST: async req => { const body = await req.json(); return Response.json({ created: true, ...body }); }, }, // Wildcard route for all routes that start with "/api/" and aren't otherwise matched "/api/*": Response.json({ message: "Not found" }, { status: 404 }), // Redirect from /blog/hello to /blog/hello/world "/blog/hello": Response.redirect("/blog/hello/world"), // Serve a file by buffering it in memory "/favicon.ico": new Response(await Bun.file("./favicon.ico").bytes(), { headers: { "Content-Type": "image/x-icon", }, }), }, // (optional) fallback for unmatched routes: // Required if Bun's version < 1.2.3 fetch(req) { return new Response("Not Found", { status: 404 }); }, }); console.log(`Server running at ${server.url}`); ``` *** ## HTML imports Bun supports importing HTML files directly into your server code, enabling full-stack applications with both server-side and client-side code. HTML imports work in two modes: **Development (`bun --hot`):** Assets are bundled on-demand at runtime, enabling hot module replacement (HMR) for a fast, iterative development experience. When you change your frontend code, the browser automatically updates without a full page reload. **Production (`bun build`):** When building with `bun build --target=bun`, the `import index from "./index.html"` statement resolves to a pre-built manifest object containing all bundled client assets. `Bun.serve` consumes this manifest to serve optimized assets with zero runtime bundling overhead. This is ideal for deploying to production. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} import myReactSinglePageApp from "./index.html"; Bun.serve({ routes: { "/": myReactSinglePageApp, }, }); ``` HTML imports don't just serve HTML — it's a full-featured frontend bundler, transpiler, and toolkit built using Bun's [bundler](/bundler), JavaScript transpiler and CSS parser. You can use this to build full-featured frontends with React, TypeScript, Tailwind CSS, and more. For a complete guide on building full-stack applications with HTML imports, including detailed examples and best practices, see [/docs/bundler/fullstack](/bundler/fullstack). *** ## Configuration ### Changing the `port` and `hostname` To configure which port and hostname the server will listen on, set `port` and `hostname` in the options object. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} Bun.serve({ port: 8080, // defaults to $BUN_PORT, $PORT, $NODE_PORT otherwise 3000 // [!code ++] hostname: "mydomain.com", // defaults to "0.0.0.0" // [!code ++] fetch(req) { return new Response("404!"); }, }); ``` To randomly select an available port, set `port` to `0`. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const server = Bun.serve({ port: 0, // random port // [!code ++] fetch(req) { return new Response("404!"); }, }); // server.port is the randomly selected port console.log(server.port); ``` You can view the chosen port by accessing the `port` property on the server object, or by accessing the `url` property. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} console.log(server.port); // 3000 console.log(server.url); // http://localhost:3000 ``` ### Configuring a default port Bun supports several options and environment variables to configure the default port. The default port is used when the `port` option is not set. * `--port` CLI flag ```sh theme={"theme":{"light":"github-light","dark":"dracula"}} bun --port=4002 server.ts ``` * `BUN_PORT` environment variable ```sh theme={"theme":{"light":"github-light","dark":"dracula"}} bun_PORT=4002 bun server.ts ``` * `PORT` environment variable ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} PORT=4002 bun server.ts ``` * `NODE_PORT` environment variable ```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} NODE_PORT=4002 bun server.ts ``` *** ## Unix domain sockets To listen on a [unix domain socket](https://en.wikipedia.org/wiki/Unix_domain_socket), pass the `unix` option with the path to the socket. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} Bun.serve({ unix: "/tmp/my-socket.sock", // path to socket fetch(req) { return new Response(`404!`); }, }); ``` ### Abstract namespace sockets Bun supports Linux abstract namespace sockets. To use an abstract namespace socket, prefix the `unix` path with a null byte. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} Bun.serve({ unix: "\0my-abstract-socket", // abstract namespace socket fetch(req) { return new Response(`404!`); }, }); ``` Unlike unix domain sockets, abstract namespace sockets are not bound to the filesystem and are automatically removed when the last reference to the socket is closed. *** ## idleTimeout To configure the idle timeout, set the `idleTimeout` field in Bun.serve. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} Bun.serve({ // 10 seconds: idleTimeout: 10, fetch(req) { return new Response("Bun!"); }, }); ``` This is the maximum amount of time a connection is allowed to be idle before the server closes it. A connection is idling if there is no data sent or received. *** ## export default syntax Thus far, the examples on this page have used the explicit `Bun.serve` API. Bun also supports an alternate syntax. ```ts server.ts theme={"theme":{"light":"github-light","dark":"dracula"}} import { type Serve } from "bun"; export default { fetch(req) { return new Response("Bun!"); }, } satisfies Serve; ``` Instead of passing the server options into `Bun.serve`, `export default` it. This file can be executed as-is; when Bun sees a file with a `default` export containing a `fetch` handler, it passes it into `Bun.serve` under the hood. *** ## Hot Route Reloading Update routes without server restarts using `server.reload()`: ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const server = Bun.serve({ routes: { "/api/version": () => Response.json({ version: "1.0.0" }), }, }); // Deploy new routes without downtime server.reload({ routes: { "/api/version": () => Response.json({ version: "2.0.0" }), }, }); ``` *** ## Server Lifecycle Methods ### `server.stop()` To stop the server from accepting new connections: ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const server = Bun.serve({ fetch(req) { return new Response("Hello!"); }, }); // Gracefully stop the server (waits for in-flight requests) await server.stop(); // Force stop and close all active connections await server.stop(true); ``` By default, `stop()` allows in-flight requests and WebSocket connections to complete. Pass `true` to immediately terminate all connections. ### `server.ref()` and `server.unref()` Control whether the server keeps the Bun process alive: ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} // Don't keep process alive if server is the only thing running server.unref(); // Restore default behavior - keep process alive server.ref(); ``` ### `server.reload()` Update the server's handlers without restarting: ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const server = Bun.serve({ routes: { "/api/version": Response.json({ version: "v1" }), }, fetch(req) { return new Response("v1"); }, }); // Update to new handler server.reload({ routes: { "/api/version": Response.json({ version: "v2" }), }, fetch(req) { return new Response("v2"); }, }); ``` This is useful for development and hot reloading. Only `fetch`, `error`, and `routes` can be updated. *** ## Per-Request Controls ### `server.timeout(Request, seconds)` Set a custom idle timeout for individual requests: ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const server = Bun.serve({ async fetch(req, server) { // Set 60 second timeout for this request server.timeout(req, 60); // If they take longer than 60 seconds to send the body, the request will be aborted await req.text(); return new Response("Done!"); }, }); ``` Pass `0` to disable the timeout for a request. ### `server.requestIP(Request)` Get client IP and port information: ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const server = Bun.serve({ fetch(req, server) { const address = server.requestIP(req); if (address) { return new Response(`Client IP: ${address.address}, Port: ${address.port}`); } return new Response("Unknown client"); }, }); ``` Returns `null` for closed requests or Unix domain sockets. *** ## Server Metrics ### `server.pendingRequests` and `server.pendingWebSockets` Monitor server activity with built-in counters: ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const server = Bun.serve({ fetch(req, server) { return new Response( `Active requests: ${server.pendingRequests}\n` + `Active WebSockets: ${server.pendingWebSockets}`, ); }, }); ``` ### `server.subscriberCount(topic)` Get count of subscribers for a WebSocket topic: ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const server = Bun.serve({ fetch(req, server) { const chatUsers = server.subscriberCount("chat"); return new Response(`${chatUsers} users in chat`); }, websocket: { message(ws) { ws.subscribe("chat"); }, }, }); ``` *** ## Benchmarks Below are Bun and Node.js implementations of a simple HTTP server that responds `Bun!` to each incoming `Request`. ```ts Bun theme={"theme":{"light":"github-light","dark":"dracula"}} Bun.serve({ fetch(req: Request) { return new Response("Bun!"); }, port: 3000, }); ``` ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} require("http") .createServer((req, res) => res.end("Bun!")) .listen(8080); ``` The `Bun.serve` server can handle roughly 2.5x more requests per second than Node.js on Linux. | Runtime | Requests per second | | ------- | ------------------- | | Node 16 | \~64,000 | | Bun | \~160,000 | ![image](https://user-images.githubusercontent.com/709451/162389032-fc302444-9d03-46be-ba87-c12bd8ce89a0.png) *** ## Practical example: REST API Here's a basic database-backed REST API using Bun's router with zero dependencies: ```ts server.ts expandable icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} import type { Post } from "./types.ts"; import { Database } from "bun:sqlite"; const db = new Database("posts.db"); db.exec(` CREATE TABLE IF NOT EXISTS posts ( id TEXT PRIMARY KEY, title TEXT NOT NULL, content TEXT NOT NULL, created_at TEXT NOT NULL ) `); Bun.serve({ routes: { // List posts "/api/posts": { GET: () => { const posts = db.query("SELECT * FROM posts").all(); return Response.json(posts); }, // Create post POST: async req => { const post: Omit = await req.json(); const id = crypto.randomUUID(); db.query( `INSERT INTO posts (id, title, content, created_at) VALUES (?, ?, ?, ?)`, ).run(id, post.title, post.content, new Date().toISOString()); return Response.json({ id, ...post }, { status: 201 }); }, }, // Get post by ID "/api/posts/:id": req => { const post = db.query("SELECT * FROM posts WHERE id = ?").get(req.params.id); if (!post) { return new Response("Not Found", { status: 404 }); } return Response.json(post); }, }, error(error) { console.error(error); return new Response("Internal Server Error", { status: 500 }); }, }); ``` ```ts types.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} export interface Post { id: string; title: string; content: string; created_at: string; } ``` *** ## Reference ```ts expandable See TypeScript Definitions theme={"theme":{"light":"github-light","dark":"dracula"}} interface Server extends Disposable { /** * Stop the server from accepting new connections. * @param closeActiveConnections If true, immediately terminates all connections * @returns Promise that resolves when the server has stopped */ stop(closeActiveConnections?: boolean): Promise; /** * Update handlers without restarting the server. * Only fetch and error handlers can be updated. */ reload(options: Serve): void; /** * Make a request to the running server. * Useful for testing or internal routing. */ fetch(request: Request | string): Response | Promise; /** * Upgrade an HTTP request to a WebSocket connection. * @returns true if upgrade successful, false if failed */ upgrade( request: Request, options?: { headers?: Bun.HeadersInit; data?: T; }, ): boolean; /** * Publish a message to all WebSocket clients subscribed to a topic. * @returns Bytes sent, 0 if dropped, -1 if backpressure applied */ publish( topic: string, data: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer, compress?: boolean, ): ServerWebSocketSendStatus; /** * Get count of WebSocket clients subscribed to a topic. */ subscriberCount(topic: string): number; /** * Get client IP address and port. * @returns null for closed requests or Unix sockets */ requestIP(request: Request): SocketAddress | null; /** * Set custom idle timeout for a request. * @param seconds Timeout in seconds, 0 to disable */ timeout(request: Request, seconds: number): void; /** * Keep process alive while server is running. */ ref(): void; /** * Allow process to exit if server is only thing running. */ unref(): void; /** Number of in-flight HTTP requests */ readonly pendingRequests: number; /** Number of active WebSocket connections */ readonly pendingWebSockets: number; /** Server URL including protocol, hostname and port */ readonly url: URL; /** Port server is listening on */ readonly port: number; /** Hostname server is bound to */ readonly hostname: string; /** Whether server is in development mode */ readonly development: boolean; /** Server instance identifier */ readonly id: string; } interface WebSocketHandler { /** Maximum WebSocket message size in bytes */ maxPayloadLength?: number; /** Bytes of queued messages before applying backpressure */ backpressureLimit?: number; /** Whether to close connection when backpressure limit hit */ closeOnBackpressureLimit?: boolean; /** Called when backpressure is relieved */ drain?(ws: ServerWebSocket): void | Promise; /** Seconds before idle timeout */ idleTimeout?: number; /** Enable per-message deflate compression */ perMessageDeflate?: | boolean | { compress?: WebSocketCompressor | boolean; decompress?: WebSocketCompressor | boolean; }; /** Send ping frames to keep connection alive */ sendPings?: boolean; /** Whether server receives its own published messages */ publishToSelf?: boolean; /** Called when connection opened */ open?(ws: ServerWebSocket): void | Promise; /** Called when message received */ message(ws: ServerWebSocket, message: string | Buffer): void | Promise; /** Called when connection closed */ close?(ws: ServerWebSocket, code: number, reason: string): void | Promise; /** Called when ping frame received */ ping?(ws: ServerWebSocket, data: Buffer): void | Promise; /** Called when pong frame received */ pong?(ws: ServerWebSocket, data: Buffer): void | Promise; } interface TLSOptions { /** Certificate authority chain */ ca?: string | Buffer | BunFile | Array; /** Server certificate */ cert?: string | Buffer | BunFile | Array; /** Path to DH parameters file */ dhParamsFile?: string; /** Private key */ key?: string | Buffer | BunFile | Array; /** Reduce TLS memory usage */ lowMemoryMode?: boolean; /** Private key passphrase */ passphrase?: string; /** OpenSSL options flags */ secureOptions?: number; /** Server name for SNI */ serverName?: string; } ``` # TLS Source: https://bun.com/docs/runtime/http/tls Enable TLS in Bun.serve Bun supports TLS out of the box, powered by [BoringSSL](https://boringssl.googlesource.com/boringssl). Enable TLS by passing in a value for `key` and `cert`; both are required to enable TLS. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} Bun.serve({ tls: { key: Bun.file("./key.pem"), // [!code ++] cert: Bun.file("./cert.pem"), // [!code ++] }, }); ``` The `key` and `cert` fields expect the *contents* of your TLS key and certificate, *not a path to it*. This can be a string, `BunFile`, `TypedArray`, or `Buffer`. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} Bun.serve({ tls: { key: Bun.file("./key.pem"), // BunFile key: fs.readFileSync("./key.pem"), // Buffer key: fs.readFileSync("./key.pem", "utf8"), // string key: [Bun.file("./key1.pem"), Bun.file("./key2.pem")], // array of above }, }); ``` ### Passphrase If your private key is encrypted with a passphrase, provide a value for `passphrase` to decrypt it. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} Bun.serve({ tls: { key: Bun.file("./key.pem"), cert: Bun.file("./cert.pem"), passphrase: "my-secret-passphrase", // [!code ++] }, }); ``` ### CA Certificates Optionally, you can override the trusted CA certificates by passing a value for `ca`. By default, the server will trust the list of well-known CAs curated by Mozilla. When `ca` is specified, the Mozilla list is overwritten. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} Bun.serve({ tls: { key: Bun.file("./key.pem"), // path to TLS key cert: Bun.file("./cert.pem"), // path to TLS cert ca: Bun.file("./ca.pem"), // path to root CA certificate // [!code ++] }, }); ``` ### Diffie-Hellman To override Diffie-Hellman parameters: ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} Bun.serve({ tls: { dhParamsFile: "/path/to/dhparams.pem", // path to Diffie Hellman parameters // [!code ++] }, }); ``` *** ## Server name indication (SNI) To configure the server name indication (SNI) for the server, set the `serverName` field in the `tls` object. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} Bun.serve({ tls: { serverName: "my-server.com", // SNI // [!code ++] }, }); ``` To allow multiple server names, pass an array of objects to `tls`, each with a `serverName` field. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} Bun.serve({ tls: [ { key: Bun.file("./key1.pem"), cert: Bun.file("./cert1.pem"), serverName: "my-server1.com", // [!code ++] }, { key: Bun.file("./key2.pem"), cert: Bun.file("./cert2.pem"), serverName: "my-server2.com", // [!code ++] }, ], }); ``` # WebSockets Source: https://bun.com/docs/runtime/http/websockets Server-side WebSockets in Bun `Bun.serve()` supports server-side WebSockets, with on-the-fly compression, TLS support, and a Bun-native publish-subscribe API. **⚡️ 7x more throughput** Bun's WebSockets are fast. For a [simple chatroom](https://github.com/oven-sh/bun/tree/main/bench/websocket-server/README.md) on Linux x64, Bun can handle 7x more requests per second than Node.js + [`"ws"`](https://github.com/websockets/ws). | **Messages sent per second** | **Runtime** | **Clients** | | ---------------------------- | ------------------------------ | ----------- | | \~700,000 | (`Bun.serve`) Bun v0.2.1 (x64) | 16 | | \~100,000 | (`ws`) Node v18.10.0 (x64) | 16 | Internally Bun's WebSocket implementation is built on [uWebSockets](https://github.com/uNetworking/uWebSockets). *** ## Start a WebSocket server Below is a simple WebSocket server built with `Bun.serve`, in which all incoming requests are [upgraded](https://developer.mozilla.org/en-US/docs/Web/HTTP/Protocol_upgrade_mechanism) to WebSocket connections in the `fetch` handler. The socket handlers are declared in the `websocket` parameter. ```ts server.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} Bun.serve({ fetch(req, server) { // upgrade the request to a WebSocket if (server.upgrade(req)) { return; // do not return a Response } return new Response("Upgrade failed", { status: 500 }); }, websocket: {}, // handlers }); ``` The following WebSocket event handlers are supported: ```ts server.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} Bun.serve({ fetch(req, server) {}, // upgrade logic websocket: { message(ws, message) {}, // a message is received open(ws) {}, // a socket is opened close(ws, code, message) {}, // a socket is closed drain(ws) {}, // the socket is ready to receive more data }, }); ``` In Bun, handlers are declared once per server, instead of per socket. `ServerWebSocket` expects you to pass a `WebSocketHandler` object to the `Bun.serve()` method which has methods for `open`, `message`, `close`, `drain`, and `error`. This is different than the client-side `WebSocket` class which extends `EventTarget` (onmessage, onopen, onclose), Clients tend to not have many socket connections open so an event-based API makes sense. But servers tend to have **many** socket connections open, which means: * Time spent adding/removing event listeners for each connection adds up * Extra memory spent on storing references to callbacks function for each connection * Usually, people create new functions for each connection, which also means more memory So, instead of using an event-based API, `ServerWebSocket` expects you to pass a single object with methods for each event in `Bun.serve()` and it is reused for each connection. This leads to less memory usage and less time spent adding/removing event listeners. The first argument to each handler is the instance of `ServerWebSocket` handling the event. The `ServerWebSocket` class is a fast, Bun-native implementation of [`WebSocket`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket) with some additional features. ```ts server.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} Bun.serve({ fetch(req, server) {}, // upgrade logic websocket: { message(ws, message) { ws.send(message); // echo back the message }, }, }); ``` ### Sending messages Each `ServerWebSocket` instance has a `.send()` method for sending messages to the client. It supports a range of input types. ```ts server.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" focus={4-6} theme={"theme":{"light":"github-light","dark":"dracula"}} Bun.serve({ fetch(req, server) {}, // upgrade logic websocket: { message(ws, message) { ws.send("Hello world"); // string ws.send(response.arrayBuffer()); // ArrayBuffer ws.send(new Uint8Array([1, 2, 3])); // TypedArray | DataView }, }, }); ``` ### Headers Once the upgrade succeeds, Bun will send a `101 Switching Protocols` response per the [spec](https://developer.mozilla.org/en-US/docs/Web/HTTP/Protocol_upgrade_mechanism). Additional `headers` can be attached to this `Response` in the call to `server.upgrade()`. ```ts server.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} Bun.serve({ fetch(req, server) { const sessionId = await generateSessionId(); server.upgrade(req, { headers: { // [!code ++] "Set-Cookie": `SessionId=${sessionId}`, // [!code ++] }, // [!code ++] }); }, websocket: {}, // handlers }); ``` ### Contextual data Contextual `data` can be attached to a new WebSocket in the `.upgrade()` call. This data is made available on the `ws.data` property inside the WebSocket handlers. ```ts server.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} type WebSocketData = { createdAt: number; channelId: string; authToken: string; }; Bun.serve({ fetch(req, server) { const cookies = new Bun.CookieMap(req.headers.get("cookie")!); server.upgrade(req, { // this object must conform to WebSocketData data: { createdAt: Date.now(), channelId: new URL(req.url).searchParams.get("channelId"), authToken: cookies.get("X-Token"), }, }); return undefined; }, websocket: { // TypeScript: specify the type of ws.data like this data: {} as WebSocketData, // handler called when a message is received async message(ws, message) { // ws.data is now properly typed as WebSocketData const user = getUserFromToken(ws.data.authToken); await saveMessageToDatabase({ channel: ws.data.channelId, message: String(message), userId: user.id, }); }, }, }); ``` To connect to this server from the browser, create a new `WebSocket`. ```ts browser.js icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}} const socket = new WebSocket("ws://localhost:3000/chat"); socket.addEventListener("message", event => { console.log(event.data); }); ``` **Identifying users** The cookies that are currently set on the page will be sent with the WebSocket upgrade request and available on `req.headers` in the `fetch` handler. Parse these cookies to determine the identity of the connecting user and set the value of `data` accordingly. ### Pub/Sub Bun's `ServerWebSocket` implementation implements a native publish-subscribe API for topic-based broadcasting. Individual sockets can `.subscribe()` to a topic (specified with a string identifier) and `.publish()` messages to all other subscribers to that topic (excluding itself). This topic-based broadcast API is similar to [MQTT](https://en.wikipedia.org/wiki/MQTT) and [Redis Pub/Sub](https://redis.io/topics/pubsub). ```ts server.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} const server = Bun.serve({ fetch(req, server) { const url = new URL(req.url); if (url.pathname === "/chat") { console.log(`upgrade!`); const username = getUsernameFromReq(req); const success = server.upgrade(req, { data: { username } }); return success ? undefined : new Response("WebSocket upgrade error", { status: 400 }); } return new Response("Hello world"); }, websocket: { // TypeScript: specify the type of ws.data like this data: {} as { username: string }, open(ws) { const msg = `${ws.data.username} has entered the chat`; ws.subscribe("the-group-chat"); server.publish("the-group-chat", msg); }, message(ws, message) { // this is a group chat // so the server re-broadcasts incoming message to everyone server.publish("the-group-chat", `${ws.data.username}: ${message}`); }, close(ws) { const msg = `${ws.data.username} has left the chat`; ws.unsubscribe("the-group-chat"); server.publish("the-group-chat", msg); }, }, }); console.log(`Listening on ${server.hostname}:${server.port}`); ``` Calling `.publish(data)` will send the message to all subscribers of a topic *except* the socket that called `.publish()`. To send a message to all subscribers of a topic, use the `.publish()` method on the `Server` instance. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const server = Bun.serve({ websocket: { // ... }, }); // listen for some external event server.publish("the-group-chat", "Hello world"); ``` ### Compression Per-message [compression](https://websockets.readthedocs.io/en/stable/topics/compression.html) can be enabled with the `perMessageDeflate` parameter. ```ts server.ts icon="https://mintcdn.com/bun-1dd33a4e/Hq64iapoQXHbYMEN/icons/typescript.svg?fit=max&auto=format&n=Hq64iapoQXHbYMEN&q=85&s=c6cceedec8f82d2cc803d7c6ec82b240" theme={"theme":{"light":"github-light","dark":"dracula"}} Bun.serve({ websocket: { perMessageDeflate: true, // [!code ++] }, }); ``` Compression can be enabled for individual messages by passing a `boolean` as the second argument to `.send()`. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} ws.send("Hello world", true); ``` For fine-grained control over compression characteristics, refer to the [Reference](#reference). ### Backpressure The `.send(message)` method of `ServerWebSocket` returns a `number` indicating the result of the operation. * `-1` — The message was enqueued but there is backpressure * `0` — The message was dropped due to a connection issue * `1+` — The number of bytes sent This gives you better control over backpressure in your server. ### Timeouts and limits By default, Bun will close a WebSocket connection if it is idle for 120 seconds. This can be configured with the `idleTimeout` parameter. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} Bun.serve({ fetch(req, server) {}, // upgrade logic websocket: { idleTimeout: 60, // 60 seconds // [!code ++] }, }); ``` Bun will also close a WebSocket connection if it receives a message that is larger than 16 MB. This can be configured with the `maxPayloadLength` parameter. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} Bun.serve({ fetch(req, server) {}, // upgrade logic websocket: { maxPayloadLength: 1024 * 1024, // 1 MB // [!code ++] }, }); ``` *** ## Connect to a `Websocket` server Bun implements the `WebSocket` class. To create a WebSocket client that connects to a `ws://` or `wss://` server, create an instance of `WebSocket`, as you would in the browser. ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const socket = new WebSocket("ws://localhost:3000"); // With subprotocol negotiation const socket2 = new WebSocket("ws://localhost:3000", ["soap", "wamp"]); ``` In browsers, the cookies that are currently set on the page will be sent with the WebSocket upgrade request. This is a standard feature of the `WebSocket` API. For convenience, Bun lets you setting custom headers directly in the constructor. This is a Bun-specific extension of the `WebSocket` standard. *This will not work in browsers.* ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} const socket = new WebSocket("ws://localhost:3000", { headers: { /* custom headers */ }, // [!code ++] }); ``` To add event listeners to the socket: ```ts theme={"theme":{"light":"github-light","dark":"dracula"}} // message is received socket.addEventListener("message", event => {}); // socket opened socket.addEventListener("open", event => {}); // socket closed socket.addEventListener("close", event => {}); // error handler socket.addEventListener("error", event => {}); ``` *** ## Reference ```ts See Typescript Definitions expandable theme={"theme":{"light":"github-light","dark":"dracula"}} namespace Bun { export function serve(params: { fetch: (req: Request, server: Server) => Response | Promise; websocket?: { message: (ws: ServerWebSocket, message: string | ArrayBuffer | Uint8Array) => void; open?: (ws: ServerWebSocket) => void; close?: (ws: ServerWebSocket, code: number, reason: string) => void; error?: (ws: ServerWebSocket, error: Error) => void; drain?: (ws: ServerWebSocket) => void; maxPayloadLength?: number; // default: 16 * 1024 * 1024 = 16 MB idleTimeout?: number; // default: 120 (seconds) backpressureLimit?: number; // default: 1024 * 1024 = 1 MB closeOnBackpressureLimit?: boolean; // default: false sendPings?: boolean; // default: true publishToSelf?: boolean; // default: false perMessageDeflate?: | boolean | { compress?: boolean | Compressor; decompress?: boolean | Compressor; }; }; }): Server; } type Compressor = | `"disable"` | `"shared"` | `"dedicated"` | `"3KB"` | `"4KB"` | `"8KB"` | `"16KB"` | `"32KB"` | `"64KB"` | `"128KB"` | `"256KB"`; interface Server { pendingWebSockets: number; publish(topic: string, data: string | ArrayBufferView | ArrayBuffer, compress?: boolean): number; upgrade( req: Request, options?: { headers?: HeadersInit; data?: any; }, ): boolean; } interface ServerWebSocket { readonly data: any; readonly readyState: number; readonly remoteAddress: string; send(message: string | ArrayBuffer | Uint8Array, compress?: boolean): number; close(code?: number, reason?: string): void; subscribe(topic: string): void; unsubscribe(topic: string): void; publish(topic: string, message: string | ArrayBuffer | Uint8Array): void; isSubscribed(topic: string): boolean; cork(cb: (ws: ServerWebSocket) => void): void; } ``` # Bun Runtime Source: https://bun.com/docs/runtime/index Execute JavaScript/TypeScript files, package.json scripts, and executable packages with Bun's fast runtime. The Bun Runtime is designed to start fast and run fast. Under the hood, Bun uses the [JavaScriptCore engine](https://developer.apple.com/documentation/javascriptcore), which is developed by Apple for Safari. In most cases, the startup and running performance is faster than V8, the engine used by Node.js and Chromium-based browsers. Its transpiler and runtime are written in Zig, a modern, high-performance language. On Linux, this translates into startup times [4x faster](https://twitter.com/jarredsumner/status/1499225725492076544) than Node.js. | Command | Time | | --------------- | -------- | | `bun hello.js` | `5.2ms` | | `node hello.js` | `25.1ms` | This benchmark is based on running a simple Hello World script on Linux ## Run a file Use `bun run` to execute a source file. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun run index.js ``` Bun supports TypeScript and JSX out of the box. Every file is transpiled on the fly by Bun's fast native transpiler before being executed. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun run index.js bun run index.jsx bun run index.ts bun run index.tsx ``` Alternatively, you can omit the `run` keyword and use the "naked" command; it behaves identically. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun index.tsx bun index.js ``` ### `--watch` To run a file in watch mode, use the `--watch` flag. ```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}} bun --watch run index.tsx ``` When using `bun run`, put Bun flags like `--watch` immediately after `bun`. ```bash theme={"theme":{"light":"github-light","dark":"dracula"}} bun --watch run dev # ✔️ do this bun run dev --watch # ❌ don't do this ``` Flags that occur at the end of the command will be ignored and passed through to the `"dev"` script itself. ## Run a `package.json` script Compare to `npm run