So what is Bun?
Bun is the new hot thing out that everyone is talking about. At least in the JavaScript community. Although not quite production ready yet, Bun is a modern JavaScript runtime, similar to Node.js. The differences though are the performance enhancements promised, so the simplest way to think about Bun is as a newer, faster environment where you can run your JavaScript code.
This is an exciting opportunity to explore a new runtime technology so let’s take a deeper dive into it and build our first REST API with it. We will build this API natively on Bun, without any frameworks. Sure, adding one will not only speed up the development time but also clean up the code. That is not what we are going for here though. The goal is to just simply see how Bun natively handles these requests.
What's so different about Bun?
1. Performance
Speed: Bun boasts significantly faster startup times and execution speeds compared to Node.js, making it ideal for high-performance applications, especially those requiring quick responses under heavy load35.
Built-in Tooling: Unlike Node.js, which often requires multiple external tools (like Webpack or Babel), Bun includes a built-in bundler and transpiler, reducing complexity and improving build times
2. Development Workflow
Simplified Setup: Bun's all-in-one approach means developers can get started without needing to configure multiple tools. This leads to a more streamlined development process.
Native TypeScript Support: Bun supports TypeScript natively, allowing developers to write type-safe code without additional configuration, which can lead to fewer errors in large codebases.
3. Package Management
- Faster Package Management: Bun's package manager is designed to be faster than npm, reducing the time spent on dependency management during development. This is particularly beneficial for projects with numerous dependencies.
4. Compatibility
- Ecosystem Integration: Bun works seamlessly with popular libraries and frameworks such as React, Vue, and Express, ensuring that developers can transition without facing compatibility issues.
5. Target Use Cases
High-Performance Applications:
Bun is well-suited for building APIs and server-side applications that require high throughput and low latency.
Prototyping:
Its fast startup times make it ideal for rapid prototyping and experimentation with new ideas in JavaScript.
Installation :
For windows :
powershell -c "irm bun.sh/install.ps1 | iex"
For Linux & macOS:
curl -fsSL https://bun.sh/install | bash
Project Setup
We will create a REST API for a simple blog application. Start by initializing your project in a new directory, which will contain your application files.
mkdir bun-blog-api
cd bun-blog-api
Initialize Project
This step creates a package.json
file to manage dependencies and scripts.
bun init
Building the REST API
After initializing the application, create a new file named index.ts
. This file will serve as the server's entry point. Start by setting up a basic server that responds with "Hello, World!" when accessed.
import { serve } from 'bun';
const PORT = 6989;
serve({
port: PORT,
async fetch(request) {
return new Response('Hello, world!');
},
});
console.log(`Listening on http://localhost:${PORT} ...`);
Aaaaand thats it! We now have our server. In order to test that things are working properly, we need to start the server with the following command :
bun run index.ts
You should see a message indicating it is listening on http://localhost:6989
. You can verify functionality using curl
http://localhost:6989
, which should return the greeting message.
Next, we will implement several routes:
GET: Retrieve all blog posts
POST: Create a new blog post
DELETE: Remove a blog post by ID
For simplicity, this example will not include a database; instead, we will store posts in memory.
import { serve } from 'bun';
const PORT = 6989;
interface Post {
id: string;
title: string;
content: string;
}
let blogPosts: Post = [];
function handleGetPostById(id: string) {
const post = blogPosts.find((post) => post.id === id);
if (!post) {
return new Response('Post Not Found', { status: 404 });
}
return new Response(JSON.stringify(post), {
headers: { 'Content-Type': 'application/json' },
});
}
function handleGetAllPosts() {
return new Response(JSON.stringify(blogPosts), {
headers: { 'Content-Type': 'application/json' },
});
}
function handleDeletePost(id: string) {
const postIndex = blogPosts.findIndex((post) => post.id === id);
if (postIndex === -1) {
return new Response('Post Not Found', { status: 404 });
}
blogPosts.splice(postIndex, 1);
return new Response('Post Deleted', { status: 200 });
}
serve({
port: PORT,
async fetch(request: Request) {
const { method } = request;
const { pathname } = new URL(request.url);
const pathRegexForID = /^\/api\/posts\/(\d+)$/;
if (method === 'GET' && pathname === '/api/posts') {
return handleGetAllPosts();
}
if (method === 'GET') {
const match = pathname.match(pathRegexForID);
const id = match && match[1];
if (id) {
return handleGetPostById(id);
}
}
if (method === 'POST' && pathname === '/api/posts') {
const newPost = await request.json();
return handleCreatePost(newPost.title, newPost.content);
}
if (method === 'DELETE' && pathname === '/api/posts') {
const { id } = await request.json();
return handleDeletePost(id);
}
return new Response('Not Found', { status: 404 });
},
});
console.log(`Listening on http://localhost:${PORT} ...`);
Before testing, if your server is still running from when we first created it, you will have to restart in order for all the changes to be applied.
Conclusion
Congratulations! You have now created your first REST API using Bun. The code is not the cleanest and there are things, like path parameters, that are not supported out of the box. This is where using a framework can make life a lot easier by both providing more functionality and also cleaning up a lot of the code. As you can tell, this works for a small API, but if we were to expand this, the code can quickly get out of hand.