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 has built-in TypeScript support and is blazingly fast ⚡earning it the favor of many JavaScript & TypeScript Developers 💘.
Installing Bun
Installing Bun is very simple. All you have to do is:
curl -fsSL https://bun.sh/install | bash
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
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}`
);
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}`
);
You can define your Routes with many built-in methods like:
app.get('/', () => {});
app.post('/', () => {});
app.put('/', () => {});
app.patch('/', () => {});
app.delete('/', () => {});
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}`;
});
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)
)
You can also install the cors
plugin which adds support for customizing Cross-Origin Resource Sharing behavior.
Install it with:
bun add @elysiajs/cors
Then use it:
import { cors } from "@elysiajs/cors";
const app = new Elysia();
app.use(cors());
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 });
Now, we need to create the Table
DB.query(
`CREATE TABLE IF NOT EXISTS MESSAGES(
id INTEGER PRIMARY KEY AUTOINCREMENT,
message TEXT
);`
).run();
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" },
});
});
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" },
});
});
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(),
}),
}
);
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/
And Booom!! 💥💥
{"message":"Added"}
{"messages": [{"id":1, "message": "This is a Reminder!"}]}
Here are some tasks for you:
- Create
PUT
andDELETE
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.
Also, take a look at how fast Elysia is compared to Express:
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)
Just FYI you can use
Response.json
this will automatically do the parsing and set the headersoh, thank you I didn't know that 😄🙌!
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.
Middlewares in Elysia is a bit different compared to Express. See if this helps
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?
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 👍
I was looking for this tutorial just now, thnks
You are welcome!
So the future of JS runtime is Bun :love:
💘💘
Really helpful ❤️
I'm glad that I saw this 😁
Glad you liked it!
Too many Runtimes and JS frameworks. My mind is exploding XD
But is good to know that the technology advances so fast
🥲🥲