The Power of Bun: Building a Lightning-Fast TypeScript REST API with Built-in SQLite Support

The Power of Bun: Building a Lightning-Fast TypeScript REST API with Built-in SQLite Support
Photo by Anders Jildén / Unsplash

When it comes to building modern backends, developers often wrestle with setting up the right tools and dependencies. Bun, a high-performance JavaScript runtime, is changing the game. It’s fast, it’s simple, and it comes with powerful built-in features like an HTTP server and SQLite support. Let’s dive into how you can use Bun to create a lean, blazing-fast REST API with TypeScript.

Why Bun?

Bun isn’t just another runtime; it’s a developer-friendly powerhouse. Here’s what makes Bun stand out:

  1. Built-in SQLite Support: Forget about installing external SQLite libraries. Bun integrates SQLite natively, simplifying database interactions.
  2. Native HTTP Server: No need for frameworks like Express to handle HTTP requests. Bun’s built-in HTTP server is lightweight and efficient.
  3. Lightning-Fast Performance: Bun is written in Zig, making it one of the fastest JavaScript runtimes available.
  4. TypeScript First-Class Citizen: With built-in TypeScript transpilation, you don’t need tools like ts-node or additional configurations.

Building a REST API with Bun

Here’s how simple it is to build a REST API with Bun and SQLite.

Step 1: Setting Up the Project

First, initialize your project and install dependencies. The package.json file in Bun is straightforward:

{
  "name": "typescript-backend",
  "scripts": {
    "start": "bun run --watch index.ts"
  },
  "devDependencies": {
    "@types/bun": "^1.1.14",
    "@types/node": "^22.10.0",
    "typescript": "^5.7.2"
  }
}

This setup ensures you have TypeScript support, along with type definitions for Bun and Node.js. The --watch flag enables hot reloading for an enhanced development experience.

Step 2: Writing the Code

Here’s the core API logic:

import { Database } from "bun:sqlite";
const db = new Database("db.sqlite");

Bun.serve({
  port: 3001,
  fetch(req) {
    switch (new URL(req.url).pathname) {
      case "/vehicles": {
        const vehicles = db.query("SELECT * FROM vehicle");
        return new Response(JSON.stringify(vehicles));
      }
      default:
        return new Response("404!");
    }
  },
});

What’s happening here?

  • Database Initialization: A SQLite database is created (or opened if it already exists) with db.sqlite.
  • Route Handling: The fetch function checks the request’s path and executes SQL queries accordingly.
  • JSON Response: The API converts the SQLite query result to JSON, making it ready for consumption by front-end applications.

This minimalistic code creates a fully functional REST endpoint for /vehicles.

Additional Enhancements to Consider

While this setup is impressive in its simplicity, there are a few improvements and considerations to keep in mind:

1. Use Prepared Statements

To prevent SQL injection attacks, always use prepared statements when handling dynamic input in your queries:

const vehicles = db.query("SELECT * FROM vehicle WHERE type = ?", [vehicleType]);

2. Add Error Handling

Currently, the API doesn’t handle database or runtime errors. Wrapping your logic in a try...catch block ensures stability:

try {
  const vehicles = db.query("SELECT * FROM vehicle");
  return new Response(JSON.stringify(vehicles));
} catch (err) {
  return new Response(JSON.stringify({ error: "Internal Server Error" }), { status: 500 });
}

3. Modularize the Code

As your project grows, consider separating the routes, database queries, and server logic into distinct files for better maintainability.

4. Secure the Application

For production:

  • Set up CORS policies to restrict access.
  • Use HTTPS for secure communication.
  • Protect your SQLite database file by ensuring proper file permissions.

5. Optimize for Deployment

Although Bun is fast, you can take additional steps to ensure your application scales:

  • Connection Pooling: Use SQLite in a read-heavy environment or consider migrating to a database better suited for concurrent writes.
  • Load Testing: Use tools like Apache Benchmark (ab) to ensure your API performs well under load.

Finally

Bun makes it incredibly easy to build fast and efficient REST APIs with minimal boilerplate. Its built-in features reduce the dependency footprint, enabling developers to focus on building functionality rather than setting up tooling. By following the sample code and implementing the additional considerations mentioned, you can create a robust and secure backend.

If you’re tired of the overhead in traditional back-end setups, give Bun a try. It’s not just fast—it’s the future of JavaScript runtime development.

What’s your favorite feature of Bun so far? Let me know in the comments! 🚀

Support Us