Discover Elysia: The Exciting New Framework for Building APIs with TypeScript

Wallop O-pasakhun
6 min readSep 6, 2024

--

src: https://elysiajs.com/

Recently, I discovered a fantastic new framework that leverages the Bun runtime, which boasts impressive performance improvements over traditional frameworks like NestJS and Express. This new framework not only streamlines development with its modern features but also delivers significantly faster response times and handles higher throughput with ease. If you’re looking for a cutting-edge solution that enhances both speed and efficiency, this framework might be exactly what you need to take your API development to the next level.

In this blog post, I’ll share what I’ve learned about connecting to a database and performing GET requests to explore some of Elysia’s features. By the end, you’ll understand why Elysia could become your new go-to tool for API development.

What is ElysiaJS

Feature sheet from ElysiaJS Website

ElysiaJS is a cutting-edge API framework built to harness the power of Bun as its runtime environment. As the flagship API framework for Bun, ElysiaJS represents the forefront of innovation in web development, offering developers a modern toolset that integrates seamlessly with Bun’s high-performance capabilities.

Recently, ElysiaJS was officially released into production by X (formerly known as Twitter), marking a significant milestone in its development and adoption. This release underscores the framework’s robustness and reliability, making it a strong contender in the API development landscape.

Prerequisites

First, Elysia is a framework that runs on Bun, so let’s start by installing Bun.

For MacOS & Linux

curl -fsSL https://bun.sh/install | bash

For Windows

powershell -c "irm bun.sh/install.ps1 | iex"

Install Elysia and Set Up Your Project

Once you have Bun installed, setting up Elysia for your project is straightforward. In this example, we’ll use a Master API for managing Thai location addresses.

First, use Bun to create a new Elysia project by running:

bun create elysia address-api

This command initializes a new Elysia project named address-api, setting up the basic project structure and files.

Then, move into the project folder

cd address-api

Since we will be using PostgreSQL with TypeORM for this project, you’ll need to add the appropriate dependencies. Install them using:

bun add @elysiajs/swagger pg typeorm

Additionally, for TypeScript type definitions, install the devDependencies:

bun add -D @types/pg

Run and Test Your Project

After setting up your project and installing the necessary dependencies, let’s run and test it first.

Use this command to start development server.

bun dev

Now, open browser or Postman to see the results at http://localhost:3000

Successfully run result with Postman

Then let’s make a small change. Typically, APIs have /api as a prefix (or /api/v1/ or else if you want), so let's add that prefix to our project.

const app = new Elysia({ prefix: "/api" })
.get("/", () => "Hello Elysia")

Now, our index.ts should look like this:

./src/index.ts

Now if we run it again in Postman should be like this.

Postman results, prefix /api added.

Swagger API

Elysiajs has the very easy way to add swagger API to the project. All we need to do is import Swagger:

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

Then update the code as follows

const app = new Elysia({ prefix: "/api" })
.use(swagger())
.get("/", () => "Hello Elysia");

Then, we can access it by opening http://localhost:3000/api/swagger. In this case, /api/swagger is the path because we added the /api prefix. By default, Swagger is accessible at /swagger, but you can customize the path by adding the path option inside swagger(), like this:

.use(swagger({ path: '/custom-swagger-path' }))

The result should be like this.

Database Integration

Now, let’s set up the database connection. Create a new file called ./src/datasource.ts and define it as follows. In this blog, we'll assume that you already have PostgreSQL set up and running. Feel free to setup and config it as yours.

./src/datasource.ts

Next, update your index.ts file to include the database initialization:

async function initializeDatabase() {
try {
await myDataSource.initialize();
console.log("Database connection established");
} catch (error) {
console.error("Error connecting to the database", error);
}
}

initializeDatabase();

Now our index.ts file should be like this.

Entities Setup

Next, let’s set up the entities. In TypeORM, the table structure definition is called an Entity. These entities define the structure of your database tables. Create the following files for your entities.

  • Assuming you have created tables with the same names as specified in the @Entity decorators, these entities will map to your database tables correctly.

1. Province Entity

./src/entities/province.ts

This entity defines a table named province with columns for id, name_th, and name_en.

2. District Entity

./src/entities/district.ts

This entity defines a table named district with columns for id, name_th, name_enand province_id for link with Province entity.

3. Subdistrict Entity

./src/entities/subdistrict.ts

This entity defines a table named subdistrict with columns for id, name_th, name_en, zip_code and district_id for link with District entity.

Next, to inform TypeORM that these files are entities for the database, add them to datasource.ts so that datasource.ts will look like this:

./src/datasource.ts

Controllers

Now, move to the controller section. Create the file ./src/controllers/location.ts and add the following code:

./src/controllers/location.ts

This locationController provides methods to fetch and sort data related to provinces, districts, and subdistricts from your database.

  • provinces: Retrieves a list of all provinces ordered by their Thai names.
  • districtsByProvinceId: Retrieves a list of districts for a given province, ordered by their Thai names.
  • subdistrictsByDistrictId: Retrieves a list of subdistricts for a given district, ordered by their Thai names.

Routes

Now, the last section: routes
Let’s create ./src/routes/location.ts as follows:

./src/routes/location.ts

Include all things to the App

Let’s add our route into the app in index.ts like follows

app.use(Locations);

so our index.ts should be like this

./src/index.ts

Let’s run and check our API

/api/locations/provinces
/api/locations/districts/33 (33 is example province id)
/api/locations/subdistricts/33 (33 is example district id)

Now let’s check the swagger, it could be automatically updated right now.

Updated Swagger Documentation

Check some routes

A Route Testing

Conclusion

Elysia offers an exciting and streamlined approach to building APIs with TypeScript. Its powered by Bun, support for Swagger documentation, and compatibility with PostgreSQL via TypeORM make it a compelling choice for modern API development. One of the standout features of Elysia is its impressive performance. Benchmark tests have shown that Elysia outperforms many traditional API frameworks, delivering faster response times and handling higher throughput with ease.

In our next blog, we’ll dive into creating, updating, and deleting requests, as well as explore performance optimizations and authentication strategies.

Happy coding! And have fun with Elysia.

--

--

Wallop O-pasakhun

A full-stack developer with skills focused on frontend development.