A Modern, Fullstack Node.js & TypeScript Framework
Built on raw node:http - zero overhead, decorator-based, blazing fast.
Zentify adalah framework fullstack berbasis TypeScript yang dirancang untuk memberikan Developer Experience (DX) kelas satu - seperti Laravel atau NestJS - namun tetap sangat ringan dan cepat karena dibangun langsung di atas modul node:http bawaan Node.js.
Framework ini menggabungkan dua dunia dalam satu server:
- Backend: Decorator-based routing, Dependency Injection, Controllers, Services, dan Middleware Pipeline
- Frontend: Integrasi Vite dalam middleware mode untuk Hot Module Replacement (HMR) dan React SPA - semuanya dari satu port, tanpa menjalankan dua proses terpisah.
Zentify bukan wrapper di atas Express. Ia membangun HTTP server-nya sendiri, sehingga tidak ada overhead dari library routing eksternal yang tidak perlu.
| Prinsip | Penjelasan |
|---|---|
| Convention over Configuration | Struktur direktori dan pola penamaan yang konsisten. Tidak perlu konfigurasi boilerplate yang rumit. |
| Clean Architecture | Mendukung pola arsitektur bersih via Decorator-based routing, Dependency Injection (DI), serta pemisahan Controllers dan Services. |
| Modular & Agnostic | Inti framework (@zentify/core) dibuat murni. Integrasi React/Vite dipisah melalui Adapter Pattern (@zentify/vite, @zentify/react). |
| Fullstack in One Server | Me-render halaman React dari Backend semudah memanggil return render("Page", { props }). Tidak ada double server, tidak ada CORS antar port. |
| Performa Tinggi | Dibangun di atas node:http murni dengan find-my-way sebagai router (router yang sama dengan Fastify), sehingga overhead sangat minimal. |
Zentify adalah monorepo dengan beberapa package yang saling melengkapi:
| Package | Deskripsi |
|---|---|
@zentify/core |
Inti framework: HTTP server, routing, decorators, DI container, CLI |
@zentify/vite |
Adapter Vite - mengintegrasikan Vite dev server dalam middleware mode |
@zentify/react |
Client runtime - SPA navigation (navigate, <Link>), app mounting |
- Node.js >= 22
- npm >= 10
npx @zentify/core new my-appCLI akan menampilkan prompt interaktif untuk memilih tipe project:
? Select project type:
β― API Only - A lightweight Zentify application for REST APIs
Fullstack (React) - Zentify backend with React + Vite frontend
cd my-app
npm install
npm run devServer akan berjalan di http://localhost:3000 dengan auto-reload saat file berubah.
my-app/
βββ app/
β βββ Controllers/ # Endpoint & routing handler
β β βββ UserController.ts
β βββ Services/ # Logika bisnis & injectable dependencies
β β βββ UserService.ts
β βββ Modules/ # Grouping controller + provider
β β βββ UserModule.ts
β βββ Routes/ # Registrasi modul ke router global
β β βββ web.ts
β βββ index.ts # Entry point aplikasi
βββ tsconfig.json
βββ package.json
my-app/
βββ app/
β βββ Controllers/
β βββ Services/
β βββ Modules/
β βββ Routes/
β βββ Views/
β β βββ Pages/ # Komponen React per halaman
β β β βββ Index.tsx
β β β βββ About.tsx
β β βββ index.css
β β βββ main.tsx # Client entry point (Vite)
β βββ index.ts
βββ vite.config.ts
βββ tsconfig.json
βββ package.json
Setelah instalasi, semua perintah dijalankan via zentify:
| Command | Deskripsi |
|---|---|
zentify new <name> |
Buat project baru (interaktif: API Only / Fullstack) |
zentify dev |
Start server development dengan TypeScript watch + Node.js watch |
zentify build |
Compile TypeScript ke JavaScript (production) |
zentify start |
Jalankan server production dari hasil build |
zentify make:controller <name> |
Generate file Controller baru |
zentify make:service <name> |
Generate file Service baru |
zentify make:module <name> |
Generate file Module baru |
Mode standalone menghasilkan folder deployment yang self-contained: seluruh kode backend, node_modules yang benar-benar dibutuhkan (hasil file tracing), serta aset frontend β siap disalin ke server mana pun dan dijalankan tanpa npm install.
Tambahkan "standalone": true di zentify.json:
{
"entry": "app/index.ts",
"outDir": "dist",
"standalone": true
}Atau lewati config dan jalankan langsung:
zentify build --standaloneOutput berada di dalam folder build, seperti .next/standalone di Next.js:
dist/standalone/
βββ server.js # entry point: jalankan `node server.js`
βββ package.json # { "type": "module", "private": true }
βββ zentify.json # agar resolveOutDir() = "dist"
βββ node_modules/ # hanya paket yang ter-trace (react, typeorm, pg, @zentify/*, dst.)
βββ dist/
βββ app/ # hasil kompilasi backend (tsc)
βββ public/ # aset frontend (Vite client build)
βββ server/ # bundle SSR (Vite server build)
# build
npm run build
# salin folder standalone ke server (env di-set dari luar, .env TIDAK ikut tersalin)
cp -r dist/standalone /opt/my-app
cd /opt/my-app
# jalankan
node server.js
server.jsotomatis men-setNODE_ENV=productiondan men-selaraskan seluruh resolusi path ke dalam folder standalone.
- Dependency ditelusuri dari entry backend + bundle SSR menggunakan file tracing (
@vercel/nft), ditambah safety net: semua dependency eksplisit dipackage.jsonikut disalin. - Paket dev-only (
vite,@vitejs/*,esbuild,@swc/*,rollup,tsx,@inquirer/*) tidak ikut, karena hanya dibutuhkan saat development/build. .envsengaja tidak disalin β environment variable disediakan oleh platform deploy.
import { Zentify } from "@zentify/core";
import "./Routes/web.js";
const app = new Zentify({
server: { port: 3000, host: "0.0.0.0" },
});
app.run();import { Route } from "@zentify/core";
import { UserModule } from "../Modules/UserModule.js";
Route.module(UserModule);Module adalah unit pengelompokan antara Controllers dan Services (providers). Semua dependency injection di-resolve secara otomatis di dalam scope module.
import { Module } from "@zentify/core";
import { UserController } from "../Controllers/UserController.js";
import { UserService } from "../Services/UserService.js";
@Module({
controllers: [UserController],
providers: [UserService],
})
export class UserModule {}import {
Controller,
Get,
Post,
Put,
Delete,
Body,
Param,
Query,
} from "@zentify/core";
import { UserService } from "../Services/UserService.js";
import { CreateUserDTO } from "./dto/CreateUserDTO.js";
@Controller({ path: "/users" })
export class UserController {
constructor(
private readonly userService: UserService, // Di-inject otomatis
) {}
@Get("/")
async index(@Query() query: any) {
return this.userService.findAll(query);
}
@Get("/:id")
async show(@Param("id") id: string) {
return this.userService.findById(id);
}
@Post("/")
async create(@Body() body: CreateUserDTO) {
return this.userService.create(body);
}
@Put("/:id")
async update(@Param("id") id: string, @Body() body: CreateUserDTO) {
return this.userService.update(id, body);
}
@Delete("/:id")
async destroy(@Param("id") id: string) {
return this.userService.delete(id);
}
}Service adalah tempat logika bisnis. Tandai dengan @Dependency() agar bisa di-inject ke Controller atau Service lain.
import { Dependency, NotFoundException } from "@zentify/core";
@Dependency()
export class UserService {
private users = [{ id: "1", name: "Raja" }];
async findAll(query: any) {
return this.users;
}
async findById(id: string) {
const user = this.users.find((u) => u.id === id);
if (!user) throw new NotFoundException(`User with id ${id} not found`);
return user;
}
async create(data: any) {
const user = { id: Date.now().toString(), ...data };
this.users.push(user);
return user;
}
async update(id: string, data: any) {
const index = this.users.findIndex((u) => u.id === id);
if (index === -1)
throw new NotFoundException(`User with id ${id} not found`);
this.users[index] = { ...this.users[index], ...data };
return this.users[index];
}
async delete(id: string) {
const index = this.users.findIndex((u) => u.id === id);
if (index === -1)
throw new NotFoundException(`User with id ${id} not found`);
this.users.splice(index, 1);
return { deleted: true };
}
}Untuk endpoint sederhana, bisa langsung daftar route tanpa membuat Controller:
import { Route } from "@zentify/core";
// GET /health
Route.get("/health", async (req, res) => {
res.json({ status: "ok" });
});
// Route group dengan prefix
Route.group("/api/v1", () => {
Route.get("/ping", async (req, res) => {
res.json({ pong: true });
});
});| Decorator | Deskripsi | Contoh |
|---|---|---|
@Body() |
Parse & validasi request body via DTO schema | @Body() body: CreateUserDTO |
@Param(key) |
Ambil route parameter | @Param("id") id: string |
@Query() |
Ambil query string sebagai object | @Query() query: any |
@Req() |
Raw request object (ZRequest) |
@Req() req: ZRequest |
@Res() |
Raw response object (ZResponse) |
@Res() res: ZResponse |
Zentify mengintegrasikan Valibot untuk validasi body request secara otomatis. Jika validasi gagal, framework otomatis mengembalikan response 422 Unprocessable Entity.
import * as v from "valibot";
export class CreateUserDTO {
static schema = v.object({
name: v.pipe(v.string(), v.minLength(3)),
email: v.pipe(v.string(), v.email()),
age: v.optional(v.number()),
});
name!: string;
email!: string;
age?: number;
}@Post("/")
async create(@Body() body: CreateUserDTO) {
// body sudah tervalidasi sebelum sampai sini
return this.userService.create(body);
}Response ketika validasi gagal:
{
"message": "Invalid request body",
"details": [...]
}Zentify menyediakan kelas exception bawaan yang otomatis dikonversi ke response JSON:
import {
HttpException,
BadRequestException, // 400
UnauthorizedException, // 401
ForbiddenException, // 403
NotFoundException, // 404
InternalServerErrorException, // 500
} from "@zentify/core";
@Get("/:id")
async show(@Param("id") id: string) {
const user = await this.userService.findById(id);
if (!user) throw new NotFoundException("User not found");
return user;
}
// Custom exception dengan status code bebas
throw new HttpException({ message: "Too many requests", statusCode: 429 });Response otomatis:
{
"message": "User not found"
}import { Zentify, Middleware } from "@zentify/core";
class LoggerMiddleware implements Middleware {
async handle(req, res, next) {
console.log(`[${req.method}] ${req.url}`);
await next();
}
}
const app = new Zentify({ server: { port: 3000 } });
app.addMiddleware(new LoggerMiddleware());@Get("/admin", [new AuthMiddleware()])
async adminDashboard() {
return { secret: true };
}@Module({
controllers: [AdminController],
providers: [AdminService],
middleware: [
{
middlewares: [new AuthMiddleware()],
includeRoutes: [{ method: "REQ_METHOD_ALL", path: "/admin/*" }],
},
],
})
export class AdminModule {}Untuk project fullstack, tambahkan Vite Adapter ke entry point:
import { Zentify } from "@zentify/core";
import { ZentifyViteAdapter } from "@zentify/vite";
import "./Routes/web.js";
const app = new Zentify({ server: { port: 3000 } });
app.addAdapter(
new ZentifyViteAdapter({
entry: "app/Views/main.tsx",
}),
);
app.useStatic("dist/public");
app.run();Render halaman React dari Controller:
import { render } from "@zentify/core";
@Get("/")
async index() {
return render("Index", { title: "Zentify", user: "Raja" });
}Client entry (app/Views/main.tsx):
import { createZentifyApp } from "@zentify/react";
import { createRoot } from "react-dom/client";
const pages = (import.meta as any).glob("./Pages/**/*.tsx", { eager: true });
createZentifyApp({
resolve: (name) => pages[`./Pages/${name}.tsx`],
setup({ el, App, props }) {
const root = createRoot(el);
root.render(<App {...props} />);
},
});SPA Navigation dengan <Link>:
import { Link } from "@zentify/react";
export default function Index({
title,
user,
}: {
title: string;
user: string;
}) {
return (
<div>
<h1>{title}</h1>
<p>Hello, {user}!</p>
<Link href="/about">Tentang Kami</Link>
</div>
);
}
<Link>melakukan client-side navigation viaX-Zentify-Bridgeheader - server hanya membalas JSON, bukan full HTML reload.
Pastikan tsconfig.json mengaktifkan decorator metadata:
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"strict": true,
"outDir": "./dist"
}
}Benchmark dijalankan pada hardware nyata untuk skenario REST API murni - tanpa database, tanpa middleware tambahan, tanpa autentikasi - menggunakan autocannon.
| Spesifikasi | Detail |
|---|---|
| Node.js | v22.22.0 |
| OS | Windows 10 (build 26200) |
| CPU | AMD Ryzen 5 7520U (8 cores) |
| RAM | 13.83 GB |
| Durasi benchmark | 10 detik per skenario |
| Warmup | 5 detik |
| Tool | autocannon (keep-alive enabled) |
| Zentify version | 1.0.0 |
| Fastify version | 5.11.3 |
Fastify dipilih sebagai pembanding utama karena merupakan salah satu framework Node.js tercepat. Kolom RPS adalah nilai median dari seluruh run.
| Connections | Zentify RPS | Fastify RPS | Selisih |
|---|---|---|---|
| 1 | 5,742 | 3,965 | +44.8% β |
| 10 | 10,335 | 8,552 | +20.8% β |
| 100 | 10,981 | 6,213 | +76.7% β |
| Connections | Zentify RPS | Fastify RPS | Selisih |
|---|---|---|---|
| 1 | 6,078 | 5,061 | +20.1% β |
| 10 | 12,962 | 7,113 | +82.2% β |
| 100 | 11,023 | 6,797 | +62.2% β |
| Connections | Zentify RPS | Fastify RPS | Selisih |
|---|---|---|---|
| 1 | 6,683 | 3,271 | +104.3% β |
| 10 | 11,107 | 7,970 | +39.4% β |
| 100 | 8,048 | 7,917 | ~Setara |
| Connections | Zentify RPS | Fastify RPS | Selisih |
|---|---|---|---|
| 1 | 7,739 | 5,853 | +32.2% β |
| 10 | 12,064 | 11,409 | +5.7% β |
| 100 | 10,868 | 8,637 | +25.8% β |
| Connections | Zentify RPS | Fastify RPS | Selisih |
|---|---|---|---|
| 1 | 3,469 | 2,180 | +59.1% β |
| 10 | 6,405 | 5,126 | +24.9% β |
| 100 | 5,252 | 3,414 | +53.8% β |
- Zero Overhead HTTP Layer - Tidak ada middleware pipeline dari library eksternal. Request langsung masuk ke handler.
find-my-wayRouter - Router berbasis Radix tree yang sama dengan Fastify, matching O(1).- Route Table Pre-compilation - Semua route di-resolve saat inisialisasi, bukan per-request.
- No Unnecessary Abstraction - Tidak ada layer Express-style wrapping yang membengkakkan overhead.
- Vite Middleware Mode - Beban kompilasi Frontend diserahkan ke proses Vite terpisah, tidak memblok event loop utama.
β οΈ Catatan: Hasil benchmark ini spesifik pada hardware, versi Node.js, dan kondisi pengujian yang tercantum. Hasil aktual dapat bervariasi tergantung lingkungan deployment.
- Authentication middleware bawaan (JWT, Session)
- WebSocket support
- Database adapter (
@zentify/prisma,@zentify/mongoose) - Testing utilities (
@zentify/testing) - Vue.js adapter (
@zentify/vue) - CLI:
zentify make:dto <name> - OpenAPI / Swagger documentation generator
ISC License.