Migrasi dari Express.js ke CanxJS: Panduan Praktis + Perbandingan Kode
Express.js sudah jadi standar industri selama bertahun-tahun, tapi kalau kamu mengejar performa lebih tinggi dan developer experience yang lebih modern, CanxJS di atas runtime Bun layak dipertimbangkan. Artikel ini membahas perbandingan kode langsung antara Express dan CanxJS, serta panduan migrasi bertahap supaya project lama kamu tidak perlu ditulis ulang dari nol sekaligus.
Kenapa Pertimbangkan Migrasi?
Berdasarkan benchmark performa:
| Metrik | Express.js | CanxJS |
|---|---|---|
| Requests/detik | ~15.000 | 250.000+ |
| Startup time | ~200ms | < 50ms |
| Native TypeScript | Perlu setup tambahan | Native |
| Native WebSocket | Perlu library eksternal | Bawaan |
Catatan: angka di atas adalah klaim benchmark resmi CanxJS berdasarkan pengujian internal, hasil aktual bisa bervariasi tergantung use case.
Migrasi masuk akal kalau kamu:
- Butuh throughput tinggi (API dengan traffic besar)
- Ingin mengurangi jumlah dependency (banyak fitur Express butuh package tambahan:
cors,helmet,express-rate-limit, dll — semua ini sudah bawaan di CanxJS) - Sudah nyaman dengan Bun sebagai runtime
Perbandingan Kode: Setup Dasar
Express.js
const express = require("express");
const cors = require("cors");
const helmet = require("helmet");
const rateLimit = require("express-rate-limit");
const app = express();
app.use(express.json());
app.use(cors());
app.use(helmet());
app.use(rateLimit({ windowMs: 60000, max: 100 }));
app.get("/", (req, res) => {
res.json({ message: "Hello Express!" });
});
app.listen(3000, () => console.log("Server berjalan di port 3000"));
Perhatikan bahwa Express butuh 4 package berbeda hanya untuk fitur dasar: parsing JSON, CORS, security header, dan rate limiting.
CanxJS (Setara)
import { createApp, logger, cors } from "canxjs";
const app = createApp({ port: 3000 });
app.use(logger());
app.use(cors());
// Rate limiting & security header sudah aktif secara default
app.get("/", (req, res) => {
res.json({ message: "Hello CanxJS!" });
});
app.listen();
Tidak ada helmet atau express-rate-limit terpisah — keduanya sudah termasuk dalam Built-in Security CanxJS.
Perbandingan Kode: Routing dengan Parameter
Express.js
app.get("/users/:id", async (req, res) => {
const { id } = req.params;
try {
const user = await User.findById(id);
if (!user) return res.status(404).json({ message: "User tidak ditemukan" });
res.json(user);
} catch (err) {
res.status(500).json({ message: err.message });
}
});
CanxJS
app.get("/users/:id", async (req, res) => {
const { id } = req.params;
try {
const user = await User.findById(id);
if (!user) return res.status(404).json({ message: "User tidak ditemukan" });
res.json(user);
} catch (err) {
res.status(500).json({ message: err.message });
}
});
Sengaja saya tampilkan sama persis untuk menunjukkan bahwa sintaks routing CanxJS mirip Express, sehingga kurva belajarnya rendah — modal utamanya cuma paham konsep dasar Express, sisanya tinggal menyesuaikan bagian middleware dan ORM.
Perbandingan Kode: Middleware Custom
Express.js
function authMiddleware(req, res, next) {
const token = req.headers.authorization?.split(" ")[1];
if (!token) return res.status(401).json({ message: "Token tidak ditemukan" });
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
next();
} catch (err) {
return res.status(401).json({ message: "Token tidak valid" });
}
}
app.use("/protected", authMiddleware);
CanxJS
async function authMiddleware(req, res, next) {
const token = req.headers.authorization?.split(" ")[1];
if (!token) return res.status(401).json({ message: "Token tidak ditemukan" });
try {
const decoded = await verifyJWT(token, process.env.JWT_SECRET);
req.user = decoded;
await next();
} catch (err) {
return res.status(401).json({ message: "Token tidak valid" });
}
}
app.use("/protected", authMiddleware);
Perbedaan kunci: Karena CanxJS async-first, pemanggilan next() juga di-await. Ini penting supaya middleware chain berjalan sesuai urutan tanpa race condition — kalau lupa await, response bisa terkirim sebelum proses berikutnya selesai.
Langkah-Langkah Migrasi Bertahap
Daripada rewrite total, ikuti pendekatan bertahap ini:
Langkah 1: Setup CanxJS Berdampingan
Buat project CanxJS baru terpisah dari project Express lama:
bunx create-canx new-api
Langkah 2: Migrasi Model/Schema Terlebih Dahulu
Kalau project Express lama kamu pakai Sequelize/Mongoose, konversi ke model CanxJS satu per satu:
// Sebelum (Sequelize)
const User = sequelize.define("User", {
name: DataTypes.STRING,
email: DataTypes.STRING,
});
// Sesudah (CanxJS ORM)
import { Model, Column, Table } from "canxjs/orm";
@Table("users")
export class User extends Model {
@Column({ primary: true, autoIncrement: true })
id!: number;
@Column({ type: "string", required: true })
name!: string;
@Column({ type: "string", required: true, unique: true })
email!: string;
}
Langkah 3: Migrasi Route per Modul
Pindahkan route satu modul dulu (misal modul auth), test menyeluruh, baru lanjut modul berikutnya. Jangan migrasi semua endpoint sekaligus — ini mengurangi risiko downtime produksi.
Langkah 4: Jalankan Kedua Server Sementara (Strangler Pattern)
Gunakan reverse proxy (Nginx) untuk mengarahkan sebagian traffic ke CanxJS (modul yang sudah dimigrasi) dan sisanya tetap ke Express (modul yang belum), sampai migrasi 100% selesai:
location /api/v2/ {
proxy_pass http://localhost:3000; # CanxJS
}
location /api/ {
proxy_pass http://localhost:4000; # Express lama
}
Langkah 5: Matikan Server Express Setelah Semua Modul Beres
Setelah semua endpoint sudah pindah dan lolos testing, baru matikan server Express lama sepenuhnya.
Troubleshooting Saat Migrasi
1. Cannot find module 'canxjs/orm'
Penyebab: Dependency belum lengkap terinstall, atau versi CanxJS yang dipakai belum menyertakan module ORM.
Solusi:
bun add canxjs@latest
bun install
2. Middleware Express lama tidak jalan di CanxJS
Penyebab: Middleware Express klasik ((req, res, next) => {}) umumnya sinkron, sedangkan CanxJS mengharapkan pola async (async (req, res, next) => {}).
Solusi: Bungkus middleware lama dengan wrapper async:
function toAsyncMiddleware(fn) {
return async (req, res, next) => {
fn(req, res, next);
};
}
app.use(toAsyncMiddleware(oldExpressMiddleware));
3. Environment variable tidak terbaca setelah migrasi
Penyebab: Bun memiliki cara load .env yang sedikit berbeda dari Node.js.
Solusi: Pastikan file .env ada di root project, dan Bun otomatis membacanya — tidak perlu install dotenv secara manual di kebanyakan kasus. Kalau tetap tidak terbaca, verifikasi dengan:
bun run -e "console.log(process.env.DB_HOST)"
4. Query database lama (raw SQL) tidak kompatibel dengan ORM baru
Penyebab: ORM CanxJS punya konvensi penamaan dan struktur query builder sendiri.
Solusi: CanxJS biasanya tetap menyediakan raw query fallback untuk kasus kompleks:
import { db } from "canxjs/orm";
const results = await db.raw("SELECT * FROM users WHERE age > ?", [18]);
Gunakan ini sebagai jembatan sementara sambil pelan-pelan mengonversi ke sintaks ORM native.
Kesimpulan
Migrasi dari Express.js ke CanxJS tidak harus dilakukan sekaligus. Dengan pendekatan bertahap — mulai dari model, lalu route per modul, dan strangler pattern lewat reverse proxy — kamu bisa memindahkan project produksi tanpa downtime signifikan, sambil menikmati peningkatan performa dan fitur bawaan yang lebih lengkap.
Kalau kamu sedang proses migrasi dan menemui error yang belum dibahas di sini, tulis di kolom komentar — saya bantu cek!