Elysia Framework
This article will show you how to create a server using the bun Elysia Framework. By the end you will know about the following features:
- Understand the bun Javascript Runtime to create an api
- Basic setup and server scaffolding
- Get requests with URL parameters
- Post requests with body data
- Architecture for organising your routes
- Sending responses to the end user
I won’t cover anything generic such as database connections so that we can concentrate on the topic at hand. That said, it’s important to remember that bun is a drop in replacement for node js, so any business logic you need can simply be copy pasted from a node implementation with zero compatibility issues!
Setup bun Elysia App
The first step is to setup bun and create the Elysia app.
- Open up Visual Studio Code
- Open your working folder with “File => Open Folder”
- Open a terminal with “Terminal => New Terminal”
- In terminal type: “bun create Elysia myApp” (myApp is the name of your app)
- Follow the onscreen instructions, simply accepting the defaults
Run Server
Now that your Elysia server has been created you should test it out. By default an npm script to run the server should be in package.json under the scripts section:
“scripts”: {
“test”: “echo \”Error: no test specified\” && exit 1″,
“dev”: “bun run –watch src/index.ts”
},
If this is not there then go ahead and add it. Then in your terminal, build and create the Elysia instance:
npm run dev
You should see “Elysia is running at localhost:3000” in the terminal. Use your browser to go to localhost:3000 and you should see a welcome message corresponding to what’s in your index.ts server declaration code. At this point you may also want to check out my guide on hot reloading natively.
Create a Get Route
Before we start, a word on architecture … one option to create your get route is just to place it straight into the index.ts file but after adding a couple of routes that would really clutter up the index file!
Let’s use good architecture practice instead:
- Create a “routes” folder in the “src” folder.
- Create a “shop.ts” file in that routes folder.
This shop.ts file will hold all the routes corresponding to our pseudo online shopping server. Open that file and paste in the following code:
import { Elysia } from "elysia";
const shopRoutes = new Elysia({ prefix: '/shop' })
.get('/', () => 'HOME PAGE for shop, welcome')
export default shopRoutes;
A couple of notes on the above code:
We first import the Elysia class as it contains everything we need to define all things “server”. Then we define a constant called “shopRoutes” which will hold all of our routes.
This takes an optional object in which we can define things such as the URL prefix. In this case we have defined “/shop” so this route group will only be called if the URL entered has this form:
Eg: localhost:3000/shop
You should have noticed that the server auto reloads when you save any code. The –watch option in the dev script watches all our code, automatically reloading when required!
Although if we tried this route now it wouldn’t work as we need to specify that these routes exist in the index.ts file.
Add Route Groups
Open up the index.ts file and clear out all the boilerplate code. Paste in the following lines:
import { Elysia } from "elysia";
import shopRoutes from "./routes/shop"
const app = new Elysia()
app
.group('', (app) => app.use(shopRoutes)) //ADDITION OF ROUTE GROUPS
.listen(process.env.PORT || 3000);
console.log(
`🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`
);
Our “shopRoutes” are imported at the top of file which can then be used the .group function. This registered those routes with the main Elysia object.
Now open up any browser and navigate to localhost:3000/shop to see your bun GET request in action!
Add URL Parameters to a GET Route
On a bun server you will need a way to retrieve data based on some variable passed in from the client.
For example, in our shop we may want to retrieve a product based on a code or product name. Usually this is specified in a GET route using a URL parameter, ie a subsection of the URL. The example below will take the last part of the URL as a variable which is used to find product information and return it:
myshop.com/shop/butter => Should return a page for butter
myshop.com/shop/ribeye => Should return a page for ribeye steak
This is pretty easy to implement in bun. Let’s open up the routes/shop.ts file and add a second GET route as marked below:
import { Elysia } from "elysia";
const shopRoutes = new Elysia({ prefix: '/shop' })
.get('/', () => 'HOME PAGE for shop, welcome')
.get('/:item', ({ params: params }) => "Returned data for: " + params.item) //ADD THIS LINE
export default shopRoutes;
Test it out by going to localhost:3000/shop/shampoo. You should get “Returned data for shampoo” in your browser!
Let’s now step through this GET request so you understand it fully. The URL is defined as having a variable with the colon character ( : ).
Any name after that will be the variable name but you have to explicitly pass the params to the response function! This is done with the { params: params } argument, then the anonymous function will have access to your variables.
Note that the above is a shortened version of reality. You would normally be doing a database lookup somewhere based on the variable (usually an id that is passed over).
Create a POST Route with Body Data
As you probably know POST routes differ from GET routes as the former sends structured data along with the request. Usually this data would be too complex to include in a bun GET parameter so instead is bundled into the BODY of the POST request.
Using the routes/shop.ts we already have in place we will add a POST route that allows us to send over some data about a users cart. Start by adding a route as below:
import { Elysia } from "elysia";
const shopRoutes = new Elysia({ prefix: '/shop' })
.get('/', () => 'HOME PAGE for shop, welcome')
.get('/:item', ({ params: params }) => "Returned data for: " + params.item)
.post('/cart', ({ body }) => JSON.stringify(body)) //ADD THIS LINE
export default shopRoutes;
Now there are tools in Chrome that you can use to send a POST request but for sake of speed we’ll issue a curl command (on Linux / Mac). Simply copy and paste the following into your terminal:
curl localhost:3000/shop/cart -X POST -H “Content-Type: application/json” -d ‘{“id”: “Bread”, “count”: 9}’
When we send over our CURL request when also send some body data with the type “application/json” (defined in the header -H). The body data is defined after the -d flag in the curl command.
Our bun application picks up the body, converts it to a string using JSON.stringify and sends it back to us. In reality you’d actually use this data to calculate the cart price or whatever your application needs.
Finally
If you liked this then you’ll love my full bun and Elysia course.
Appendix: Full Code
Here’s the code from index.ts
import { Elysia } from "elysia";
import shopRoutes from "./routes/shop"
const app = new Elysia()
app
.group('', (app) => app.use(shopRoutes))
.listen(process.env.PORT || 3000);
console.log(
`🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`
);
And here’s the code for routes/shop.ts
import { Elysia } from "elysia";
const shopRoutes = new Elysia({ prefix: '/shop' })
.get('/', () => 'HOME PAGE for shop, welcome')
.get('/:item', ({ params: params }) => "Returned data for: " + params.item)
.post('/cart', ({ body }) => JSON.stringify(body))
export default shopRoutes;