Episode terakhir seri ini menyatukan semuanya: membangun API CRUD lengkap dengan Fiber v3 — model dan store, routing group, binding dan validasi, custom error handler, middleware, serta uji dengan curl. Semua materi episode 0-21 dirangkai dalam satu proyek.

Semua episode sebelumnya membahas potongan-potongan. Episode 22 — penutup seri ini — merangkainya menjadi proyek CRUD lengkap: aplikasi manajemen buku dengan Fiber v3 yang memakai routing, group, middleware, binding, validasi, dan error handling sekaligus.
Pisahkan tanggung jawab ke file kecil agar mudah diuji:
main.go — perakitan aplikasi dan routebook.go — model beserta tag validasistore.go — penyimpanan data in-memoryhandler.go — handler CRUD dan error handlermain.go menggabungkan konfigurasi, middleware, dan route:
func main() {
app := fiber.New(fiber.Config{
ErrorHandler: customErrorHandler,
BodyLimit: 1 * 1024 * 1024,
})
app.Use(logger.New(), recover.New())
books := app.Group("/books")
books.Get("/", listBooks)
books.Get("/:id<int>", getBook)
books.Post("/", createBook)
books.Put("/:id<int>", updateBook)
books.Delete("/:id<int>", deleteBook)
app.Listen(":3000")
}ErrorHandler kustom (episode 9), middleware logger/recover (episode 6), dan constraint :id<int> (episode 4) dipasang sekaligus.
type Book struct {
ID int `json:"id"`
Title string `json:"title" validate:"required,min=1"`
Author string `json:"author" validate:"required"`
Year int `json:"year" validate:"gte=1900,lte=2100"`
}Tag json untuk binding (episode 5); tag validate memastikan Title/Author wajib dan Year masuk rentang yang wajar.
type Store struct {
mu sync.RWMutex
items map[int]Book
next int
}
func (s *Store) Create(b Book) Book {
s.mu.Lock()
defer s.mu.Unlock()
s.next++
b.ID = s.next
s.items[b.ID] = b
return b
}
func (s *Store) Get(id int) (Book, bool) {
s.mu.RLock()
defer s.mu.RUnlock()
b, ok := s.items[id]
return b, ok
}sync.RWMutex melindungi map dari akses bersamaan karena handler Go berjalan paralel.
Handler memakai binding dan validasi otomatis:
var store = &Store{items: map[int]Book{}}
func createBook(c fiber.Ctx) error {
var b Book
if err := c.Bind().JSON(&b); err != nil {
return err
}
return c.Status(fiber.StatusCreated).JSON(store.Create(b))
}
func listBooks(c fiber.Ctx) error {
return c.JSON(store.All())
}c.Bind().JSON(&b) mem-parse body dan menjalankan validasi; error diteruskan ke ErrorHandler.
func getBook(c fiber.Ctx) error {
id, _ := strconv.Atoi(c.Params("id"))
b, ok := store.Get(id)
if !ok {
return fiber.NewError(fiber.StatusNotFound, "buku tidak ditemukan")
}
return c.JSON(b)
}
func updateBook(c fiber.Ctx) error {
id, _ := strconv.Atoi(c.Params("id"))
var b Book
if err := c.Bind().JSON(&b); err != nil {
return err
}
updated, ok := store.Update(id, b)
if !ok {
return fiber.NewError(fiber.StatusNotFound, "buku tidak ditemukan")
}
return c.JSON(updated)
}
func deleteBook(c fiber.Ctx) error {
id, _ := strconv.Atoi(c.Params("id"))
if err := store.Delete(id); err != nil {
return fiber.NewError(fiber.StatusNotFound, "buku tidak ditemukan")
}
return c.SendStatus(fiber.StatusNoContent)
}getBook mengembalikan 404 lewat fiber.NewError saat id tidak ada; deleteBook membalas 204 setelah berhasil menghapus.
func customErrorHandler(c fiber.Ctx, err error) error {
code := fiber.StatusInternalServerError
var fe *fiber.Error
if errors.As(err, &fe) {
code = fe.Code
}
return c.Status(code).JSON(fiber.Map{
"error": fiber.Map{
"code": code,
"message": err.Error(),
},
})
}curl -X POST -H "Content-Type: application/json" \
--data '{"title":"Clean Code","author":"Robert Martin","year":2008}' http://localhost:3000/books
curl http://localhost:3000/books
curl http://localhost:3000/books/1
curl -X PUT -H "Content-Type: application/json" --data '{"title":"Clean Code","author":"Robert C. Martin","year":2008}' http://localhost:3000/books/1
curl -X DELETE http://localhost:3000/books/1
curl http://localhost:3000/books/999Selamat — kalian telah menyelesaikan seri Belajar Fiber! Inti yang harus dibawa pulang:
:id<int>) menjaga route tetap terorganisasi.fiber.NewError + custom ErrorHandler menyatukan format error.bun run dev dan uji dengan curl.