DEV Community

Cover image for Build an HTTP server in Bun
Sadeedpv🥇
Sadeedpv🥇

Posted on

Build an HTTP server in Bun

As of last week, Bun has taken over the JavaScript world. In this post, we will see how to create an HTTP server in Bun using the Elysia Framework. Here is the link to the full source code of this project.

So, What is Bun?

Bun is a new JavaScript Runtime built from scratch to serve the JavaScript ecosystem.

Bun Meme

Bun has built-in TypeScript support and is blazingly fast ⚡earning it the favor of many JavaScript & TypeScript Developers 💘.

Bun Meme

Installing Bun

Installing Bun is very simple. All you have to do is:

curl -fsSL https://bun.sh/install | bash
Enter fullscreen mode Exit fullscreen mode

If you like to know more about the installation process, here is the link

Note that Bun is not yet compatible with Windows, But you can still install if you have WSL installed on your system.

Creating an HTTP server using Elysia Framework

Elysia is a Bun-first performance focused web framework that takes full advantage of Bun's HTTP, file system, and hot reloading APIs.

Let's first initialize our Elysia app:

bun create elysia myapp
cd myapp
bun run dev
Enter fullscreen mode Exit fullscreen mode

Let us Build our server

In this tutorial, I will be building a simple TODO Application. For building the application, I will be using the SQLite Database. The cool thing about Bun is that it has completely free SQLite Database built in the runtime. If you like to read more about the bun:sqlite, here is the docs

In our src/index.ts, we will declare our Elysia app:

import { Elysia } from 'elysia'

const app = new Elysia();
app.get('/', () => "Hello World!");
app.listen(8000);
console.log(
  `🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`
);

Enter fullscreen mode Exit fullscreen mode

If you want to use environment variables:

app.listen(Number(Bun.env.PORT));

console.log(
  `🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`
);

Enter fullscreen mode Exit fullscreen mode

You can define your Routes with many built-in methods like:

app.get('/', () => {});
app.post('/', () => {});
app.put('/', () => {});
app.patch('/', () => {});
app.delete('/', () => {});
Enter fullscreen mode Exit fullscreen mode

Retrieving the path parameter is also very easy just like any other Backend Framework like Express.js:

// Both does the same thing

app.get("/hello/:name", ({ params: { name } }) => {
  return `Hello ${name}!`;
});

app.get("bye/:name", (context) => {
  return `Hello ${context.params.name}`;
});

Enter fullscreen mode Exit fullscreen mode

If you have many paths with the same prefix, you can Group them. Grouping allows you to combine multiple prefixes into one. Since we do not require that many routes to build this application, we do not need to Group the paths.

app.group('/user', app => app
    .post('/sign-in', signIn)
    .post('/sign-up', signUp)
    .post('/profile', getProfile)
)
Enter fullscreen mode Exit fullscreen mode

You can also install the cors plugin which adds support for customizing Cross-Origin Resource Sharing behavior.

Install it with:

bun add @elysiajs/cors
Enter fullscreen mode Exit fullscreen mode

Then use it:

import { cors } from "@elysiajs/cors";

const app = new Elysia();
app.use(cors());
Enter fullscreen mode Exit fullscreen mode

In order for us to build this application, first we need to initialize our Database. Let's take a look at how we can do that:

import { Database } from "bun:sqlite";

// Create DB If not Exists
const DB = new Database("mydb.sqlite", { create: true });
Enter fullscreen mode Exit fullscreen mode

Now, we need to create the Table

DB.query(
  `CREATE TABLE IF NOT EXISTS MESSAGES(
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  message TEXT
);`
).run();
Enter fullscreen mode Exit fullscreen mode

After creating the relation, now we need to implement the GET and POST request. Our GET request would look something like this

app.get("/", (context) => {
  const query = DB.query(`SELECT * FROM MESSAGES;`);
  const result = query.all();
  console.log(result);
  context.set.status = 200;

  return new Response(JSON.stringify({ messages: result }), {
    headers: { "Content-Type": "application/json" },
  });
});
Enter fullscreen mode Exit fullscreen mode

We used the db.query() method on your Database instance to prepare a SQL query and used .all() to run a query and get back the results as an array of objects.

Our POST request would look something like this:

app.post(
  "/add",
  ({ body }:any) => {
    const message = body?.message;
    console.log(message);
    const query = DB.query(`INSERT INTO MESSAGES (message) VALUES (?1)`);
    query.run(message);
    return new Response(JSON.stringify({ message: "Added" }), {
      headers: { "Content-Type": "application/json" },
    });
  });
Enter fullscreen mode Exit fullscreen mode

As you can see here, I am using the any type. Instead of using the any type, what we can do is

import { Elysia, t } from "elysia";

app.post(
  "/add",
  ({ body }) => {
    const message = body?.message;
    console.log(message);
    const query = DB.query(`INSERT INTO MESSAGES (message) VALUES (?1)`);
    query.run(message);
    return new Response(JSON.stringify({ message: "Added" }), {
      headers: { "Content-Type": "application/json" },
    });
  },
  {
    body: t.Object({
      message: t.String(),
    }),
  }
);
Enter fullscreen mode Exit fullscreen mode

Note that we used context.set.status to set the StatusCode of the response

Lets try running our application:

curl -X POST http://localhost:8000/add -H "Content-Type: application/json" -d '{"message":"This is a Reminder!"}'  
curl http://localhost:8000/
Enter fullscreen mode Exit fullscreen mode

And Booom!! 💥💥

{"message":"Added"}
{"messages": [{"id":1, "message": "This is a Reminder!"}]}
Enter fullscreen mode Exit fullscreen mode

Here are some tasks for you:

  • Create PUT and DELETE requests to update or DELETE the Database
  • Implement error handling

CONCLUSION

I hope you gained some new insights from this post. As you can see from the graph, Bun is much faster than Node as of now.
Bun Speed
Also, take a look at how fast Elysia is compared to Express:

Express Speed

References

If you like to read more about bun:sqlite, read more
If you like to read more about Elysia, read more
If you want the link to the full source code in GitHub, click here

Top comments (15)

Collapse
 
lvndry profile image
Landry Monga

Just FYI you can use Response.json this will automatically do the parsing and set the headers

Collapse
 
sadeedpv profile image
Sadeedpv🥇

oh, thank you I didn't know that 😄🙌!

Collapse
 
rampa2510 profile image
RAM PANDEY

Have you encountered any challenges while migrating an Express application to Elysia? If so, could you share your experiences and any obstacles you encountered? I'm curious about whether Elysia can seamlessly replace Express, especially regarding middleware compatibility, configuration options, and any limitations you might have come across.

Collapse
 
sadeedpv profile image
Sadeedpv🥇

Middlewares in Elysia is a bit different compared to Express. See if this helps

Collapse
 
marmeden profile image
eneasmarin

I was considering building this server using purely Bun.js. Saw some tutorials where they were handling routes simply by using 'ifs' inside the Bun.serve block and was not very convinced about that because it looks untidy, but wouldn't like to use extra dependencies like Elysia, Hono, Express...

Is it possible to do it using only Bun.js? What are the drawbacks? Why using Elysia, Hono, Express is better?

Collapse
 
sadeedpv profile image
Sadeedpv🥇 • Edited

Most of the time, it is better to build stuffs without the use of a framework unless you have specific reasons to. But, like you said, in this case, it can make your code a bit untidy. That's where frameworks come in. Using a framework like Elysia for example, can help to speed up the development process. Also, as shown in the graph above, Elysia claims to be much faster than any other frameworks. But, if you can build things without the need of a framework, go for it 👍

Collapse
 
bob4262 profile image
Bob dnaeil

I was looking for this tutorial just now, thnks

Collapse
 
sadeedpv profile image
Sadeedpv🥇

You are welcome!

Collapse
 
schwiftycold profile image
Shubham Kumar

So the future of JS runtime is Bun :love:

Collapse
 
sadeedpv profile image
Sadeedpv🥇

💘💘

Collapse
 
bob4262 profile image
Bob dnaeil

Really helpful ❤️

Collapse
 
jerommiole profile image
jrom

I'm glad that I saw this 😁

Collapse
 
sadeedpv profile image
Sadeedpv🥇

Glad you liked it!

Collapse
 
astrodev07 profile image
Diego Enríquez Puig

Too many Runtimes and JS frameworks. My mind is exploding XD

But is good to know that the technology advances so fast

Collapse
 
sadeedpv profile image
Sadeedpv🥇

🥲🥲