02: Bun API
Environment variables
All environment variables from any .env
files are automatically loaded into process.env
, so just use process.env
to access any environment variables.
You can also set environment variables in scripts, like so by setting it in front of the command you're trying to run.
{
"scripts": {
"dev": "NODE_ENV=development bun run --watch index.ts"
}
}
Bun utilities
Bun.sleep(ms)
: async method that sleeps for specified number of millisecondsBun.sleepSync(ms)
: sync method that sleeps for specified number of milliseconds
Base 64 encoding and decoding
The btoa(string)
converts a string to a base64 representation and atob(base64string)
converts a base64 string back to its original contents. These are web standard methods.
const data = "hello world";
const encoded = btoa(data); // => "aGVsbG8gd29ybGQ="
const decoded = atob(encoded); // => "hello world"
Compressing data with gzip
const data = Buffer.from("Hello, world!");
const compressed = Bun.gzipSync(data);
// => Uint8Array
const decompressed = Bun.gunzipSync(compressed);
// => Uint8Array
Node Shell
This is how to run shell commands in node/bun:
import { spawnSync } from "child_process";
class LinuxError extends Error {
constructor(command: string) {
super(`Running the '${command}' command caused this error`);
}
}
function linux(command: string, args: string[] = []) {
try {
const { status, stdout, stderr } = spawnSync(command, args, {
encoding: "utf8",
});
return stdout;
} catch (e) {
console.error(e);
throw new LinuxError(command);
}
}
Bun Shell
Starting from bun v1.0.24 and on, you can use the bun shell. The great thing about this is that you can treat variables of type Response
, ArrayBuffer
, and Blob
just as you would strings.
import { $ } from "bun";
const response = await fetch("https://example.com");
// Use Response as stdin.
await $`cat < ${response} | wc -c`; // 1256
Here are some basic methods you can tack on to the end of the tempalte literal method:
const { stdout, stderr } = await $`echo "Hello World!"`.quiet(); // No output
const welcome = await $`echo "Hello World!"`.text(); // returns as text
quiet()
: instead of printing standard out, returns an object with thestdout
,stderr
, andexitCode
as properties.nothrow()
: doesn't throw on an error.text()
: returns the standard out of the command as text.json()
: reads the output of a command as JSONblob()
: reads the output of a command as a blob
const { stdout, stderr, exitCode } = await $`something-that-may-fail`
.nothrow()
.quiet();
Piping and Redirecting with JavaScript Objects
- You can redirect content to a buffer, blob, or file.
- You can use these files as standard input: buffer, blob, file,
Response
import { $ } from "bun";
// redirect output into a buffer
const buffer = Buffer.alloc(100);
await $`echo "Hello World!" > ${buffer}`;
console.log(buffer.toString()); // Hello World!\n
import { $ } from "bun";
const response = new Response("hello i am a response body");
const result = await $`cat < ${response}`.text();
console.log(result); // hello i am a response body
Setting environment variables
You can use the bun shell to set all environment variables, useing the $.env(obj)
command. Just make sure not to override everything.
import { $ } from "bun";
$.env({ ...process.env, FOO: "bar" });
Changing directories
You can change the working directory of a command by passing a string to .cwd()
:
import { $ } from "bun";
// temporarily change working directory to /tmp
await $`pwd`.cwd("/tmp"); // /tmp
Iterating through standard output
import { $ } from "bun";
for await (let line of $`echo "Hello World!"`.lines()) {
console.log(line); // Hello World!
}
Brace expansion
import { $ } from "bun";
await $.braces(`echo {1,2,3}`);
// => ["echo 1", "echo 2", "echo 3"]
Files
Writing files
Use the async Bun.write(filepath, data)
method to write data to a file
await Bun.write("output.txt", "this is output file")
The data
can be a string, Blob
, BunFile
, ArrayBuffer
, or a Response
.
File API
You can get a file representation in bun by using the async Bun.file(filepath)
method, which returns a file.
const file = await Bun.file("output.txt")
file.exists()
: async method that returns whether the file existsfile.size
: returns the file size in number of bytesfile.type
: returns the MIME type of the file.
This is how you change the type of a file you create.
const notreal = Bun.file("notreal.json", { type: "application/json" });
Here is how you write a file to Standard output:
const input = Bun.file("input.txt");
await Bun.write(Bun.stdout, input);
Reading Files
One a bun file object, you have these async methods to read data in from different specified encodings:
file.text()
: returns the utf8 text file contentfile.stream()
: returns the file content as a readable streamfile.arrayBuffer()
: returns the file content as an array bufferfile.blob()
: returns the file content as a Blob instance
import.meta
There is a lot of useful information stored on import.meta
, which is a way for a module to access information about itself.
import.meta.dir; // => "/path/to/project"
import.meta.file; // => "file.ts"
import.meta.path; // => "/path/to/project/file.ts"
import.meta.url; // => "file:///path/to/project/file.ts"
import.meta.main; // `true` if this file is directly executed by `bun run`
SQLite
import { Database } from "bun:sqlite";
const db = new Database();
const query = db.query("select 'Hello world' as message;");
query.get(); // => { message: "Hello world" }
- Create a new database by instantiating the
Database
class. You can create either an in-memory database or a persisting database.
const db = new Database(); // in-memory
const db = new Database("mydb.sqlite", {create: true}); // file
- Create a query with the
db.query(querystring)
method, which return a query object. You can then execute the query withquery.get()
const query = db.query("select 'Hello world' as message;");
query.get();
Queries
You can pass parameters to queries. and then execute them with methods. We define parameters with a $
prefixed on the variable name.
// $param1 and $param2 are now variables we use later
const query = db.query(`SELECT $param1, $param2;`);
// pass values for $param1 and $param2
const data = query.get({
$param1: "column1",
$param2: "column2"
})
query.get()
: returns the first result as an objectquery.all()
: returns all results as an array of objectsquery.run()
: runs the query, returns nothing.query.toString()
: expands the query as a string, plugging in parameters. Useful for debugging.
Testing
Basic testing
This is how you create basic tests in bun.
- Tests should end in the file extension
.test.ts
- Run tests with
bun test
import { expect, test } from "bun:test";
test("2 + 2", () => {
expect(2 + 2).toBe(4);
});
You create a test with test(name, func)
, and you create a test suite with describe(name, func)
.
import { expect, test, describe } from "bun:test";
describe("arithmetic", () => {
test("2 + 2", () => {
expect(2 + 2).toBe(4);
});
test("2 * 2", () => {
expect(2 * 2).toBe(4);
});
});
You can even specify a timeout in milliseconds for how long the test should take, else it will fail if it goes over the timeout.
test(name, cb, timeoutMillis)
test("my test", () => {}, 500)
Testing with flags
Sometimes you want to run only some tests and not others. You can use these test methods to do that, and then run only those matching tests with CLI options on the bun test
command.
test.skip(name, cb)
: skips the testtest.todo(name, cb)
: marks the test as a TODO test, meaning the test runner expects them to fail since they're supposed to be prematurely done. To exclusively run TODO tests, runbun test --todo
test.only(name, cb)
: marks tests, andbun test --only
runs only those tests.
conditional testing
Conditional testing allows you to run or skip tests based on some condition
test.if(condition)(name, cb)
: runs the test if the condition is truetest.skipif(condition)(name, cb)
: skips the test if the condition is truetest.todoif(condition)(name, cb)
: marks the test as a todo test if the condition is true
test.if(Math.random() > 0.5)("runs half the time", () => {
// ...
});
const macOS = process.arch === "darwin";
test.if(macOS)("runs on macOS", () => {
// runs if macOS
});