Advanced Go API Development

Panduan lengkap untuk membangun API yang aman dan teroptimasi menggunakan Gin Framework dan GORM. Mencakup contoh kode siap produksi untuk autentikasi, integrasi database, dan pengujian dengan praktik terbaik untuk aplikasi Go yang dapat dipelihara.

Pengenalan Pengembangan API Go

Kekuatan Go untuk Backend

Performa tinggi, sintaks sederhana, dan dukungan concurrency bawaan membuat Go ideal untuk layanan backend modern.

Stack Pengembangan Modern

Kombinasi Gin framework, GORM ORM, dan tools testing untuk pengembangan API yang efisien dan maintainable.

API Aman dengan Persistensi

Membangun REST API dengan autentikasi, otorisasi, dan integrasi database yang aman serta terstruktur.

Memulai dengan Gin Framework

Performa Tinggi

Gin memberikan performa 40x lebih cepat dibanding net/http standar dengan overhead minimal dan routing yang efisien.

Fitur Lengkap

Routing sederhana, dukungan middleware, validasi JSON otomatis, dan binding request yang mudah digunakan.

Instalasi Mudah

Instalasi dengan perintah: go get -u github.com/gin-gonic/gin dan siap digunakan langsung.

Struktur Dasar API dengan Gin

Mengapa Gin

Gin menawarkan keseimbangan sempurna antara performa dan kemudahan penggunaan.

  • Ekosistem middleware yang matang
  • Dokumentasi lengkap dan komunitas besar
  • Kompatibilitas tinggi dengan pustaka Go

Fiber vs Gin

Meski Fiber lebih cepat, Gin lebih stabil untuk produksi.

  • Gin: Arsitektur proven, debugging mudah
  • Fiber: Memory overhead tinggi saat scale
  • Gin: Built-in testing tools superior

Struktur Proyek

Organisasi kode yang baik memudahkan maintenance dan scaling.

  • Controllers untuk logic routing
  • Models untuk struktur data
  • Middleware untuk cross-cutting concerns

Struktur Dasar API dengan Gin

Routing dan HTTP Methods

Membuat routes untuk GET, POST, PUT, DELETE dengan handler yang sesuai untuk setiap endpoint.

Parameter Handling

Mengelola path parameters, query strings, dan request body dengan binding otomatis ke struct.

Response Management

Strukturisasi data response dengan format JSON yang konsisten dan error handling yang proper.

Setup Proyek Go API

Struktur Direktori

Organisasi file untuk maintainability dengan separation of concerns yang jelas antara controllers, services, dan repositories.

Module Management

Inisialisasi go module dan pengelolaan dependencies menggunakan go.mod untuk reproducible builds.

Configuration

Setup environment variables dan configuration management untuk berbagai environment (dev, staging, production).

Membuat API Gin Pertama

Struktur Sederhana

Semua kode dalam satu file main.go. Cocok untuk prototyping dan aplikasi kecil.

  • main.go - semua logic
  • go.mod - dependencies
  • README.md - dokumentasi

Struktur MVC

Pemisahan controller, model, dan view. Ideal untuk aplikasi medium dengan tim kecil.

  • controllers/ - routing logic
  • models/ - data structures
  • routes/ - endpoint definitions

Clean Architecture

Arsitektur berlapis dengan dependency injection. Recommended untuk aplikasi enterprise dan tim besar.

  • domain/ - business logic
  • infrastructure/ - external services
  • application/ - use cases

Membuat API Gin Pertama

Server Initialization

Setup HTTP server dasar dengan Gin engine dan konfigurasi port untuk development.

Route Definition

Mendefinisikan routes dan handler functions untuk endpoint RESTful dengan method yang sesuai.

Request Processing

Implementasi request-response cycle dengan parsing input dan formatting output yang konsisten.

Sample Code: Basic Gin Server

package main import ( "net/http" "github.com/gin-gonic/gin" ) func main() { r := gin.Default() r.GET("/ping", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "pong", }) }) r.Run(":8080") }

Kode dasar untuk menginisialisasi server Gin dengan endpoint sederhana. Server akan berjalan di port 8080 dan merespons GET request ke /ping dengan JSON response.

HTTP Methods dan Prinsip RESTful

GET Operations

Mengambil data dengan parameter URL dan query strings untuk filtering dan pagination yang efisien.

POST/PUT Methods

Mengirim dan mengupdate data dengan request body validation dan proper status code responses.

DELETE Operations

Menghapus resources dengan konfirmasi dan soft delete untuk data integrity yang terjaga.

Sample Code: Complete GET dan POST API

Implementasi lengkap API dengan method GET dan POST. Termasuk struktur data dan validation basic.

package main import ( "net/http" "github.com/gin-gonic/gin" ) type User struct { ID int `json:"id"` Name string `json:"name" binding:"required"` Email string `json:"email" binding:"required,email"` } var users []User var nextID = 1 func main() { r := gin.Default() // GET all users r.GET("/users", getUsers) // POST new user r.POST("/users", createUser) r.Run(":8080") } func getUsers(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "data": users, "count": len(users), }) } func createUser(c *gin.Context) { var newUser User if err := c.ShouldBindJSON(&newUser); err != nil { c.JSON(http.StatusBadRequest, gin.H{ "error": err.Error(), }) return } newUser.ID = nextID nextID++ users = append(users, newUser) c.JSON(http.StatusCreated, gin.H{ "message": "User created successfully", "data": newUser, }) }

Code mendemonstrasikan CRUD operations dasar dengan validation dan proper HTTP status codes.

Middleware dalam Gin

Request Processing

Middleware memproses request sebelum mencapai handler utama.

Built-in Middleware

Logger, Recovery, dan CORS middleware tersedia out-of-the-box.

Custom Middleware

Membuat middleware khusus untuk autentikasi dan validasi bisnis logic.

Middleware Chaining

Menggabungkan multiple middleware dengan urutan eksekusi yang tepat.

Validasi Request

JSON Binding

Binding otomatis JSON request ke Go structs dengan tag validation untuk memastikan data integrity dan type safety.

  • Struct tags untuk validation rules
  • Custom validator functions
  • Error message customization

Input Validation

Teknik validasi input yang komprehensif termasuk format checking, range validation, dan business rule enforcement.

  • Required field validation
  • Format validation (email, phone)
  • Range dan length constraints

Validasi Request

Validasi request memastikan data yang masuk sesuai format yang diharapkan. Gin menyediakan binding otomatis untuk JSON dengan tag validation.

Struct tags seperti required, email, dan min memberikan validasi built-in. Error handling yang proper mencegah data corrupt masuk ke sistem.

type RegisterRequest struct { Username string `json:"username" binding:"required,min=3,max=20"` Email string `json:"email" binding:"required,email"` Password string `json:"password" binding:"required,min=8"` Age int `json:"age" binding:"required,min=18,max=100"` } func registerUser(c *gin.Context) { var req RegisterRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{ "error": "Invalid input", "details": err.Error(), }) return } // Process valid data c.JSON(http.StatusOK, gin.H{ "message": "User registered successfully", "username": req.Username, }) }

Complete Code: JSON Binding dan Input Validation

Implementasi lengkap JSON binding dengan validasi input menggunakan struct tags. Kombinasi binding dan validate tags memastikan data integrity.

Error handling yang comprehensive mencakup validation errors dan JSON parsing errors. Response yang informatif membantu debugging client-side integration.

type User struct { Username string `json:"username" binding:"required,min=3,max=20"` Email string `json:"email" binding:"required,email"` Age int `json:"age" binding:"required,gte=18,lte=100"` Password string `json:"password" binding:"required,min=8"` } func createUser(c *gin.Context) { var user User if err := c.ShouldBindJSON(&user); err != nil { var validationErrors []string if ve, ok := err.(validator.ValidationErrors); ok { for _, e := range ve { switch e.Tag() { case "required": validationErrors = append(validationErrors, fmt.Sprintf("%s is required", e.Field())) case "email": validationErrors = append(validationErrors, "Invalid email format") case "min": validationErrors = append(validationErrors, fmt.Sprintf("%s must be at least %s characters", e.Field(), e.Param())) } } } c.JSON(http.StatusBadRequest, gin.H{ "status": "error", "message": "Validation failed", "errors": validationErrors, }) return } c.JSON(http.StatusCreated, gin.H{ "status": "success", "message": "User created successfully", "data": user, }) }

Format Response

Structured JSON

Format response konsisten

Error Handling

Standardisasi error messages

Data Management

Pagination dan filtering

Response formatting yang konsisten memudahkan client integration dan debugging. Struktur yang jelas dengan error codes yang meaningful membantu developer memahami API behavior.

Response Formatting yang Konsisten

  • Structured JSON: Format standar dengan fields success, data, dan metadata untuk konsistensi
  • Error Handling: Response error dengan kode status HTTP yang tepat dan pesan deskriptif
  • Data Management: Pagination, filtering, dan sorting terintegrasi dalam response structure
  • Timestamp & Tracking: Include request ID dan timestamp untuk debugging dan monitoring
  • API Versioning: Header version untuk backward compatibility dan smooth migration

Complete Code: Consistent Response Formatting

Implementasi lengkap response formatting yang konsisten dengan error handling dan data management terintegrasi.

package main import ( "net/http" "strconv" "github.com/gin-gonic/gin" ) type APIResponse struct { Status string `json:"status"` Message string `json:"message"` Data interface{} `json:"data,omitempty"` Errors []string `json:"errors,omitempty"` Meta *Meta `json:"meta,omitempty"` Version string `json:"version"` } type Meta struct { Page int `json:"page"` Limit int `json:"limit"` Total int `json:"total"` TotalPages int `json:"total_pages"` } type User struct { ID int `json:"id"` Name string `json:"name"` Email string `json:"email"` } var users = []User{ {ID: 1, Name: "John Doe", Email: "john@example.com"}, {ID: 2, Name: "Jane Smith", Email: "jane@example.com"}, } func successResponse(c *gin.Context, message string, data interface{}, meta *Meta) { response := APIResponse{ Status: "success", Message: message, Data: data, Meta: meta, Version: "v1", } c.JSON(http.StatusOK, response) } func errorResponse(c *gin.Context, statusCode int, message string, errors []string) { response := APIResponse{ Status: "error", Message: message, Errors: errors, Version: "v1", } c.JSON(statusCode, response) } func getUsers(c *gin.Context) { page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) limit, _ := strconv.Atoi(c.DefaultQuery("limit", "10")) if page < 1 { page = 1 } if limit < 1 || limit > 100 { limit = 10 } start := (page - 1) * limit end := start + limit if start >= len(users) { successResponse(c, "Users retrieved successfully", []User{}, &Meta{ Page: page, Limit: limit, Total: len(users), TotalPages: (len(users) + limit - 1) / limit, }) return } if end > len(users) { end = len(users) } paginatedUsers := users[start:end] meta := &Meta{ Page: page, Limit: limit, Total: len(users), TotalPages: (len(users) + limit - 1) / limit, } successResponse(c, "Users retrieved successfully", paginatedUsers, meta) } func getUserByID(c *gin.Context) { id, err := strconv.Atoi(c.Param("id")) if err != nil { errorResponse(c, http.StatusBadRequest, "Invalid user ID", []string{"ID must be a valid number"}) return } for _, user := range users { if user.ID == id { successResponse(c, "User found", user, nil) return } } errorResponse(c, http.StatusNotFound, "User not found", []string{"User with specified ID does not exist"}) } func main() { r := gin.Default() // API versioning v1 := r.Group("/api/v1") { v1.GET("/users", getUsers) v1.GET("/users/:id", getUserByID) } r.Run(":8080") }

Fundamental Keamanan

OWASP Top 10 untuk API Security

Broken Object Level Authorization, Broken User Authentication, Excessive Data Exposure, dan vulnerability lainnya yang umum pada API modern.

Vulnerability pada Aplikasi Go

SQL injection, XSS, CSRF, dan security issues spesifik Go seperti race conditions dan memory leaks yang perlu diwaspadai.

Security Headers dan Rate Limiting

Implementasi security headers, CORS policy, dan rate limiting untuk melindungi API dari berbagai jenis serangan.

Metode Autentikasi

Token-based Auth

JWT dan bearer tokens untuk stateless authentication

Session Management

Server-side sessions dengan secure cookie handling

OAuth Integration

Third-party authentication dengan Google, GitHub, Facebook

Pemilihan metode autentikasi tergantung pada use case. Token-based authentication cocok untuk mobile apps dan SPA, sementara session-based lebih sesuai untuk traditional web applications.

Basic Authentication

func BasicAuth() gin.HandlerFunc { return gin.BasicAuth(gin.Accounts{ "admin": "secret123", "user": "password", }) } func setupRoutes() *gin.Engine { r := gin.Default() authorized := r.Group("/admin", BasicAuth()) authorized.GET("/dashboard", getDashboard) return r }

Basic Authentication sederhana namun kurang aman untuk production. Gunakan HTTPS dan pertimbangkan untuk development environment saja karena credentials dikirim dalam setiap request.

JWT Authentication

Header

Algorithm dan token type

Payload

Claims dan user data

Signature

Verification dengan secret key

Refresh Strategy

Token renewal mechanism

Struktur JWT Token

Header

Berisi algoritma enkripsi dan tipe token.

{ "alg": "HS256", "typ": "JWT" }

Payload

Menyimpan claims dan data user.

{ "sub": "1234567890", "name": "John Doe", "iat": 1516239022, "exp": 1516242622 }

Signature

Verifikasi menggunakan secret key untuk keamanan.

HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret )

Sample Code: JWT Implementation

import "github.com/golang-jwt/jwt/v4" func GenerateToken(userID uint) (string, error) { claims := jwt.MapClaims{ "user_id": userID, "exp": time.Now().Add(time.Hour * 24).Unix(), } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) return token.SignedString([]byte("secret-key")) } func ValidateToken(tokenString string) (*jwt.Token, error) { return jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { return []byte("secret-key"), nil }) }

Implementasi JWT lengkap dengan token generation, validation, dan claims extraction. Pastikan menggunakan secret key yang kuat dan store secara aman di environment variables.

Role-Based Access Control (RBAC)

RBAC memungkinkan kontrol akses granular berdasarkan peran pengguna. Implementasi melibatkan middleware yang memeriksa role dan permissions sebelum mengizinkan akses ke endpoint tertentu.

Role-Based Access Control (RBAC)

RBAC mengatur akses berdasarkan peran pengguna. Setiap peran memiliki izin spesifik untuk resource tertentu.

Implementasi mencakup definisi roles, permissions, dan middleware untuk validasi akses secara otomatis.

package main import ( "net/http" "github.com/gin-gonic/gin" "github.com/golang-jwt/jwt/v4" ) type User struct { ID uint `json:"id"` Name string `json:"name"` Role string `json:"role"` } type Claims struct { UserID uint `json:"user_id"` Role string `json:"role"` jwt.RegisteredClaims } var users = map[uint]User{ 1: {ID: 1, Name: "Admin", Role: "admin"}, 2: {ID: 2, Name: "User", Role: "user"}, } func RequireRole(allowedRoles ...string) gin.HandlerFunc { return func(c *gin.Context) { tokenString := c.GetHeader("Authorization") if tokenString == "" { c.JSON(401, gin.H{"error": "Token required"}) c.Abort() return } claims := &Claims{} token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) { return []byte("secret-key"), nil }) if err != nil || !token.Valid { c.JSON(401, gin.H{"error": "Invalid token"}) c.Abort() return } for _, role := range allowedRoles { if claims.Role == role { c.Set("user_id", claims.UserID) c.Set("role", claims.Role) c.Next() return } } c.JSON(403, gin.H{"error": "Insufficient permissions"}) c.Abort() } } func main() { r := gin.Default() // Public endpoint r.GET("/public", func(c *gin.Context) { c.JSON(200, gin.H{"message": "Public access"}) }) // Admin only r.GET("/admin", RequireRole("admin"), func(c *gin.Context) { c.JSON(200, gin.H{"message": "Admin area"}) }) // Admin and user r.GET("/dashboard", RequireRole("admin", "user"), func(c *gin.Context) { role := c.GetString("role") c.JSON(200, gin.H{ "message": "Dashboard access", "role": role, }) }) r.Run(":8080") }

Pencegahan Vulnerability Keamanan

SQL Injection Prevention

Gunakan prepared statements dan parameterized queries dengan GORM untuk mencegah SQL injection attacks secara otomatis.

XSS Protection

Sanitasi input dan escape output untuk mencegah Cross-Site Scripting dengan validation yang ketat.

CSRF Prevention

Implementasi CSRF tokens dan SameSite cookies untuk melindungi dari Cross-Site Request Forgery.

Implementasi Kode Aman untuk Keamanan API

Implementasi praktik coding yang aman mencegah berbagai jenis serangan. CSRF bekerja dengan memanfaatkan trust relationship antara browser dan server.

Attacker membuat request palsu yang terlihat legitimate dari user browser. Browser secara otomatis mengirim cookies dan credentials yang tersimpan.

Cara Kerja CSRF: User login ke aplikasi bank. Attacker kirim email dengan link berbahaya. Ketika diklik, browser kirim request transfer uang tanpa sepengetahuan user.

Pencegahan: Gunakan CSRF tokens yang unik setiap request. Validasi origin header dan implement SameSite cookie attributes untuk blocking cross-site requests.

Selalu sanitasi input data dan gunakan prepared statements. Implement rate limiting dan logging untuk monitoring suspicious activities secara real-time.

Contoh Kode Vulnerability Keamanan

Tiga ancaman keamanan utama yang sering ditemukan dalam aplikasi web. SQL Injection memungkinkan penyerang memanipulasi database melalui input yang tidak tervalidasi.

Cross-Site Scripting (XSS) memungkinkan eksekusi script berbahaya di browser pengguna. Cross-Site Request Forgery (CSRF) memaksa pengguna melakukan aksi tidak diinginkan.

// SQL Injection Vulnerability func getUserByID(id string) User { query := "SELECT * FROM users WHERE id = " + id // DANGEROUS: Direct string concatenation rows, _ := db.Query(query) // Attacker input: "1 OR 1=1" } // XSS Vulnerability func displayComment(comment string) { // DANGEROUS: Unescaped output html := "<div>" + comment + "</div>" // Attacker input: "<script>alert('XSS')</script>" } // CSRF Vulnerability func deleteUser(c *gin.Context) { // DANGEROUS: No CSRF protection userID := c.Param("id") db.Delete(&User{}, userID) }

Implementasi yang tepat menggunakan prepared statements, HTML escaping, dan CSRF tokens untuk melindungi aplikasi dari serangan ini.

Contoh Kode Vulnerability Keamanan

  1. SQL Injection: SELECT * FROM users WHERE id = " + userInput - Input langsung ke query tanpa sanitasi
  1. XSS Attack: <script>alert('XSS')</script> - Script berbahaya diinjeksi melalui form input
  1. CSRF Attack: <img src="bank.com/transfer?amount=1000"> - Request otomatis tanpa verifikasi token
  1. Prevention: Gunakan prepared statements, escape HTML, dan implementasi CSRF tokens

Implementasi Kode Aman untuk Keamanan API

Implementasi yang tepat menggunakan prepared statements, HTML escaping, dan CSRF tokens mencegah serangan keamanan umum.

GORM secara otomatis melindungi dari SQL injection. Middleware Gin menyediakan perlindungan XSS dan CSRF built-in.

// SQL Injection Prevention dengan GORM func getUserByID(id uint) (User, error) { var user User // SAFE: GORM menggunakan prepared statements result := db.First(&user, id) return user, result.Error } // XSS Prevention dengan HTML Escaping func displayComment(c *gin.Context) { comment := c.PostForm("comment") // SAFE: Gin secara otomatis escape HTML c.HTML(200, "comment.html", gin.H{ "comment": comment, // Automatically escaped }) } // CSRF Prevention dengan Middleware func setupCSRF() gin.HandlerFunc { return csrf.Middleware(csrf.Options{ Secret: "your-secret-key", ErrorFunc: func(c *gin.Context) { c.JSON(403, gin.H{"error": "CSRF token invalid"}) }, }) } func main() { r := gin.Default() r.Use(setupCSRF()) // Apply CSRF protection r.POST("/delete/:id", deleteUser) }

Pengenalan GORM

Object-Relational Mapping

GORM menyediakan abstraksi database yang powerful dengan mapping otomatis antara Go structs dan database tables.

Fitur Lengkap

Auto-migrations, associations, hooks, transactions, dan connection pooling untuk produktivitas maksimal.

Multi-Database

Dukungan MySQL, PostgreSQL, SQLite, SQL Server dengan driver yang konsisten dan reliable.

Mapping Database Table ke Go Structs

GORM memetakan database tables secara otomatis ke Go structs. Field names dan types disesuaikan dengan konvensi Go.

Membuat Model dengan GORM

type User struct { ID uint `gorm:"primaryKey"` Name string `gorm:"size:100;not null"` Email string `gorm:"uniqueIndex;not null"` CreatedAt time.Time UpdatedAt time.Time DeletedAt gorm.DeletedAt `gorm:"index"` // Relationships Posts []Post `gorm:"foreignKey:UserID"` } type Post struct { ID uint `gorm:"primaryKey"` Title string `gorm:"size:200;not null"` UserID uint User User `gorm:"references:ID"` }

Model definition dengan struct tags untuk database constraints, relationships, dan indexing. Soft delete support dengan DeletedAt field untuk data recovery.

Setup Database dengan GORM

Connection Setup

Establishing database connection dengan connection string dan driver yang sesuai untuk environment.

Configuration Options

Setting connection pool, timeout, logging level, dan performance tuning parameters.

Auto Migration

Otomatis membuat dan mengupdate database schema berdasarkan struct definitions.

Sample Code: GORM Setup

import ( "gorm.io/driver/postgres" "gorm.io/gorm" ) func InitDB() (*gorm.DB, error) { dsn := "host=localhost user=postgres password=secret dbname=myapp port=5432" db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{ Logger: logger.Default.LogMode(logger.Info), }) if err != nil { return nil, err } // Auto migrate db.AutoMigrate(&User{}, &Product{}) return db, nil }

Setup koneksi database dengan PostgreSQL driver, logging configuration, dan auto-migration untuk model User dan Product. Error handling memastikan aplikasi tidak crash saat database unavailable.

Operasi CRUD dengan GORM

Create Operations

Insert single records, batch inserts, dan upsert operations dengan validation dan error handling yang proper.

Read Operations

Query dengan conditions, joins, preloading relationships, pagination, dan sorting untuk data retrieval yang efisien.

Update & Delete

Update selective fields, batch updates, soft deletes, dan hard deletes dengan transaction support untuk data consistency.

Sample Code: CRUD Implementation

type UserRepository struct { db *gorm.DB } func (r *UserRepository) Create(user *User) error { return r.db.Create(user).Error } func (r *UserRepository) GetByID(id uint) (*User, error) { var user User err := r.db.Preload("Posts").First(&user, id).Error return &user, err } func (r *UserRepository) Update(user *User) error { return r.db.Save(user).Error } func (r *UserRepository) Delete(id uint) error { return r.db.Delete(&User{}, id).Error }

Repository pattern implementation untuk encapsulation database operations. Preloading relationships dan proper error handling untuk robustness dan maintainability.

Database Migrations

Create Migration

Membuat migration files dengan timestamp untuk version control dan reproducible schema changes.

Run Migrations

Eksekusi migrations programmatically atau via CLI dengan tracking applied migrations.

3

Rollback Strategy

Rollback mechanism untuk reverting schema changes ketika terjadi issues dalam production.

Fitur Advanced GORM

3x

Query Performance

Peningkatan speed dengan preloading dan query optimization

50%

Memory Usage

Pengurangan memory footprint dengan selective loading

99.9%

Query Success

Reliability dengan proper error handling dan retries

Best Practices Struktur Proyek

Domain Layer

Business logic dan entities

Application Layer

Use cases dan orchestration

Interface Layer

Controllers dan presenters

Infrastructure

Database dan external services

MVC Pattern dalam Go

Controllers

Handle HTTP requests, parameter binding, dan response formatting. Thin controllers yang delegate business logic ke service layer.

  • Request validation
  • Response formatting
  • Error handling

Services & Models

Business logic implementation dan data access layer. Services untuk use cases, models untuk data structure dan validation.

  • Business rules
  • Data validation
  • Database operations

Sample Code: MVC Structure

Controllers Directory

controllers/ ├── user_controller.go ├── product_controller.go └── auth_controller.go

HTTP handlers untuk setiap endpoint API dengan request/response processing.

Models Directory

models/ ├── user.go ├── product.go └── database.go

Struct definitions dan database schema dengan GORM annotations.

Views & Routes

routes/ ├── api.go └── middleware.go main.go go.mod

Route definitions, middleware setup, dan application entry point.

Clean Architecture

Clean Architecture membagi aplikasi menjadi lapisan-lapisan dengan batasan jelas untuk menciptakan sistem modular dan mudah diuji. Berbeda dengan MVC (3 komponen), Clean Architecture memiliki 4 lapisan terpisah dengan prinsip dependency inversion.

Perbandingan dengan MVC

MVC memiliki batasan kurang tegas:

  • Controllers terikat langsung dengan framework
  • Models mencampur business logic dan data access
  • Separation of concerns kurang diutamakan
  • Testability lebih rendah

Keunggulan Clean Architecture

  • Independent of frameworks - tidak terikat framework tertentu
  • Testable - business rules dapat diuji tanpa UI/database
  • Independent of UI - UI dapat berubah tanpa mengubah sistem
  • Independent of database - database dapat diganti tanpa mengubah business rules

Clean Architecture terdiri dari:

Domain Layer

Entities dan business rules core aplikasi (user entities, business logic, domain interfaces)

Use Case Layer

Application services dan orchestration logic (user services, repository interfaces, input/output ports)

Interface Layer

Controllers, presenters, dan gateways (HTTP handlers, response formatters, request validators)

Infrastructure Layer

Database, external APIs, dan framework (GORM repositories, third-party APIs, configuration)

Struktur Proyek Clean Architecture

Alur request dari HTTP hingga database dengan pemisahan tanggung jawab yang jelas.

HTTP Request

Client mengirim request ke API endpoint

2

Handler (Gin)

Router menerima dan memproses HTTP request

Service (Business Logic)

Implementasi logika bisnis dan use cases

4

Repository (Data Access)

Interface untuk operasi data abstrak

Model (Entities)

Struktur data dan validasi business rules

Database

Penyimpanan data persisten

Struktur Proyek Clean Architecture

Struktur proyek Clean Architecture memisahkan kode berdasarkan tanggung jawab. Setiap layer memiliki direktori yang jelas dan terorganisir.

cmd/server/main.go adalah entry point aplikasi. configs/ menyimpan konfigurasi sistem dan environment variables.

internal/handler/ berisi Gin HTTP handlers untuk komunikasi eksternal.
internal/service/ mengatur business logic dan use cases aplikasi.

internal/repository/ menangani database interaction dan persistence layer.
internal/domain/models/ menyimpan core entities tanpa dependency eksternal.

pkg/ berisi reusable libraries seperti JWT utilities dan logging functions yang dapat digunakan ulang.

Pengenalan Goroutines

Concurrency vs Parallelism

Concurrency adalah composition of independent processes, parallelism adalah simultaneous execution pada multiple cores.

Lightweight Threads

Goroutines memiliki overhead rendah (~2KB stack) dibanding OS threads, memungkinkan thousands concurrent operations.

API Use Cases

Background processing, parallel database queries, async task execution, dan real-time notifications dalam API development.

Pengenalan Goroutines

Goroutines adalah lightweight threads yang memungkinkan concurrent execution dalam Go. Setiap goroutine hanya membutuhkan 2KB memory stack.

Keyword go digunakan untuk menjalankan function secara asynchronous. Runtime Go dapat mengelola ribuan goroutines secara efisien.

package main import ( "fmt" "time" ) func sayHello(name string) { for i := 0; i < 3; i++ { fmt.Printf("Hello %s! %d\n", name, i) time.Sleep(100 * time.Millisecond) } } func main() { // Sequential execution sayHello("Alice") // Concurrent execution with goroutines go sayHello("Bob") go sayHello("Charlie") // Wait for goroutines to complete time.Sleep(500 * time.Millisecond) fmt.Println("Main function completed") }

Dasar-dasar Channel

1
2
1

Channel Types

Buffered dan unbuffered channels

2

Communication Patterns

Send, receive, dan close operations

Select Statement

Non-blocking channel operations dan timeouts

Channels adalah primary communication mechanism antara goroutines. "Don't communicate by sharing memory; share memory by communicating" adalah filosofi Go untuk safe concurrency.

Dasar-dasar Channel

Channel adalah komunikasi antar goroutines yang aman. Mereka memungkinkan pertukaran data tanpa race conditions.

Channel menggunakan operator <- untuk mengirim dan menerima data. Channel dapat buffered atau unbuffered sesuai kebutuhan.

// Membuat channel ch := make(chan string) // Mengirim data go func() { ch <- "Hello from goroutine" }() // Menerima data message := <-ch fmt.Println(message) // Buffered channel buffered := make(chan int, 3) buffered <- 1 buffered <- 2

Sample Code: Concurrent API Processing

Unbuffered channels menyinkronkan goroutines secara langsung. Setiap operasi send akan **memblokir** hingga ada receiver.

Channel ini ideal untuk **komunikasi real-time** antar goroutines dalam API processing. Tidak ada buffering data internal.

package main import ( "fmt" "time" ) // Simulasi API request processing func processAPIRequest(id int, ch chan string) { // Simulate API processing time time.Sleep(200 * time.Millisecond) result := fmt.Sprintf("API Request %d processed", id) ch <- result // Send ke channel (blocking) } func main() { // Unbuffered channel resultChan := make(chan string) // Launch goroutines untuk process requests for i := 1; i <= 3; i++ { go processAPIRequest(i, resultChan) } // Receive results dari channel for i := 1; i <= 3; i++ { result := <-resultChan // Receive dari channel (blocking) fmt.Println(result) } fmt.Println("All API requests completed") }

Sample Code: Concurrent API Processing

func ProcessOrdersConcurrently(orders []Order) error { const maxWorkers = 10 orderChan := make(chan Order, len(orders)) errorChan := make(chan error, len(orders)) // Start workers for i := 0; i < maxWorkers; i++ { go func() { for order := range orderChan { if err := processOrder(order); err != nil { errorChan <- err } else { errorChan <- nil } } }() } // Send orders to channel for _, order := range orders { orderChan <- order } close(orderChan) // Collect results for i := 0; i < len(orders); i++ { if err := <-errorChan; err != nil { return err } } return nil }

Worker pool pattern untuk memproses multiple orders secara concurrent. Limiting jumlah workers mencegah resource exhaustion dan memberikan control yang lebih baik terhadap system load.

Mutex dan Sinkronisasi

Race Conditions

Kondisi dimana multiple goroutines mengakses shared memory secara bersamaan tanpa proper synchronization.

Mutex Protection

sync.Mutex menyediakan mutual exclusion untuk melindungi critical sections dari concurrent access.

3

RWMutex Optimization

sync.RWMutex memungkinkan multiple readers atau single writer untuk workload read-heavy.

Sample Code: Mutex Implementation

type SafeCounter struct { mu sync.Mutex count int } func (c *SafeCounter) Increment() { c.mu.Lock() defer c.mu.Unlock() c.count++ } func (c *SafeCounter) Value() int { c.mu.Lock() defer c.mu.Unlock() return c.count } // Usage in API handler func incrementHandler(counter *SafeCounter) gin.HandlerFunc { return func(c *gin.Context) { counter.Increment() c.JSON(200, gin.H{"count": counter.Value()}) } }

Thread-safe counter implementation dengan mutex protection. Defer statement memastikan unlock selalu dipanggil meskipun terjadi panic, mencegah deadlock scenarios.

Fundamental Unit Testing

Testing Package

Go built-in testing package menyediakan framework lengkap untuk unit testing dengan simple API dan powerful features.

Table-Driven Tests

Pattern untuk testing multiple scenarios dengan data-driven approach, memudahkan maintenance dan coverage expansion.

Mocking Dependencies

Isolation unit under test dengan mocking external dependencies menggunakan interfaces dan dependency injection.

Testing HTTP Handlers

httptest Package

Go's httptest package menyediakan utilities untuk testing HTTP handlers tanpa starting actual server.

  • Request recorder untuk capturing responses
  • Test server untuk integration testing
  • Request simulation dengan berbagai methods

Gin Testing

Testing Gin handlers dengan proper context setup dan middleware testing untuk comprehensive coverage.

  • Context mocking untuk handler testing
  • Middleware testing dengan chaining
  • Route parameter dan query testing

Sample Code: API Tests

func TestCreateUser(t *testing.T) { gin.SetMode(gin.TestMode) tests := []struct { name string payload string expected int }{ {"Valid user", `{"name":"John","email":"john@example.com"}`, 201}, {"Invalid email", `{"name":"John","email":"invalid"}`, 400}, {"Missing name", `{"email":"john@example.com"}`, 400}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { w := httptest.NewRecorder() req, _ := http.NewRequest("POST", "/users", strings.NewReader(tt.payload)) req.Header.Set("Content-Type", "application/json") router := setupRouter() router.ServeHTTP(w, req) assert.Equal(t, tt.expected, w.Code) }) } }

Table-driven test untuk API endpoint dengan multiple test scenarios. Comprehensive testing mencakup valid cases, validation errors, dan edge cases untuk robust API behavior.

Performance Testing

1000

Requests/Second

Target throughput untuk API endpoints

100ms

Response Time

Maximum acceptable latency

95%

Success Rate

Minimum success rate under load

512MB

Memory Limit

Maximum memory usage threshold

Strategi Error Handling

Custom Errors

Domain-specific error types

Error Wrapping

Context preservation dengan wrapping

Structured Response

Consistent error response format

Logging Strategy

Comprehensive error logging dan monitoring

Containerization dan Deployment

# Multi-stage Dockerfile for Go FROM golang:1.19-alpine AS builder WORKDIR /app COPY go.mod go.sum ./ RUN go mod download COPY . . RUN CGO_ENABLED=0 GOOS=linux go build -o main . FROM alpine:latest RUN apk --no-cache add ca-certificates WORKDIR /root/ COPY --from=builder /app/main . EXPOSE 8080 CMD ["./main"]

Multi-stage Docker build untuk optimized container size. Builder stage untuk compilation, final stage dengan minimal Alpine Linux untuk production deployment yang efficient.

Scaling Go Applications

Horizontal Scaling

Multiple instances dengan load balancing untuk handling increased traffic dan fault tolerance.

2

Stateless Design

Session-less architecture dengan external state storage untuk seamless scaling dan deployment.

Connection Pooling

Database dan Redis connection pools untuk resource efficiency dan performance optimization.

Load Balancing

Traffic distribution strategies dengan health checks dan graceful shutdown support.

Made with