ElysiaJS ile Bun Üzerinde Ultra-Hızlı TypeScript API Geliştirme
Backend geliştirme dünyası hızla evrilirken, performans ve geliştirici deneyimi arasındaki denge her zamankinden daha önemli hale geldi. Bun runtime'ının sunduğu ham hız ile ElysiaJS framework'ünün ergonomik API tasarımı bir araya geldiğinde, ortaya TypeScript ekosisteminin en hızlı web framework'lerinden biri çıkıyor.
Bu yazıda ElysiaJS'in ne olduğunu, neden bu kadar hızlı olduğunu ve sıfırdan production-ready bir API nasıl geliştireceğinizi detaylıca inceleyeceğiz.
Bun Nedir ve Neden Önemli?
Bun, Jarred Sumner tarafından geliştirilen ve JavaScriptCore motorunu kullanan bir JavaScript/TypeScript runtime'ıdır. Node.js'in kullandığı V8 motorunun aksine, Apple'ın Safari tarayıcısında kullandığı JavaScriptCore motoru üzerine inşa edilmiştir.
Bun'un öne çıkan özellikleri:
- Paket yöneticisi:
npm installyerinebun installile 25 kata kadar daha hızlı paket kurulumu - Bundler: Yerleşik bundler ile esbuild'e rakip performans
- Test runner: Jest uyumlu, yerleşik test çalıştırıcı
- TypeScript desteği: Sıfır konfigürasyonla TypeScript çalıştırma
- Node.js uyumluluğu: Çoğu Node.js API'si ve npm paketi ile uyumlu çalışma
# Bun kurulumu (macOS, Linux, WSL)
curl -fsSL https://bun.sh/install | bash
# Versiyon kontrolü
bun --versionElysiaJS'e Giriş
ElysiaJS, Bun runtime'ı için özel olarak optimize edilmiş, end-to-end type safety sunan bir TypeScript web framework'üdür. Benchmark'larda Express.js'ten 18 kata kadar, Fastify'dan ise 3-4 kata kadar daha hızlı performans göstermektedir.
ElysiaJS'in Temel Avantajları
- Statik Kod Analizi: ElysiaJS, route handler'larınızı derleme zamanında analiz ederek gereksiz overhead'i ortadan kaldırır
- End-to-End Type Safety: Request'ten response'a kadar tam tip güvenliği
- Eden Treaty: Frontend ile backend arasında otomatik tip senkronizasyonu
- Plugin Ekosistemi: Swagger, JWT, CORS gibi hazır plugin'ler
- Ergonomik API: Temiz, zincirleme (chainable) method yapısı
Proje Kurulumu
Haydi sıfırdan bir ElysiaJS projesi oluşturalım:
# Yeni proje oluşturma
bun create elysia my-api
cd my-api
# Bağımlılıkları yükleme
bun install
# Geliştirme sunucusunu başlatma
bun run devProje yapısı şu şekilde oluşacaktır:
my-api/
├── src/
│ └── index.ts
├── package.json
├── tsconfig.json
└── bun.lockbİlk API Endpoint'imiz
src/index.ts dosyasını açalım ve ilk API'mizi yazalım:
import { Elysia } from 'elysia';
const app = new Elysia()
.get('/', () => 'Merhaba ElysiaJS! 🦊')
.get('/api/health', () => ({
status: 'ok',
timestamp: new Date().toISOString(),
runtime: 'Bun',
framework: 'ElysiaJS'
}))
.listen(3000);
console.log(
`🦊 ElysiaJS sunucusu http://${app.server?.hostname}:${app.server?.port} adresinde çalışıyor`
);Bu kadar! Sadece birkaç satır kodla çalışan bir API sunucunuz var. bun run src/index.ts komutuyla çalıştırabilirsiniz.
Route Yapısı ve HTTP Metodları
ElysiaJS, tüm standart HTTP metodlarını destekler ve zincirleme (method chaining) yapısıyla son derece okunabilir bir kod yazmanızı sağlar:
import { Elysia, t } from 'elysia';
interface User {
id: number;
name: string;
email: string;
}
const users: User[] = [
{ id: 1, name: 'Ahmet Yılmaz', email: 'ahmet@example.com' },
{ id: 2, name: 'Elif Demir', email: 'elif@example.com' }
];
const app = new Elysia()
// Tüm kullanıcıları listele
.get('/api/users', () => users)
// ID ile kullanıcı getir
.get('/api/users/:id', ({ params: { id } }) => {
const user = users.find(u => u.id === Number(id));
if (!user) {
throw new Error('Kullanıcı bulunamadı');
}
return user;
})
// Yeni kullanıcı oluştur
.post('/api/users', ({ body }) => {
const newUser: User = {
id: users.length + 1,
...body as Omit<User, 'id'>
};
users.push(newUser);
return { message: 'Kullanıcı oluşturuldu', user: newUser };
})
// Kullanıcı güncelle
.put('/api/users/:id', ({ params: { id }, body }) => {
const index = users.findIndex(u => u.id === Number(id));
if (index === -1) throw new Error('Kullanıcı bulunamadı');
users[index] = { ...users[index], ...body as Partial<User> };
return { message: 'Kullanıcı güncellendi', user: users[index] };
})
// Kullanıcı sil
.delete('/api/users/:id', ({ params: { id } }) => {
const index = users.findIndex(u => u.id === Number(id));
if (index === -1) throw new Error('Kullanıcı bulunamadı');
const deleted = users.splice(index, 1);
return { message: 'Kullanıcı silindi', user: deleted[0] };
})
.listen(3000);Validasyon ile Type Safety
ElysiaJS'in en güçlü özelliklerinden biri TypeBox tabanlı şema validasyonudur. t modülü ile request ve response'larınızı hem runtime'da hem de derleme zamanında doğrulayabilirsiniz:
import { Elysia, t } from 'elysia';
const app = new Elysia()
.post(
'/api/users',
({ body }) => {
// body otomatik olarak tiplenmiştir!
// body.name -> string
// body.email -> string
// body.age -> number
return {
message: `Hoş geldin, ${body.name}!`,
user: { id: Date.now(), ...body }
};
},
{
body: t.Object({
name: t.String({ minLength: 2, maxLength: 50 }),
email: t.String({ format: 'email' }),
age: t.Number({ minimum: 18, maximum: 120 })
}),
response: t.Object({
message: t.String(),
user: t.Object({
id: t.Number(),
name: t.String(),
email: t.String(),
age: t.Number()
})
}),
detail: {
tags: ['Kullanıcılar'],
summary: 'Yeni kullanıcı oluştur'
}
}
)
.listen(3000);Yanlış bir request gönderildiğinde ElysiaJS otomatik olarak anlamlı hata mesajları döner:
{
"type": "validation",
"on": "body",
"message": "Expected string with format 'email'",
"expected": "{ name: string, email: string, age: number }",
"found": "{ name: 'Test', email: 'invalid', age: 25 }"
}Plugin Sistemi ve Modüler Yapı
ElysiaJS'in plugin sistemi, kodunuzu modüler parçalara ayırmanızı ve yeniden kullanılabilir bileşenler oluşturmanızı sağlar:
// plugins/auth.ts
import { Elysia, t } from 'elysia';
import { jwt } from '@elysiajs/jwt';
export const authPlugin = new Elysia({ name: 'auth' })
.use(
jwt({
name: 'jwt',
secret: process.env.JWT_SECRET || 'super-gizli-anahtar'
})
)
.derive(async ({ jwt, headers }) => {
const token = headers.authorization?.replace('Bearer ', '');
if (!token) {
return { user: null };
}
const payload = await jwt.verify(token);
return { user: payload };
})
.macro(({ onBeforeHandle }) => ({
requireAuth(enabled: boolean) {
if (!enabled) return;
onBeforeHandle(({ user, error }) => {
if (!user) return error(401, 'Yetkilendirme gerekli');
});
}
}));// routes/posts.ts
import { Elysia, t } from 'elysia';
import { authPlugin } from '../plugins/auth';
export const postsRoutes = new Elysia({ prefix: '/api/posts' })
.use(authPlugin)
// Herkese açık endpoint
.get('/', () => {
return { posts: [{ id: 1, title: 'İlk Yazı' }] };
})
// Korumalı endpoint
.post(
'/',
({ body, user }) => {
return {
message: 'Yazı oluşturuldu',
post: { ...body, author: user?.sub }
};
},
{
requireAuth: true,
body: t.Object({
title: t.String({ minLength: 3 }),
content: t.String({ minLength: 10 })
})
}
);// src/index.ts
import { Elysia } from 'elysia';
import { swagger } from '@elysiajs/swagger';
import { cors } from '@elysiajs/cors';
import { postsRoutes } from './routes/posts';
const app = new Elysia()
.use(swagger({
documentation: {
info: {
title: 'My API',
version: '1.0.0',
description: 'ElysiaJS ile geliştirilmiş ultra-hızlı API'
}
}
}))
.use(cors())
.use(postsRoutes)
.get('/', () => ({ message: 'API çalışıyor!' }))
.listen(3000);
console.log('🦊 API hazır: http://localhost:3000');
console.log('📚 Swagger UI: http://localhost:3000/swagger');Gerekli plugin'leri kurmayı unutmayın:
bun add @elysiajs/swagger @elysiajs/cors @elysiajs/jwtHata Yönetimi
ElysiaJS, yapılandırılmış hata yönetimi için zarif bir API sunar:
import { Elysia, t, NotFoundError } from 'elysia';
class BusinessError extends Error {
constructor(
public statusCode: number,
message: string
) {
super(message);
}
}
const app = new Elysia()
// Global hata yakalayıcı
.onError(({ code, error, set }) => {
if (error instanceof BusinessError) {
set.status = error.statusCode;
return {
success: false,
error: error.message,
code: error.statusCode
};
}
switch (code) {
case 'NOT_FOUND':
set.status = 404;
return { success: false, error: 'Kaynak bulunamadı' };
case 'VALIDATION':
set.status = 422;
return { success: false, error: 'Validasyon hatası', details: error.message };
case 'INTERNAL_SERVER_ERROR':
set.status = 500;
return { success: false, error: 'Sunucu hatası' };
default:
return { success: false, error: 'Bilinmeyen hata' };
}
})
.get('/api/products/:id', ({ params: { id } }) => {
if (Number(id) > 100) {
throw new BusinessError(404, 'Bu ID ile ürün bulunamadı');
}
if (Number(id) < 0) {
throw new BusinessError(400, 'Geçersiz ürün ID');
}
return { id: Number(id), name: 'Örnek Ürün', price: 99.99 };
})
.listen(3000);Veritabanı Entegrasyonu: Drizzle ORM Örneği
Gerçek bir projede veritabanı entegrasyonu şarttır. ElysiaJS, herhangi bir ORM ile sorunsuz çalışır. İşte Drizzle ORM ile bir SQLite örneği:
bun add drizzle-orm drizzle-kit better-sqlite3
bun add -d @types/better-sqlite3// db/schema.ts
import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core';
export const users = sqliteTable('users', {
id: integer('id').primaryKey({ autoIncrement: true }),
name: text('name').notNull(),
email: text('email').notNull().unique(),
createdAt: text('created_at').default('CURRENT_TIMESTAMP')
});
export type User = typeof users.$inferSelect;
export type NewUser = typeof users.$inferInsert;// db/index.ts
import { drizzle } from 'drizzle-orm/bun-sqlite';
import { Database } from 'bun:sqlite';
import * as schema from './schema';
const sqlite = new Database('myapp.db');
export const db = drizzle(sqlite, { schema });// routes/users.ts
import { Elysia, t } from 'elysia';
import { db } from '../db';
import { users } from '../db/schema';
import { eq } from 'drizzle-orm';
export const userRoutes = new Elysia({ prefix: '/api/users' })
.get('/', async () => {
const allUsers = await db.select().from(users);
return { data: allUsers, count: allUsers.length };
})
.get('/:id', async ({ params: { id }, error }) => {
const user = await db.select().from(users).where(eq(users.id, Number(id)));
if (!user.length) return error(404, 'Kullanıcı bulunamadı');
return { data: user[0] };
})
.post(
'/',
async ({ body }) => {
const result = await db.insert(users).values(body).returning();
return { message: 'Oluşturuldu', data: result[0] };
},
{
body: t.Object({
name: t.String({ minLength: 2 }),
email: t.String({ format: 'email' })
})
}
);Eden Treaty: Frontend ile Tam Tip Entegrasyonu
ElysiaJS'in en etkileyici özelliklerinden biri Eden Treaty'dir. Backend'deki tipleri otomatik olarak frontend'e taşır — tıpkı tRPC gibi, ama REST API üzerinde:
bun add @elysiajs/eden// Frontend tarafı (React, Next.js, vb.)
import { treaty } from '@elysiajs/eden';
import type { App } from '../server'; // Backend'den tip import'u
const api = treaty<App>('http://localhost:3000');
// Tam otomatik tamamlama ve tip güvenliği!
const { data, error } = await api.api.users.get();
// data otomatik olarak User[] tipinde
if (data) {
data.forEach(user => {
console.log(user.name); // TypeScript biliyor ki bu string
});
}
// POST isteği - body tipi otomatik olarak kontrol edilir
const { data: newUser } = await api.api.users.post({
name: 'Yeni Kullanıcı',
email: 'yeni@example.com'
});Performans Karşılaştırması
Aşağıdaki tablo, farklı framework'lerin saniyedeki istek sayısını (requests/second) göstermektedir (basit JSON response benchmark'ı):
| Framework | Runtime | req/sec |
|---|---|---|
| ElysiaJS | Bun | ~320.000 |
| Hono | Bun | ~280.000 |
| Fastify | Node.js | ~78.000 |
| Koa | Node.js | ~45.000 |
| Express.js | Node.js | ~18.000 |
Not: Benchmark sonuçları donanım, test koşulları ve payload boyutuna göre değişiklik gösterebilir. Bu veriler genel bir fikir vermesi amaçlıdır.
ElysiaJS'in bu performansa ulaşmasının arkasındaki ana faktörler:
- Statik analiz: Route handler'larını derleme zamanında optimize etme
- JIT derleme: Hot path'lerin runtime'da optimize edilmesi
- Bun runtime: JavaScriptCore motorunun doğal hız avantajı
- Minimal overhead: Gereksiz middleware katmanlarının olmaması
Production Checklist
Projenizi production'a almadan önce kontrol etmeniz gereken maddeler:
// production-ready src/index.ts
import { Elysia } from 'elysia';
import { swagger } from '@elysiajs/swagger';
import { cors } from '@elysiajs/cors';
import { helmet } from 'elysia-helmet';
import { rateLimit } from 'elysia-rate-limit';
const app = new Elysia()
.use(cors({ origin: process.env.ALLOWED_ORIGINS?.split(',') || ['http://localhost:3000'] }))
.use(helmet())
.use(rateLimit({ max: 100, duration: 60000 }))
.use(swagger({ path: '/docs' }))
// İstek loglama
.onRequest(({ request }) => {
console.log(`[${new Date().toISOString()}] ${request.method} ${request.url}`);
})
// Graceful shutdown
.listen({
port: Number(process.env.PORT) || 3000,
hostname: '0.0.0.0'
});
process.on('SIGTERM', () => {
console.log('Sunucu kapatılıyor...');
app.stop();
process.exit(0);
});Sonuç
ElysiaJS ve Bun ikilisi, TypeScript ile backend geliştirme deneyimini tamamen yeni bir seviyeye taşıyor. Bu yazıda incelediğimiz temel noktaları özetleyelim:
- Bun, Node.js'e kıyasla önemli performans avantajları sunan modern bir JavaScript runtime'ıdır
- ElysiaJS, Bun için optimize edilmiş, end-to-end type-safe bir web framework'üdür
- TypeBox tabanlı validasyon sayesinde hem runtime hem derleme zamanı güvenliği sağlanır
- Plugin sistemi ile modüler ve yeniden kullanılabilir kod yazmak kolaydır
- Eden Treaty ile frontend-backend arasında otomatik tip senkronizasyonu mümkündür
- Benchmark'larda Express.js'ten 18x, Fastify'dan 4x daha hızlı performans gösterir
Eğer yeni bir API projesi başlıyorsanız veya mevcut Node.js projelerinizin performans sınırlarını zorluyorsanız, ElysiaJS + Bun kombinasyonunu kesinlikle değerlendirmelisiniz. Özellikle TypeScript'e olan doğal uyumu ve sıfır konfigürasyon felsefesi, geliştirici deneyimini üst düzeye çıkarıyor.
Başlamak için tek yapmanız gereken:
bun create elysia my-next-projectHızlı geliştirmeler! 🦊