Back to Blog
Go Language

๐ŸŽฌ Golang Ticket Booking System from Scratch โ€“ No Framework (Part 4)

24 Juni 20253 min read

๐ŸŒ Booking Controller & HTTP Handler (main.go)


Setelah kita berhasil membangun logic utama BookingService dan mengamankan sistem dari race condition di Part 3, sekarang saatnya membuat sistem ini bisa diakses melalui HTTP API. Di part ini, kita akan membahas:

* Pembuatan Booking Controller untuk menerima HTTP request

* Penyusunan struct JSON untuk request dan response

* Konfigurasi HTTP server menggunakan httprouter

* Contoh testing via Postman atau Curl


๐Ÿ”ง Struktur Proyek Saat Ini

go
ticket-booking/
โ”œโ”€โ”€ app/
โ”‚   โ””โ”€โ”€ database.go               # Koneksi database
โ”œโ”€โ”€ controller/
โ”‚   โ””โ”€โ”€ booking_controller.go     # HTTP handler untuk booking
โ”œโ”€โ”€ main.go                       # Entry point aplikasi
โ”œโ”€โ”€ repository/
โ”‚   โ””โ”€โ”€ booking_repository.go     # Akses query ke DB
โ”œโ”€โ”€ request/
โ”‚   โ””โ”€โ”€ booking_request.go        # Struct input request
โ”œโ”€โ”€ response/
โ”‚   โ””โ”€โ”€ api_response.go           # Struct response standar
โ”œโ”€โ”€ service/
โ”‚   โ””โ”€โ”€ booking_service.go        # Logika pemesanan kursi
โ”œโ”€โ”€ go.mod

1. Membuat Booking Controller

๐Ÿ“„ File: controller/booking_controller.go

go
package controller

import (
	"context"
	"encoding/json"
	"net/http"

	"github.com/fardannozami/ticket-booking/helper"
	"github.com/fardannozami/ticket-booking/request"
	"github.com/fardannozami/ticket-booking/response"
	"github.com/fardannozami/ticket-booking/service"
	"github.com/julienschmidt/httprouter"
)

type BookingController interface {
	BookSeat(writer http.ResponseWriter, req *http.Request, params httprouter.Params)
}

type bookingController struct {
	bookingService service.BookingService
}

func NewBookingController(bookingService service.BookingService) BookingController {
	return &bookingController{
		bookingService: bookingService,
	}
}

func (c *bookingController) BookSeat(writer http.ResponseWriter, req *http.Request, params httprouter.Params) {
	var bookCreateRequest request.BookingCreateRequest
	decoder := json.NewDecoder(req.Body)
	err := decoder.Decode(&bookCreateRequest)
	helper.PanicIfError(err)

	_, err = c.bookingService.BookSeat(context.Background(), bookCreateRequest.EventId, bookCreateRequest.SeatId, bookCreateRequest.UserId)
	helper.PanicIfError(err)

	apiResponse := response.ApiResponse{
		Code:    http.StatusCreated,
		Message: "created",
	}

	writer.Header().Set("Content-Type", "application/json")
	err = json.NewEncoder(writer).Encode(&apiResponse)
	helper.PanicIfError(err)

}

2. Membuat Struct Request dan Response

๐Ÿ“„ File: request/booking_request.go

go
package request

type BookingCreateRequest struct {
	EventId int `json:"event_id"`
	SeatId  int `json:"seat_id"`
	UserId  int `json:"user_id"`
}

๐Ÿ“„ File: response/api_response.go

go
package response

type ApiResponse struct {
	Code    int         `json:"code"`
	Message string      `json:"message"`
	Data    interface{} `json:"data"`
}

3. Setup Koneksi Database

๐Ÿ“„ File: app/database.go

go
package app

import (
	"database/sql"
	"github.com/fardannozami/ticket-booking/helper"
)

func NewSqlDb() *sql.DB {
	db, err := sql.Open("mysql", "root@tcp(127.0.0.1:3306)/ticket-booking")
	helper.PanicIfError(err)
	return db
}

4. Tambahkan Exception Handler

๐Ÿ“„ File: exception/error_handler.go

go
package exception

import (
	"encoding/json"
	"net/http"

	"github.com/fardannozami/ticket-booking/helper"
	"github.com/fardannozami/ticket-booking/response"
)

func ErrorHandler(w http.ResponseWriter, r *http.Request, err interface{}) {
	internalServerError(w, r, err)
}

func internalServerError(w http.ResponseWriter, r *http.Request, err interface{}) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusInternalServerError)

	var message string
	switch e := err.(type) {
	case string:
		message = e
	case error:
		message = e.Error()
	default:
		message = "Unknown error"
	}

	apiResponse := response.ApiResponse{
		Code:    http.StatusInternalServerError,
		Message: "Internal Server Error",
		Data:    message,
	}

	encode := json.NewEncoder(w)
	error := encode.Encode(&apiResponse)
	helper.PanicIfError(error)
}

5. Menjalankan HTTP Server

๐Ÿ“„ File: main.go

go
package main

import (
	"net/http"

	"github.com/fardannozami/ticket-booking/app"
	"github.com/fardannozami/ticket-booking/controller"
	"github.com/fardannozami/ticket-booking/exception"
	"github.com/fardannozami/ticket-booking/helper"
	"github.com/fardannozami/ticket-booking/repository"
	"github.com/fardannozami/ticket-booking/service"
	_ "github.com/go-sql-driver/mysql"
	"github.com/julienschmidt/httprouter"
)

func main() {
	db := app.NewSqlDb()

	bookingRepo := repository.NewBookingRepository()
	bookingService := service.NewBookingService(db, bookingRepo)
	bookingController := controller.NewBookingController(bookingService)
	router := httprouter.New()

	router.POST("/api/bookseat", bookingController.BookSeat)

	router.PanicHandler = exception.ErrorHandler

	server := http.Server{
		Addr:    ":3000",
		Handler: router,
	}

	err := server.ListenAndServe()
	helper.PanicIfError(err)
}

๐Ÿงช Contoh Testing via Curl atau Postman

๐Ÿ”ธ Request (Booking Kursi)

bash
curl -X POST http://localhost:3000/api/bookseat \
  -H "Content-Type: application/json" \
  -d '{
    "event_id": 1,
    "seat_id": 2,
    "user_id": 12
  }'

โœ… Response Berhasil

json
{
  "code": 201,
  "message": "created",
  "data": null
}

โŒ Response Gagal (kursi sudah dibooking)

json
{
  "code": 409,
  "message": "seat already booked",
  "data": null
}

> ๐Ÿ“Œ Pastikan data event_id dan seat_id valid dan tersedia di database untuk testing ini.


๐Ÿ“Œ Summary

FiturStatus
BookingServiceโœ…
Race Condition Handlingโœ…
JSON Request & Responseโœ…
HTTP Routing (httprouter)โœ…
Endpoint Booking Seatโœ…

๐Ÿ“˜ Tips Tambahan

Jika kamu ingin meningkatkan proyek ini lebih jauh, pertimbangkan untuk:

* Menambahkan validasi input menggunakan go-playground/validator

* Menyimpan konfigurasi ke .env dan memuatnya menggunakan joho/godotenv

* Menambahkan middleware untuk logging atau recovery

* Menyusun error handling lebih rapi agar tidak semua pakai PanicIfError