Lewati ke konten utama
menjadi.dev
Chapter 17.7 Database Menengah

Hono: Router, Grouping, dan Error Handling

Struktur aplikasi backend yang terorganisir dan scalable

Tujuan Pembelajaran

  • Bisa menggunakan Hono router
  • Mengerti route grouping dan modularisasi
  • Menguasai error handling dan custom responses

Analogi

Diagram

      App Structure:
src/
├── index.ts      # Entry point + middleware global
├── routes/
│   ├── users.ts  # User routes
│   └── posts.ts  # Post routes
├── middleware/
│   ├── auth.ts   # Auth middleware
│   └── validate.ts
└── types.ts      # Shared types
    

Modularisasi route membuat aplikasi mudah dikelola saat bertumbuh

Penjelasan Konsep

Seiring aplikasi bertumbuh, meletakkan semua route di satu file menjadi tidak praktis.

Hono menyediakan mekanisme untuk memecah aplikasi menjadi modul-modul yang terorganisir.

Hono Router

Hono punya sistem router yang powerful.

Selain route biasa, Hono support:

  • Path parameters: /users/:id
  • Wildcard: /users/*
  • Regexp: /version/(\d+)
  • Multiple methods: app.on(‘PURGE’, ‘/cache’, handler)
  • Priority: Static routes dicek dulu, baru parameterized

Route Grouping

Kamu bisa membuat sub-router dan mount ke app utama:

const userRoutes = new Hono();
userRoutes.get('/',

GetAllUsers);
userRoutes.get('/:id', getUser);
userRoutes.post('/', createUser);

app.route('/users', userRoutes);
// Hasil: GET /users, GET /users/:id, POST /users

Base Path

Alternatif lain: gunakan basePath saat membuat Hono instance:

const app = new Hono().basePath('/api');
// Sekarang semua route diawali /api

Error Handling

Hono punya beberapa cara handle error:

  1. app.onError(): Global error handler untuk semua route
  2. try-catch di handler: Handle error per-route
  3. HTTPException: Throw error dengan status code spesifik
import { HTTPException } from 'hono/http-exception';

app.get('/users/:id', (c) => {
  const id = c.req.param('id');
  const user = db.findUser(id);
  if (!user) {
    throw new HTTPException(404, { message: 'User not found' });
  }
  return c.json(user);
});

app.onError((err, c) => {
  if (err instanceof HTTPException) {
    return c.json({ error: err.message }, err.status);
  }
  return c.json({ error: 'Internal error' },

500);
});

Custom Response Helpers

Buat helper untuk response konsisten:

// utils/response.ts
export function success<T>(c: Context, data: T, status = 200) {
  return c.json({ success: true, data }, status);
}

export function error(c: Context, message: string, status = 400) {
  return c.json({ success: false, error: message }, status);
}

// Penggunaan
app.get('/users', (c) => {
  return success(c, users);
});

Contoh Kode

typescript
// Modular Hono app
import { Hono } from 'hono';
import { logger } from 'hono/logger';
import { HTTPException } from 'hono/http-exception';

// Types
interface User {
  id: string;
  name: string;
  email: string;
}

// Mock database
const users: User[] = [
  { id: '1', name: 'Budi', email: 'budi@mail.com' },
  { id: '2', name: 'Ani', email: 'ani@mail.com' },
];

// Response helpers
const jsonOk = (c: any, data: any) => c.json({ success: true, data });
const jsonErr = (c: any, message: string, status = 400) =>
  c.json({ success: false, error: message }, status);

// User routes
const usersRoute = new Hono();

usersRoute.get('/', (c) => jsonOk(c, users));

usersRoute.get('/:id', (c) => {
  const id = c.req.param('id');
  const user = users.find(u => u.id === id);
  if (!user) throw new HTTPException(404, { message: 'User not found' });
  return jsonOk(c, user);
});

usersRoute.post('/', async (c) => {
  const body = await c.req.json<User>();
  const newUser = { id: String(users.length + 1), ...body };
  users.push(newUser);
  return jsonOk(c, newUser, 201);
});

usersRoute.put('/:id', async (c) => {
  const id = c.req.param('id');
  const body = await c.req.json<Partial<User>>();
  const idx = users.findIndex(u => u.id === id);
  if (idx === -1) throw new HTTPException(404, { message: 'User not found' });
  users[idx] = { ...users[idx], ...body };
  return jsonOk(c, users[idx]);
});

usersRoute.delete('/:id', (c) => {
  const id = c.req.param('id');
  const idx = users.findIndex(u => u.id === id);
  if (idx === -1) throw new HTTPException(404, { message: 'User not found' });
  users.splice(idx, 1);
  return c.body(null, 204);
});

// Main app
const app = new Hono();
app.use(logger());
app.route('/api/users', usersRoute);

app.onError((err, c) => {
  if (err instanceof HTTPException) {
    return jsonErr(c, err.message, err.status);
  }
  console.error(err);
  return jsonErr(c, 'Internal Server Error', 500);
});

export default app;

Penjelasan Kode

Route grouping dengan app.route() memisahkan concern. HTTPException untuk error dengan status code. Response helpers untuk konsistensi API response.

Prompt AI

Pecah aplikasimu menjadi beberapa route module: users.ts, posts.ts, comments.ts. Tambahkan auth middleware untuk routes yang protected.

Pertanyaan Reflektif

Refactor aplikasimu menggunakan pattern MVC atau layered architecture. Pisahkan controller logic dari route definitions.