ElysiaJS is a TypeScript framework for the Bun runtime that prioritizes developer experience and performance. Today, we're going to use it to build a simple yet powerful URL shortener API.
Prerequisites
You'll need to have Bun installed. If you don't, you can install it with:
curl -fsSL https://bun.sh/install | bash
Step 1: Set Up Your Elysia Project
Let's create a new Elysia project. Bun makes this incredibly fast.
# Create a new Elysia app
bun create elysia my-shortener
# Navigate into the new directory
cd my-shortener
# We'll need a library to generate short, unique IDs
bun add nanoid
This will scaffold a simple src/index.ts file. We'll be working in there.
Step 2: Designing the API
Our URL shortener needs two things:
- A POST endpoint at /shorten to accept a long URL and return a short link.
- A GET endpoint at /:id to look up a short ID and redirect to the original long URL. To keep this simple, we'll use an in-memory object as our "database."
Step 3: Building the API (src/index.ts)
Open src/index.ts and replace the contents with the following code. We'll break it down below.
// src/index.ts
import { Elysia, t } from 'elysia';
import { nanoid } from 'nanoid';
// Our "database" to store URL mappings
const urlDatabase: Record<string, string> = {};
const BASE_URL = 'http://localhost:3000';
const app = new Elysia()
.get('/', () => 'Welcome to the URL Shortener!')
// 1. Endpoint to create a short URL
.post(
'/shorten',
({ body, set }) => {
const shortId = nanoid(7); // Generate a 7-character ID
urlDatabase[shortId] = body.url;
set.status = 201; // Created
return {
shortUrl: `${BASE_URL}/${shortId}`,
};
},
{
// 2. Built-in validation!
body: t.Object({
url: t.String({
format: 'uri',
error: 'Please provide a valid URL.',
}),
}),
}
)
// 3. Endpoint to handle redirection
.get('/:id', ({ params: { id }, set }) => {
const longUrl = urlDatabase[id];
if (!longUrl) {
set.status = 404;
return { error: 'URL not found' };
}
// 4. Perform the redirect
set.redirect = longUrl;
})
.listen(3000);
console.log(`🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`);
// This exports the type of our app for the client
export type App = typeof app;
Code Breakdown
POST /shorten: This endpoint takes a JSON body. We generate a short ID using nanoid, store the mapping in our urlDatabase, and return the newly created short URL.
Validation with t.Object: This is where Elysia shines. We define the expected shape of the body. Elysia will automatically validate incoming requests. If the url is missing or not a valid URI format, it will return a 400 Bad Request error with our custom message—all without any if statements from us!
GET /🆔 This dynamic route captures the short ID from the URL.
Redirection: If the ID exists in our database, we use set.redirect to send a 302 Found redirect response to the client's browser.
Step 4: Run and Test the API
Start the server:
bun run dev
Now, open a new terminal and test it with curl:
curl -X POST -H "Content-Type: application/json" -d '{"url": "https://elysiajs.com/"}' http://localhost:3000/shorten
Step 5. Using the Type-Safe Client
This is the coolest part. Elysia automatically provides a client library called Eden Treaty that gives you end-to-end type safety. You don't need to generate anything.
Create a new file src/client.ts to simulate a frontend or another service consuming our API.
// src/client.ts
import { treaty } from '@elysiajs/eden';
import type { App } from './index'; // Import the type of our app
// Create a typed client
const client = treaty<App>('http://localhost:3000');
async function main() {
console.log('Creating a short URL...');
// Notice the full type-safety and autocompletion here!
// It knows .shorten.post exists and expects a body with a `url` property.
const { data, error } = await client.shorten.post({
url: 'https://bun.sh',
});
if (error) {
return console.error('Error:', error.value);
}
console.log('Success! Short URL created:', data.shortUrl);
// Example output: Success! Short URL created: http://localhost:3000/abcdefg
}
main();
When you type client. in a modern editor, you'll get autocompletion for shorten and /. If you type client.shorten.post(), it will tell you that the body is required and must contain a url string. This is type safety that flows directly from your API definition to your client code.
Run the client script:
bun run src/client.ts
Conclusion
In just a few minutes, we built a fully functional, validated, and performant URL shortener. More importantly, we saw how ElysiaJS eliminates the friction between backend and frontend development with its magical, zero-config type-safe client.
Top comments (0)