Frameworks of BunJS: ElysiaJS and Hono
Tags: webdev, javascript, bunjs, typescript
Published: 2024-05-15 01:44:52

BunJS is hot, but what about frameworks?  This post dives into ElysiaJS and Hono, exploring how they can take your BunJS development to the next level.

---

## What's BunJS
BunJS is a fast and versatile JavaScript runtime designed to be a drop-in replacement for Node.js. Similar to Node.js and Deno, it allows you to execute JavaScript code outside of a web browser. However, BunJS stands out for its focus on performance.

BunJS was built from scratch to focus on four key points:

- Speed: BunJS utilizes the latest JavaScriptCore engine and is written in Zig, a language known for its efficiency. This combination contributes to BunJS's exceptional performance compared to Node.js and Deno used V8 engine.

- All-in-one Toolkit: BunJS goes beyond just a runtime. It provides a built-in bundler, test runner, and package manager, streamlining the development workflow.

- Node.js Compatibility: BunJS natively implements many Node.js APIs and Web APIs, making it easier to migrate existing Node.js projects.

- TypeScript Support: BunJS offers seamless integration with TypeScript, allowing developers to write robust and maintainable code.

According to the official site
> The goal of Bun is to run most of the world's server-side JavaScript and provide tools to improve performance, reduce complexity, and multiply developer productivity.

In summary, BunJS is a promising newcomer in the JavaScript runtime landscape, offering exceptional speed, a comprehensive toolkit, and compatibility with existing Node.js code. It's worth considering for projects that demand high performance and a smooth development experience.

## Compare speed of NodeJS and BunJS
I found the performance benchmarking of 5 runtime: Bun, NodeJS, Go, C# and Python. You can read full post [here](https://www.wwt.com/blog/performance-benchmarking-bun-vs-c-vs-go-vs-nodejs-vs-python)

![Compare speed of NodeJS, Bun, C# and Python](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dctw351icypm9gnkm0g6.png)

The result of the benchmarking according to requests per second shows that: in the beta version, Bun is almost twice as fast as NodeJS (with Fastify framework).

Next, we will explore the prominent frameworks currently available for BunJS.

---

## ElysiaJS

Elysia is a HTTP API framework written on Bun with developer experience being the main motivation of the framework. Essentially, it’s a faster Express.js replacement.
You can create new Elysia project using command:
```
bun create elysia <name of the project>
```

A basic Elysia endpoint could look like this:

![Elysia](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5rw02zkt37zk8102m2jd.png)

### Routing
We can define a route by calling a method named after HTTP verbs, passing a path and a function to execute when matched.

![Elysia routing](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5l801i8zh3h33q811lti.png)

Elysia is using method chaining to synchronize type safety for later use. So, always use method chaining in Elysia.

![Elysia method chaning](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k49bf5y44aaw0dz83g86.png)

Addition, Elysia accept custom HTTP Methods with `Elysia.route`

![Elysia custom method](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/quhticfzcwrpbt82r7mr.png)

### Path
The path in Elysia can be grouped into 3 types:
- Static paths - static string to locate the resource
- Dynamic paths - segment can be any value
- Wildcards - path until a specific point can be anything

The priorities are as follows: static > dynamic > wildcards

![Elysia path](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ac6mlp2uuhwu2ppokern.png)

### Handler & Context
After a resource is located, a function that respond is refers as handler

![Elysia handler](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2ss2dadw5gu7dmp8p973.png)

Context is a request information passed to a route handler. Context is unique for each request, and is not shared except for store which is a global mutable state. Elysia context includes path, body, query, params, headers, request, redirect, store, cookie, set and error. You can see more information about it in [the official documents](https://elysiajs.com/essential/context.html) of Elysia.

### Life cycle event
Elysia's Life Cycle event can be illustrated as the following

![Elysia life cycle](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lrupx127zvjxcb59unky.png)

### Plugin
The plugin pattern is a powerful approach to manage this complexity. It promotes modularity, which means breaking down functionality into smaller, reusable pieces.

We can use the plugin by passing an instance to `Elysia.use`.

![Elysia plugin](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h4ope0w2npwl93qynm05.png)

The plugin will inherit all properties of the plugin instance, including state, decorate, derive, route, lifecycle....

Elysia will also handle the type inference automatically as well, so you can imagine as if you call all of the other instances on the main one.

You can find an officially maintained plugin at [Elysia's plugins](https://elysiajs.com/plugins/overview.html)

I have created a boilerplate for Elysia. You can find it [here](https://github.com/jthanh8144/elysia-boilerplate). It's based on three repositories, thanks to its authors.
- https://github.com/elysiajs/awesome-elysia?tab=readme-ov-file
- https://github.dev/OthmanAmoudi/Elysiajs-api-controllers
- https://github.dev/Leka-Workshop/Bun-CRUD-App

---

## Hono
Hono is a lightweight, simple, and fast web framework for Cloudflare Workers, Deno, Bun, and other applications. It is a modern web application that is both fast and flexible. It offers inbuilt support for TypeScript, and easy development in a local environment.

Start Hono project with command
```
bun create hono <name of the project>
cd <name of the project>
bun install
```

The basic project same as

![Hono basic](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gswtvish2w9ly73z2zjf.png)

### Routing
Hono provides two main routing mechanisms:
- TrieRouter: This is the default and recommended router. It utilizes a trie data structure for efficient pattern matching, leading to faster performance, especially for applications with a large number of routes.
- RegExpRouter: it is the fastest router in the JavaScript world. They are using linear loops. Therefore, regular expression matching will be performed for all routes and the performance will be degraded as you have more routes.

Furthermore, Hono also has three other types of routers: SmartRouter, LinearRouter, and PatternRouter.

![Hono routing](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5kj4qngfiq5xxbvf5iey.png)

### Context
To handle Request and Response, you can use `Context` object.
It includes:
- req
- body()
- text()
- json()
- html()
- notFound()
- redirect()
- res
- etc…

You can read more information about it in the official document of Hono.

### Exception
When a fatal error occurs, such as authentication failure, an HTTPException must be thrown.

This example throws an HTTPException from the middleware.

![Hono throw exception](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8tommc6ma51dnc5jnhv4.png)

You can handle the thrown HTTPException with `app.onError`.

![Hono handle exception](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/prxcntz45mfbj3u9mwim.png)

### Middleware
Middleware works after/before Handler. We can get Request before dispatching or manipulate Response after dispatching.

Definition of Middleware:
- Handler - should return Response object. Only one handler will be called.
- Middleware - should return nothing, will be proceeded to next middleware with await next()

The user can register middleware using `app.use` or using `app.HTTP_METHOD` as well as the handlers. For this feature, it's easy to specify the path and the method.

![Hono middleware](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ybkm4e1dny4tqlkomzqo.png)

**Execution order**: The order in which Middleware is executed is determined by the order in which it is registered. The process before the next of the first registered Middleware is executed first, and the process after the next is executed last. See below.

![Hono middleware execution order](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3z6o9ichozocved70nzc.png)

Result is the following

![Hono middleware execution order result](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/52fhn25w02aqpndhgq92.png)

Hono has some built-in middlewares such as logger or basicAuth. You can write your own middleware directly inside `app.use()`. Besides, Hono also has some third-party middlewares of GraphQL Server, Sentry, Firebase Auth… (You can see more [here](https://hono.dev/middleware/third-party)) So with them, we may make a more complex application.

### Client components
In Hono, you can write HTML with JSX syntax with `hono/jsx`.

On the server side, it supports some features of React such as Fragment, child props, optimizing components using `memo`, context, etc.

![Hono client components](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nu4meyemcjd2ixjt3whr.png)

`hono/jsx` supports not only server side but also client side. It’s called Client Components or hono/jsx/dom. It has Hooks that are compatible or partially compatible with React: useState, useEffect, useRef, useCallback...

Here is an example of a simple counter, the same code works as in React.

![Hono with react hook](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hnpvaf8iw4xpexyxqzpj.png)

### Boilerplate
You can refer to my Hono API boilerplate [here](https://github.com/jthanh8144/hono-boilerplate).

---

## Speed test ElysiaJS, Hono, Express, Fastify and NestJS (Express)

Unit: req/s

|       Framework      | Runtime |   Average   |   Get(/)   | Params, query & header | Post JSON |
|:--------------------:|:-------:|-------------|------------|------------------------|-----------|
|ElysiaJS              |Bun      |275,063.507  |326,868.9   |261,729.3               |236,592.32 |
|Hono                  |Bun      |257,532.08   |320,757.07  |233,769.22              |218,069.95 |
|Fastify               |Node     |64,145.95    |74,631.46   |66,235.48               |51,570.91  |
|ExpressJS             |Node     |16,301.823   |17,974.35   |17,090.62               |13,840.5   |
|NestJS (Express)      |Node     |14,978.863   |16,926.01   |15,507.62               |12,502.96  |

Reference
- https://github.com/SaltyAom/bun-http-framework-benchmark/tree/c7e26fe3f1bfee7ffbd721dbade10ad72a0a14ab#results

---

## Conclusion
BunJS has emerged as a promising player in the JavaScript runtime landscape. Its focus on speed, efficiency, and a streamlined development experience makes it an attractive option for modern web development. In this post, we explored two exciting BunJS frameworks: ElysiaJS and Hono.

ElysiaJS provides a comprehensive solution for building full-fledged web applications, offering features like routing, templating, and form handling. If you're looking for a rapid development framework to jumpstart your BunJS project, ElysiaJS is a strong contender.

Hono, on the other hand, excels in lightweight routing and server-side functionality. Its flexibility and focus on performance make it ideal for building APIs and microservices. Whether you're building a complex web application or a performant API, BunJS, with frameworks like ElysiaJS and Hono, offers a powerful and versatile ecosystem for your next project.

Consider diving deeper into these frameworks and exploring the possibilities BunJS unlocks. With its focus on speed and a growing ecosystem, BunJS is definitely worth keeping an eye on for the future of JavaScript development.