Build an HTTP server in Bun

sadeedpv

Sadeedpv🥇

Posted on September 19, 2023

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

💖 💪 🙅 🚩
sadeedpv
Sadeedpv🥇

Posted on September 19, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related