## 1. Project Overview & Quickstart (gin-gonic/gin)

## File: README.md

# Gin Web Framework



[](https://github.com/gin-gonic/gin/actions/workflows/gin.yml)
[](https://github.com/gin-gonic/gin/actions/workflows/trivy-scan.yml)
[](https://codecov.io/gh/gin-gonic/gin)
[](https://goreportcard.com/report/github.com/gin-gonic/gin)
[](https://pkg.go.dev/github.com/gin-gonic/gin?tab=doc)
[](https://sourcegraph.com/github.com/gin-gonic/gin?badge)
[](https://www.codetriage.com/gin-gonic/gin)
[](https://github.com/gin-gonic/gin/releases)

## 📰 Gin 1.12.0 is now available!

We're excited to announce the release of **[Gin 1.12.0](https://gin-gonic.com/en/blog/news/gin-1-12-0-release-announcement/)**! This release brings new features, performance improvements, and important bug fixes. Check out the [release announcement](https://gin-gonic.com/en/blog/news/gin-1-12-0-release-announcement/) on our official blog for the full details.

---

Gin is a high-performance HTTP web framework written in [Go](https://go.dev/). It provides a Martini-like API but with significantly better performance—up to 40 times faster—thanks to [httprouter](https://github.com/julienschmidt/httprouter). Gin is designed for building REST APIs, web applications, and microservices where speed and developer productivity are essential.

**Why choose Gin?**

Gin combines the simplicity of Express.js-style routing with Go's performance characteristics, making it ideal for:

- Building high-throughput REST APIs
- Developing microservices that need to handle many concurrent requests
- Creating web applications that require fast response times
- Prototyping web services quickly with minimal boilerplate

**Gin's key features:**

- **Zero allocation router** - Extremely memory-efficient routing with no heap allocations
- **High performance** - Benchmarks show superior speed compared to other Go web frameworks
- **Middleware support** - Extensible middleware system for authentication, logging, CORS, etc.
- **Crash-free** - Built-in recovery middleware prevents panics from crashing your server
- **JSON validation** - Automatic request/response JSON binding and validation
- **Route grouping** - Organize related routes and apply common middleware
- **Error management** - Centralized error handling and logging
- **Built-in rendering** - Support for JSON, XML, HTML templates, and more
- **Extensible** - Large ecosystem of community middleware and plugins

## Getting Started

### Prerequisites

- **Go version**: Gin requires [Go](https://go.dev/) version [1.25](https://go.dev/doc/devel/release#go1.25.0) or above
- **Basic Go knowledge**: Familiarity with Go syntax and package management is helpful

### Installation

With [Go's module support](https://go.dev/wiki/Modules#how-to-use-modules), simply import Gin in your code and Go will automatically fetch it during build:

```go
import "github.com/gin-gonic/gin"
```

### Your First Gin Application

Here's a complete example that demonstrates Gin's simplicity:

```go
package main

import (
  "log"
  "net/http"

  "github.com/gin-gonic/gin"
)

func main() {
  // Create a Gin router with default middleware (logger and recovery)
  r := gin.Default()

  // Define a simple GET endpoint
  r.GET("/ping", func(c *gin.Context) {
    // Return JSON response
    c.JSON(http.StatusOK, gin.H{
      "message": "pong",
    })
  })

  // Start server on port 8080 (default)
  // Server will listen on 0.0.0.0:8080 (localhost:8080 on Windows)
  if err := r.Run(); err != nil {
    log.Fatalf("failed to run server: %v", err)
  }
}
```

**Running the application:**

1. Save the code above as `main.go`
2. Run the application:

   ```sh
   go run main.go
   ```

3. Open your browser and visit [`http://localhost:8080/ping`](http://localhost:8080/ping)
4. You should see: `{"message":"pong"}`

**What this example demonstrates:**

- Creating a Gin router with default middleware
- Defining HTTP endpoints with simple handler functions
- Returning JSON responses
- Starting an HTTP server

### Next Steps

After running your first Gin application, explore these resources to learn more:

#### 📚 Learning Resources

- **[Gin Quick Start Guide](docs/doc.md)** - Comprehensive tutorial with API examples and build configurations
- **[Example Repository](https://github.com/gin-gonic/examples)** - Ready-to-run examples demonstrating various Gin use cases:
  - REST API development
  - Authentication & middleware
  - File uploads and downloads
  - WebSocket connections
  - Template rendering

## 📖 Documentation

### API Reference

- **[Go.dev API Documentation](https://pkg.go.dev/github.com/gin-gonic/gin)** - Complete API reference with examples

### User Guides

The comprehensive documentation is available on [gin-gonic.com](https://gin-gonic.com) in multiple languages:

- [English](https://gin-gonic.com/en/docs/) | [简体中文](https://gin-gonic.com/zh-cn/docs/) | [繁體中文](https://gin-gonic.com/zh-tw/docs/)
- [日本語](https://gin-gonic.com/ja/docs/) | [한국어](https://gin-gonic.com/ko-kr/docs/) | [Español](https://gin-gonic.com/es/docs/)
- [Turkish](https://gin-gonic.com/tr/docs/) | [Persian](https://gin-gonic.com/fa/docs/) | [Português](https://gin-gonic.com/pt/docs/)
- [Russian](https://gin-gonic.com/ru/docs/) | [Indonesian](https://gin-gonic.com/id/docs/)

### Official Tutorials

- [Go.dev Tutorial: Developing a RESTful API with Go and Gin](https://go.dev/doc/tutorial/web-service-gin)

## ⚡ Performance Benchmarks

Gin demonstrates exceptional performance compared to other Go web frameworks. It uses a custom version of [HttpRouter](https://github.com/julienschmidt/httprouter) for maximum efficiency. [View detailed benchmarks →](/BENCHMARKS.md)

**Gin vs. Other Go Frameworks** (GitHub API routing benchmark):

| Benchmark name                 |       (1) |             (2) |          (3) |             (4) |
| ------------------------------ | --------: | --------------: | -----------: | --------------: |
| BenchmarkGin_GithubAll         | **43550** | **27364 ns/op** |   **0 B/op** | **0 allocs/op** |
| BenchmarkAce_GithubAll         |     40543 |     29670 ns/op |       0 B/op |     0 allocs/op |
| BenchmarkAero_GithubAll        |     57632 |     20648 ns/op |       0 B/op |     0 allocs/op |
| BenchmarkBear_GithubAll        |      9234 |    216179 ns/op |   86448 B/op |   943 allocs/op |
| BenchmarkBeego_GithubAll       |      7407 |    243496 ns/op |   71456 B/op |   609 allocs/op |
| BenchmarkBone_GithubAll        |       420 |   2922835 ns/op |  720160 B/op |  8620 allocs/op |
| BenchmarkChi_GithubAll         |      7620 |    238331 ns/op |   87696 B/op |   609 allocs/op |
| BenchmarkDenco_GithubAll       |     18355 |     64494 ns/op |   20224 B/op |   167 allocs/op |
| BenchmarkEcho_GithubAll        |     31251 |     38479 ns/op |       0 B/op |     0 allocs/op |
| BenchmarkGocraftWeb_GithubAll  |      4117 |    300062 ns/op |  131656 B/op |  1686 allocs/op |
| BenchmarkGoji_GithubAll        |      3274 |    416158 ns/op |   56112 B/op |   334 allocs/op |
| BenchmarkGojiv2_GithubAll      |      1402 |    870518 ns/op |  352720 B/op |  4321 allocs/op |
| BenchmarkGoJsonRest_GithubAll  |      2976 |    401507 ns/op |  134371 B/op |  2737 allocs/op |
| BenchmarkGoRestful_GithubAll   |       410 |   2913158 ns/op |  910144 B/op |  2938 allocs/op |
| BenchmarkGorillaMux_GithubAll  |       346 |   3384987 ns/op |  251650 B/op |  1994 allocs/op |
| BenchmarkGowwwRouter_GithubAll |     10000 |    143025 ns/op |   72144 B/op |   501 allocs/op |
| BenchmarkHttpRouter_GithubAll  |     55938 |     21360 ns/op |       0 B/op |     0 allocs/op |
| BenchmarkHttpTreeMux_GithubAll |     10000 |    153944 ns/op |   65856 B/op |   671 allocs/op |
| BenchmarkKocha_GithubAll       |     10000 |    106315 ns/op |   23304 B/op |   843 allocs/op |
| BenchmarkLARS_GithubAll        |     47779 |     25084 ns/op |       0 B/op |     0 allocs/op |
| BenchmarkMacaron_GithubAll     |      3266 |    371907 ns/op |  149409 B/op |  1624 allocs/op |
| BenchmarkMartini_GithubAll     |       331 |   3444706 ns/op |  226551 B/op |  2325 allocs/op |
| BenchmarkPat_GithubAll         |       273 |   4381818 ns/op | 1483152 B/op | 26963 allocs/op |
| BenchmarkPossum_GithubAll      |     10000 |    164367 ns/op |   84448 B/op |   609 allocs/op |
| BenchmarkR2router_GithubAll    |     10000 |    160220 ns/op |   77328 B/op |   979 allocs/op |
| BenchmarkRivet_GithubAll       |     14625 |     82453 ns/op |   16272 B/op |   167 allocs/op |
| BenchmarkTango_GithubAll       |      6255 |    279611 ns/op |   63826 B/op |  1618 allocs/op |
| BenchmarkTigerTonic_GithubAll  |      2008 |    687874 ns/op |  193856 B/op |  4474 allocs/op |
| BenchmarkTraffic_GithubAll     |       355 |   3478508 ns/op |  820744 B/op | 14114 allocs/op |
| BenchmarkVulcan_GithubAll      |      6885 |    193333 ns/op |   19894 B/op |   609 allocs/op |

- (1): Total Repetitions achieved in constant time, higher means more confident result
- (2): Single Repetition Duration (ns/op), lower is better
- (3): Heap Memory (B/op), lower is better
- (4): Average Allocations per Repetition (allocs/op), lower is better

## 🔌 Middleware Ecosystem

Gin has a rich ecosystem of middleware for common web development needs. Explore community-contributed middleware:

- **[gin-contrib](https://github.com/gin-contrib)** - Official middleware collection including:
  - Authentication (JWT, Basic Auth, Sessions)
  - CORS, Rate limiting, Compression
  - Logging, Metrics, Tracing
  - Static file serving, Template engines
- **[gin-gonic/contrib](https://github.com/gin-gonic/contrib)** - Additional community middleware

## 🏢 Production Usage

Gin powers many high-traffic applications and services in production:

- **[gorush](https://github.com/appleboy/gorush)** - High-performance push notification server
- **[fnproject](https://github.com/fnproject/fn)** - Container-native, serverless platform
- **[photoprism](https://github.com/photoprism/photoprism)** - AI-powered personal photo management
- **[lura](https://github.com/luraproject/lura)** - Ultra-performant API Gateway framework
- **[picfit](https://github.com/thoas/picfit)** - Real-time image processing server
- **[dkron](https://github.com/distribworks/dkron)** - Distributed job scheduling system

## 🤝 Contributing

Gin is the work of hundreds of contributors from around the world. We welcome and appreciate your contributions! See the full list of [contributors](https://github.com/gin-gonic/gin/graphs/contributors).

### How to Contribute

- 🐛 **Report bugs** - Help us identify and fix issues
- 💡 **Suggest features** - Share your ideas for improvements
- 📝 **Improve documentation** - Help make our docs clearer
- 🔧 **Submit code** - Fix bugs or implement new features
- 🧪 **Write tests** - Improve our test coverage

### Getting Started with Contributing

1. Check out our [CONTRIBUTING.md](CONTRIBUTING.md) for detailed guidelines
2. Join our community discussions and ask questions

**All contributions are valued and help make Gin better for everyone!**

---

## File: examples/README.md

# Gin Examples

⚠️  **NOTICE:** All gin examples have been moved as standalone repository to [here](https://github.com/gin-gonic/examples).

---

## File: ginS/README.md

# Gin Default Server

This is API experiment for Gin.

```go
package main

import (
	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/ginS"
)

func main() {
	ginS.GET("/", func(c *gin.Context) { c.String(200, "Hello World") })
	ginS.Run()
}
```

## 2. Official Technical Reference & Guides (gin-gonic/website)

## File: README.md

# Gin website

[](https://github.com/gin-gonic/website/actions/workflows/node.yml)
[](https://github.com/gin-gonic/website/actions/workflows/trivy-scan.yml)

Welcome! This repository houses all the assets required to build the Gin website and documentation. We're pleased that you want to contribute! The website is hosted at [https://gin-gonic.com](https://gin-gonic.com).

We use [Astro](https://astro.build) to format and generate our website, the [Starlight](https://starlight.astro.build) template for styling and site structure. Thanks!.

## Contribution

- Fork the repository

You can click the Fork button in the upper-right area of the screen to create a copy of this repository in your GitHub account. This copy is called as fork.

- Create one pull request

Make any changes you want in your fork, and when you are ready to send those changes to us, go to your fork and create a new pull request to let us know about it.

- Merge the pull request

Once your pull request is created, a Gin reviewer will take responsibility for providing clear, actionable feedback, re-improve and merge.

## Running

See the [official Astro documentation](https://docs.astro.build/en/getting-started) for Astro installation instructions. and [Starlight documentation](https://starlight.astro.build/getting-started) for Starlight installation instructions.

To run the site locally when you have Hugo installed:

```sh
git clone https://github.com/<your-username>/website.git # your fork url
cd website

# ensure you have node installed
node -v
# else https://nodejs.org/en/download

npm install 
npm run dev
```

This will start the local Astro server on port 4321. Open up your browser to <http://localhost:4321> to view the site. As you make changes to the source files, Astro updates the site and forces a browser refresh.

## 🚀 Project Structure

Inside of your Astro + Starlight project, you'll see the following folders and files:

```bash
.
├── public/
├── src/
│   ├── assets/
│   ├── content/
│   │   ├── docs/
│   └── content.config.ts
├── astro.config.mjs
├── package.json
└── tsconfig.json
```

Starlight looks for `.md` or `.mdx` files in the `src/content/docs/` directory. Each file is exposed as a route based on its file name.

Images can be added to `src/assets/` and embedded in Markdown with a relative link.

Static assets, like favicons, can be placed in the `public/` directory.

## 🧞 Commands

All commands are run from the root of the project, from a terminal:

| Command                   | Action                                           |
| :------------------------ | :----------------------------------------------- |
| `npm install`             | Installs dependencies                            |
| `npm run dev`             | Starts local dev server at `localhost:4321`      |
| `npm run build`           | Build your production site to `./dist/`          |
| `npm run preview`         | Preview your build locally, before deploying     |
| `npm run astro ...`       | Run CLI commands like `astro add`, `astro check` |
| `npm run astro -- --help` | Get help using the Astro CLI                     |

## Thanks

Gin thrives on community participation, and we really appreciate your contributions to our site and our documentation!

---

## File: src/content/docs/id/docs/faq/index.md

---
title: "Pertanyaan Umum"
sidebar:
  order: 15
---

## Pertanyaan Umum

### Bagaimana cara mengaktifkan live reload selama pengembangan?

Gunakan [Air](https://github.com/air-verse/air) untuk live reloading otomatis selama pengembangan. Air memantau file Anda dan membangun ulang/memulai ulang aplikasi Anda ketika perubahan terdeteksi.

**Instalasi:**

```sh
go install github.com/air-verse/air@latest
```

**Pengaturan:**

Buat file konfigurasi `.air.toml` di root proyek Anda:

```sh
air init
```

Lalu jalankan `air` di direktori proyek Anda alih-alih `go run`:

```sh
air
```

Air akan memantau file `.go` Anda dan secara otomatis membangun ulang/memulai ulang aplikasi Gin Anda saat ada perubahan. Lihat [dokumentasi Air](https://github.com/air-verse/air) untuk opsi konfigurasi.

### Bagaimana cara menangani CORS di Gin?

Gunakan middleware resmi [gin-contrib/cors](https://github.com/gin-contrib/cors):

```go
package main

import (
  "time"

  "github.com/gin-contrib/cors"
  "github.com/gin-gonic/gin"
)

func main() {
  r := gin.Default()

  // Default CORS configuration
  r.Use(cors.Default())

  // Or customize CORS settings
  r.Use(cors.New(cors.Config{
    AllowOrigins:     []string{"https://example.com"},
    AllowMethods:     []string{"GET", "POST", "PUT", "DELETE"},
    AllowHeaders:     []string{"Origin", "Content-Type", "Authorization"},
    ExposeHeaders:    []string{"Content-Length"},
    AllowCredentials: true,
    MaxAge:           12 * time.Hour,
  }))

  r.GET("/ping", func(c *gin.Context) {
    c.JSON(200, gin.H{"message": "pong"})
  })

  r.Run()
}
```

Untuk gambaran keamanan lengkap, lihat [Praktik terbaik keamanan](/id/docs/middleware/security-guide/).

### Bagaimana cara menyajikan file statis?

Gunakan `Static()` atau `StaticFS()` untuk menyajikan file statis:

```go
func main() {
  r := gin.Default()

  // Serve files from ./assets directory at /assets/*
  r.Static("/assets", "./assets")

  // Serve a single file
  r.StaticFile("/favicon.ico", "./resources/favicon.ico")

  // Serve from embedded filesystem (Go 1.16+)
  r.StaticFS("/public", http.FS(embedFS))

  r.Run()
}
```

Lihat [Menyajikan data dari file](/id/docs/rendering/serving-data-from-file/) untuk detail lebih lanjut.

### Bagaimana cara menangani upload file?

Gunakan `FormFile()` untuk file tunggal atau `MultipartForm()` untuk banyak file:

```go
// Single file upload
r.POST("/upload", func(c *gin.Context) {
  file, _ := c.FormFile("file")
  c.SaveUploadedFile(file, "./uploads/"+file.Filename)
  c.String(200, "File %s uploaded successfully", file.Filename)
})

// Multiple files upload
r.POST("/upload-multiple", func(c *gin.Context) {
  form, _ := c.MultipartForm()
  files := form.File["files"]

  for _, file := range files {
    c.SaveUploadedFile(file, "./uploads/"+file.Filename)
  }
  c.String(200, "%d files uploaded", len(files))
})
```

Lihat dokumentasi [Upload file](/id/docs/routing/upload-file/) untuk detail lebih lanjut.

### Bagaimana cara mengimplementasikan autentikasi dengan JWT?

Gunakan [gin-contrib/jwt](https://github.com/gin-contrib/jwt) atau implementasikan middleware kustom. Berikut contoh minimal:

```go
package main

import (
  "net/http"
  "time"

  "github.com/gin-gonic/gin"
  "github.com/golang-jwt/jwt/v5"
)

var jwtSecret = []byte("your-secret-key")

type Claims struct {
  Username string `json:"username"`
  jwt.RegisteredClaims
}

func AuthMiddleware() gin.HandlerFunc {
  return func(c *gin.Context) {
    tokenString := c.GetHeader("Authorization")
    if tokenString == "" {
      c.JSON(http.StatusUnauthorized, gin.H{"error": "Missing authorization token"})
      c.Abort()
      return
    }

    // Remove "Bearer " prefix if present
    if len(tokenString) > 7 && tokenString[:7] == "Bearer " {
      tokenString = tokenString[7:]
    }

    token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
      return jwtSecret, nil
    })

    if err != nil || !token.Valid {
      c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token"})
      c.Abort()
      return
    }

    if claims, ok := token.Claims.(*Claims); ok {
      c.Set("username", claims.Username)
      c.Next()
    }
  }
}
```

Untuk autentikasi berbasis session, lihat [Manajemen session](/id/docs/middleware/session-management/).

### Bagaimana cara mengatur logging permintaan?

Gin menyertakan middleware logger default melalui `gin.Default()`. Untuk logging JSON terstruktur di produksi, lihat [Logging terstruktur](/id/docs/logging/structured-logging/).

Untuk kustomisasi log dasar:

```go
r := gin.New()
r.Use(gin.LoggerWithConfig(gin.LoggerConfig{
  SkipPaths: []string{"/healthz"},
}))
r.Use(gin.Recovery())
```

Lihat bagian [Logging](/id/docs/logging/) untuk semua opsi termasuk format kustom, output file, dan melewati query string.

### Bagaimana cara menangani shutdown graceful?

Lihat [Restart atau stop graceful](/id/docs/server-config/graceful-restart-or-stop/) untuk panduan lengkap dengan contoh kode.

### Mengapa saya mendapatkan "404 Not Found" alih-alih "405 Method Not Allowed"?

Secara default, Gin mengembalikan 404 untuk rute yang tidak mendukung metode HTTP yang diminta. Atur `HandleMethodNotAllowed = true` untuk mengembalikan 405 sebagai gantinya:

```go
r := gin.Default()
r.HandleMethodNotAllowed = true

r.GET("/ping", func(c *gin.Context) {
  c.JSON(200, gin.H{"message": "pong"})
})

r.Run()
```

```sh
$ curl -X POST localhost:8080/ping

HTTP/1.1 405 Method Not Allowed
Allow: GET
```

### Bagaimana cara mengikat parameter query dan data POST bersamaan?

Gunakan `ShouldBind()` yang secara otomatis memilih binding berdasarkan tipe konten:

```go
type User struct {
  Name  string `form:"name" json:"name"`
  Email string `form:"email" json:"email"`
  Page  int    `form:"page"`
}

r.POST("/user", func(c *gin.Context) {
  var user User
  if err := c.ShouldBind(&user); err != nil {
    c.JSON(400, gin.H{"error": err.Error()})
    return
  }
  c.JSON(200, user)
})
```

Lihat bagian [Binding](/id/docs/binding/) untuk semua opsi binding.

### Bagaimana cara memvalidasi data permintaan?

Gin menggunakan [go-playground/validator](https://github.com/go-playground/validator) untuk validasi. Tambahkan tag validasi ke struct Anda:

```go
type User struct {
  Name  string `json:"name" binding:"required,min=3,max=50"`
  Email string `json:"email" binding:"required,email"`
  Age   int    `json:"age" binding:"gte=0,lte=130"`
}

r.POST("/user", func(c *gin.Context) {
  var user User
  if err := c.ShouldBindJSON(&user); err != nil {
    c.JSON(400, gin.H{"error": err.Error()})
    return
  }
  c.JSON(200, gin.H{"message": "User is valid"})
})
```

Lihat [Binding dan validasi](/id/docs/binding/binding-and-validation/) untuk validator kustom dan penggunaan lanjutan.

### Bagaimana cara menjalankan Gin dalam mode produksi?

Atur variabel lingkungan `GIN_MODE` ke `release`:

```sh
export GIN_MODE=release
# or
GIN_MODE=release ./your-app
```

Atau atur secara programatis:

```go
gin.SetMode(gin.ReleaseMode)
```

Mode release menonaktifkan logging debug dan meningkatkan performa.

### Bagaimana cara menangani koneksi database dengan Gin?

Lihat [Integrasi database](/id/docs/server-config/database/) untuk panduan lengkap yang mencakup `database/sql`, GORM, connection pooling, dan pola dependency injection.

### Bagaimana cara menguji handler Gin?

Gunakan `net/http/httptest` untuk menguji rute Anda:

```go
func TestPingRoute(t *testing.T) {
  router := gin.Default()
  router.GET("/ping", func(c *gin.Context) {
    c.JSON(200, gin.H{"message": "pong"})
  })

  w := httptest.NewRecorder()
  req, _ := http.NewRequest("GET", "/ping", nil)
  router.ServeHTTP(w, req)

  assert.Equal(t, 200, w.Code)
  assert.Contains(t, w.Body.String(), "pong")
}
```

Lihat dokumentasi [Pengujian](/id/docs/testing/) untuk contoh lebih lanjut.

## Pertanyaan Performa

### Bagaimana cara mengoptimalkan Gin untuk lalu lintas tinggi?

1. **Use Release Mode**: Set `GIN_MODE=release`
2. **Disable unnecessary middleware**: Only use what you need
3. **Use `gin.New()` instead of `gin.Default()`** for manual middleware control
4. **Connection pooling**: Configure database connection pools (see [Database integration](/en/docs/server-config/database/))
5. **Caching**: Implement caching for frequently accessed data
6. **Load balancing**: Use reverse proxy (nginx, HAProxy)
7. **Profiling**: Use Go's pprof to identify bottlenecks
8. **Monitoring**: Set up [metrics and monitoring](/en/docs/server-config/metrics/) to track performance

### Apakah Gin siap produksi?

Ya. Gin digunakan di produksi oleh banyak perusahaan dan telah teruji dalam skala besar. Lihat [Pengguna](/id/docs/users/) untuk contoh proyek yang menggunakan Gin di produksi.

## Pemecahan Masalah

### Mengapa parameter rute saya tidak berfungsi?

Pastikan parameter rute menggunakan sintaks `:` dan diekstrak dengan benar:

```go
// Correct
r.GET("/user/:id", func(c *gin.Context) {
  id := c.Param("id")
  c.String(200, "User ID: %s", id)
})

// Not: /user/{id} or /user/
```

Lihat [Parameter di path](/id/docs/routing/param-in-path/) untuk detail.

### Mengapa middleware saya tidak dieksekusi?

Middleware harus didaftarkan sebelum rute atau grup rute:

```go
// Correct order
r := gin.New()
r.Use(MyMiddleware()) // Register middleware first
r.GET("/ping", handler) // Then routes

// For route groups
auth := r.Group("/admin")
auth.Use(AuthMiddleware()) // Middleware for this group
{
  auth.GET("/dashboard", handler)
}
```

Lihat [Menggunakan middleware](/id/docs/middleware/using-middleware/) untuk detail.

### Mengapa binding permintaan gagal?

Alasan umum:

1. **Tag binding hilang**: Tambahkan tag `json:"field"` atau `form:"field"`
2. **Content-Type tidak cocok**: Pastikan klien mengirim header Content-Type yang benar
3. **Error validasi**: Periksa tag validasi dan persyaratan
4. **Field yang tidak diekspor**: Hanya field struct yang diekspor (huruf kapital) yang diikat

```go
type User struct {
  Name  string `json:"name" binding:"required"` // ✓ Correct
  Email string `json:"email"`                    // ✓ Correct
  age   int    `json:"age"`                      // ✗ Won't bind (unexported)
}
```

Lihat [Binding dan validasi](/id/docs/binding/binding-and-validation/) untuk detail.

---

## File: src/content/docs/id/docs/deployment/index.md

---
title: "Deployment"
sidebar:
  order: 10
---

Proyek Gin dapat di-deploy dengan mudah di penyedia cloud mana pun.

## [Railway](https://www.railway.com)

Railway is a cutting-edge cloud development platform for deploying, managing, and scaling applications and services. It simplifies your infrastructure stack from servers to observability with a single, scalable, easy-to-use platform.

Follow the Railway [guide to deploy your Gin projects](https://docs.railway.com/guides/gin).

## [Seenode](https://seenode.com)

Seenode is a modern cloud platform designed specifically for developers who want to deploy applications quickly and efficiently. It offers git-based deployment, automatic SSL certificates, built-in databases, and a streamlined interface that gets your Gin applications live in minutes.

Follow the Seenode [guide to deploy your Gin projects](https://seenode.com/docs/frameworks/go/gin).

## [Koyeb](https://www.koyeb.com)

Koyeb is a developer-friendly serverless platform to deploy apps globally with git-based deployment, TLS encryption, native autoscaling, a global edge network, and built-in service mesh & discovery.

Follow the Koyeb [guide to deploy your Gin projects](https://www.koyeb.com/tutorials/deploy-go-gin-on-koyeb).

## [Qovery](https://www.qovery.com)

Qovery provides free Cloud hosting with databases, SSL, a global CDN, and automatic deploys with Git.

See [Qovery](https://hub.qovery.com/guides/getting-started/deploy-your-first-application/) for more information.

## [Render](https://render.com)

Render is a modern cloud platform that offers native support for Go, fully managed SSL, databases, zero-downtime deploys, HTTP/2, and websocket support.

Follow the Render [guide to deploying Gin projects](https://render.com/docs/deploy-go-gin).

## [Google App Engine](https://cloud.google.com/appengine/)

GAE has two ways to deploy Go applications. The standard environment is easier to use but less customizable and prevents [syscalls](https://github.com/gin-gonic/gin/issues/1639) for security reasons. The flexible environment can run any framework or library.

Learn more and pick your preferred environment at [Go on Google App Engine](https://cloud.google.com/appengine/docs/go/).

## Self Hosted

Proyek Gin juga dapat di-deploy secara self-hosted. Arsitektur deployment dan pertimbangan keamanan bervariasi tergantung pada lingkungan target. Bagian berikut hanya menyajikan gambaran tingkat tinggi dari opsi konfigurasi yang perlu dipertimbangkan saat merencanakan deployment.

## Opsi Konfigurasi

Deployment proyek Gin dapat disesuaikan menggunakan variabel lingkungan atau langsung dalam kode.

Variabel lingkungan berikut tersedia untuk mengonfigurasi Gin:

| Variabel Lingkungan | Deskripsi                                                                                                                                                                                                   |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| PORT                 | Port TCP untuk didengarkan saat memulai server Gin dengan `router.Run()` (yaitu tanpa argumen apa pun).                                                                                                      |
| GIN_MODE             | Atur ke salah satu dari `debug`, `release`, atau `test`. Menangani manajemen mode Gin, seperti kapan mengeluarkan output debug. Juga dapat diatur dalam kode menggunakan `gin.SetMode(gin.ReleaseMode)` atau `gin.SetMode(gin.TestMode)` |

Kode berikut dapat digunakan untuk mengonfigurasi Gin.

```go
// Don't specify the bind address or port for Gin. Defaults to binding on all interfaces on port 8080.
// Can use the `PORT` environment variable to change the listen port when using `Run()` without any arguments.
router := gin.Default()
router.Run()

// Specify the bind address and port for Gin.
router := gin.Default()
router.Run("192.168.1.100:8080")

// Specify only the listen port. Will bind on all interfaces.
router := gin.Default()
router.Run(":8080")

// Set which IP addresses or CIDRs, are considered to be trusted for setting headers to document real client IP addresses.
// See the documentation for additional details.
router := gin.Default()
router.SetTrustedProxies([]string{"192.168.1.2"})
```

Untuk informasi tentang mengonfigurasi proxy tepercaya, lihat [Proxy Tepercaya](/id/docs/server-config/trusted-proxies/).

---

## File: src/content/docs/id/docs/build-tags/index.md

---
title: "Tag Build"
sidebar:
  order: 11
---

Go [build tags](https://pkg.go.dev/go/build#hdr-Build_Constraints) (juga disebut build constraints) adalah direktif yang memberitahu compiler Go untuk menyertakan atau mengecualikan file selama kompilasi. Gin menggunakan build tags untuk memungkinkan Anda mengganti implementasi internal atau menonaktifkan fitur opsional pada waktu kompilasi, tanpa mengubah kode aplikasi apa pun.

Ini berguna dalam beberapa skenario:

- **Optimasi performa** -- Ganti paket `encoding/json` default dengan encoder pihak ketiga yang lebih cepat untuk mempercepat serialisasi JSON di API Anda.
- **Pengurangan ukuran binary** -- Hapus fitur yang tidak Anda gunakan, seperti rendering MsgPack, untuk menghasilkan binary yang lebih kecil.
- **Penyesuaian deployment** -- Pilih encoder berbeda untuk lingkungan berbeda (mis., build produksi throughput tinggi vs. build pengembangan standar).

Tag build diteruskan ke toolchain Go dengan flag `-tags`:

```sh
go build -tags=<tag_name> .
```

You can combine multiple tags by separating them with commas:

```sh
go build -tags=nomsgpack,go_json .
```

### Tag build yang tersedia

| Tag | Effect |
|---|---|
| `go_json` | Replaces `encoding/json` with [go-json](https://github.com/goccy/go-json) |
| `jsoniter` | Replaces `encoding/json` with [jsoniter](https://github.com/json-iterator/go) |
| `sonic avx` | Replaces `encoding/json` with [sonic](https://github.com/bytedance/sonic) (requires AVX CPU instructions) |
| `nomsgpack` | Disables MsgPack rendering support |

:::note
Tag build hanya memengaruhi cara Gin dikompilasi. Kode aplikasi Anda (handler rute, middleware, dll.) tidak perlu berubah saat Anda berganti tag.
:::

## Dalam bagian ini

Halaman-halaman di bawah ini membahas setiap tag build secara detail:

- [**Build dengan penggantian JSON**](./json-replacement/) -- Ganti encoder JSON default dengan go-json, jsoniter, atau sonic untuk serialisasi yang lebih cepat.
- [**Build tanpa MsgPack**](./nomsgpack/) -- Nonaktifkan rendering MsgPack dengan tag build `nomsgpack` untuk mengurangi ukuran binary.

---

## File: src/content/docs/id/docs/build-tags/json-replacement.md

---
title: "Build dengan penggantian JSON"
sidebar:
  order: 1
---

Gin menggunakan paket `encoding/json` pustaka standar untuk serialisasi dan deserialisasi JSON secara default. Encoder pustaka standar telah diuji dengan baik dan sepenuhnya kompatibel, tetapi bukan opsi tercepat yang tersedia. Jika performa JSON menjadi bottleneck dalam aplikasi Anda -- misalnya, dalam API throughput tinggi yang menyerialisasi payload respons besar -- Anda dapat mengganti dengan pengganti drop-in yang lebih cepat pada waktu build menggunakan tag build. Tidak diperlukan perubahan kode.

## Pengganti yang tersedia

Gin mendukung tiga encoder JSON alternatif. Masing-masing mengimplementasikan antarmuka yang sama yang diharapkan Gin, sehingga handler, middleware, dan logika binding Anda terus bekerja tanpa modifikasi.

### go-json

[go-json](https://github.com/goccy/go-json) is a pure-Go JSON encoder that offers significant performance improvements over `encoding/json` while maintaining full compatibility. It works on all platforms and architectures.

```sh
go build -tags=go_json .
```

### jsoniter

[jsoniter](https://github.com/json-iterator/go) (json-iterator) is another pure-Go, high-performance JSON library. It is API-compatible with `encoding/json` and provides a flexible configuration system for advanced use cases.

```sh
go build -tags=jsoniter .
```

### sonic

[sonic](https://github.com/bytedance/sonic) is a blazing-fast JSON encoder developed by ByteDance. It uses JIT compilation and SIMD instructions to achieve maximum throughput, making it the fastest option among the three.

```sh
go build -tags="sonic avx" .
```

:::note
Sonic requires a CPU with AVX instruction support. This is available on most modern x86_64 processors (Intel Sandy Bridge and later, AMD Bulldozer and later), but it will not work on ARM architectures or older x86 hardware. If your deployment target does not support AVX, use go-json or jsoniter instead.
:::

## Memilih pengganti

| Encoder | Platform support | Key strength |
|---|---|---|
| `encoding/json` (default) | All | Maximum compatibility, no extra dependency |
| go-json | All | Good speedup, pure Go, broad compatibility |
| jsoniter | All | Good speedup, flexible configuration |
| sonic | x86_64 with AVX only | Highest throughput via JIT and SIMD |

Untuk sebagian besar aplikasi, **go-json** adalah pilihan yang aman dan efektif -- bekerja di mana saja dan memberikan peningkatan performa yang berarti. Pilih **sonic** ketika Anda membutuhkan throughput JSON maksimum dan server Anda berjalan pada hardware x86_64. Pilih **jsoniter** jika Anda membutuhkan fitur konfigurasi spesifiknya atau sudah menggunakannya di tempat lain dalam kode Anda.

## Memverifikasi pengganti

Anda dapat mengonfirmasi bahwa pengganti aktif dengan membandingkan performa serialisasi dengan benchmark sederhana, atau dengan memeriksa tabel simbol binary:

```sh
# Build with go-json
go build -tags=go_json -o myapp .

# Check that go-json symbols are present
go tool nm myapp | grep goccy
```

The build tag also works with other Go commands:

```sh
go run -tags=go_json .
go test -tags=go_json ./...
```

:::note
Only use one JSON replacement tag at a time. If you specify multiple JSON tags (e.g., `-tags=go_json,jsoniter`), the behavior is undefined. The `nomsgpack` tag can be safely combined with any JSON replacement tag.
:::

---

## File: src/content/docs/id/docs/build-tags/nomsgpack.md

---
title: "Build tanpa MsgPack"
sidebar:
  order: 2
---

[MsgPack](https://msgpack.org/) (MessagePack) adalah format serialisasi binary yang ringkas -- anggap saja sebagai alternatif JSON yang lebih cepat dan lebih kecil. Gin menyertakan dukungan rendering dan binding MsgPack secara default, yang berarti aplikasi Anda dapat menerima dan mengembalikan data berenkode MsgPack langsung menggunakan `c.Bind()` dan `c.Render()` dengan tipe konten yang sesuai.

Namun, banyak aplikasi hanya menggunakan JSON dan tidak pernah membutuhkan MsgPack. Dalam kasus tersebut, dependensi MsgPack menambah bobot yang tidak perlu ke binary yang dikompilasi. Anda dapat menghapusnya dengan tag build `nomsgpack`.

## Building without MsgPack

Pass the `nomsgpack` tag to `go build`:

```sh
go build -tags=nomsgpack .
```

This also works with other Go commands:

```sh
go run -tags=nomsgpack .
go test -tags=nomsgpack ./...
```

## What changes

When you build with `nomsgpack`, Gin excludes the MsgPack rendering and binding code at compile time. This has a few practical effects:

- The compiled binary is smaller because the MsgPack serialization library is not linked in.
- Any handler that attempts to render or bind MsgPack data will no longer work. If you use `c.ProtoBuf()` or other non-MsgPack renderers, those are unaffected.
- All JSON, XML, YAML, TOML, and ProtoBuf features continue to work normally.

:::note
If your API does not serve MsgPack responses and you do not call `c.MsgPack()` anywhere, it is safe to use this tag. Your existing JSON and HTML handlers will behave identically.
:::

## Verifying the result

You can confirm the binary size reduction by comparing builds:

```sh
# Standard build
go build -o gin-app .
ls -lh gin-app

# Build without MsgPack
go build -tags=nomsgpack -o gin-app-nomsgpack .
ls -lh gin-app-nomsgpack
```

The exact savings depend on your application, but removing MsgPack typically shaves a small amount off the final binary. For more background, see the [original pull request](https://github.com/gin-gonic/gin/pull/1852).

---

## File: src/content/docs/id/docs/binding/bind-body-into-different-structs.md

---
title: "Mencoba bind body ke struct berbeda"
sidebar:
  order: 13
---

Metode binding standar seperti `c.ShouldBind` mengonsumsi `c.Request.Body`, yang merupakan `io.ReadCloser` — setelah dibaca, tidak dapat dibaca lagi. Ini berarti Anda tidak dapat memanggil `c.ShouldBind` berkali-kali pada request yang sama untuk mencoba bentuk struct yang berbeda.

Untuk mengatasi ini, gunakan `c.ShouldBindBodyWith`. Metode ini membaca body sekali dan menyimpannya di context, memungkinkan binding berikutnya menggunakan kembali body yang sudah di-cache.

```go
package main

import (
  "net/http"

  "github.com/gin-gonic/gin"
  "github.com/gin-gonic/gin/binding"
)

type formA struct {
  Foo string `json:"foo" xml:"foo" binding:"required"`
}

type formB struct {
  Bar string `json:"bar" xml:"bar" binding:"required"`
}

func main() {
  router := gin.Default()

  router.POST("/bind", func(c *gin.Context) {
    objA := formA{}
    objB := formB{}
    // This reads c.Request.Body and stores the result into the context.
    if errA := c.ShouldBindBodyWith(&objA, binding.JSON); errA == nil {
      c.JSON(http.StatusOK, gin.H{"message": "matched formA", "foo": objA.Foo})
      return
    }
    // At this time, it reuses body stored in the context.
    if errB := c.ShouldBindBodyWith(&objB, binding.JSON); errB == nil {
      c.JSON(http.StatusOK, gin.H{"message": "matched formB", "bar": objB.Bar})
      return
    }

    c.JSON(http.StatusBadRequest, gin.H{"error": "request body did not match any known format"})
  })

  router.Run(":8080")
}
```

## Uji coba

```sh
# Body matches formA
curl -X POST http://localhost:8080/bind \
  -H "Content-Type: application/json" \
  -d '{"foo":"hello"}'
# Output: {"foo":"hello","message":"matched formA"}

# Body matches formB
curl -X POST http://localhost:8080/bind \
  -H "Content-Type: application/json" \
  -d '{"bar":"world"}'
# Output: {"bar":"world","message":"matched formB"}
```

:::note
`c.ShouldBindBodyWith` menyimpan body di context sebelum melakukan binding. Ini memiliki sedikit dampak pada performa, jadi gunakan hanya ketika Anda perlu melakukan bind body lebih dari sekali. Untuk format yang tidak membaca body — seperti `Query`, `Form`, `FormPost`, `FormMultipart` — Anda dapat memanggil `c.ShouldBind()` berkali-kali tanpa masalah.
:::

## Lihat juga

- [Binding dan validasi](/id/docs/binding/binding-and-validation/)
- [Bind query string atau post data](/id/docs/binding/bind-query-or-post/)

---

## File: src/content/docs/id/docs/binding/bind-custom-unmarshaler.md

---
title: "Bind unmarshaler kustom"
sidebar:
  order: 8
---

Untuk mengganti logika binding default Gin, definisikan fungsi pada tipe Anda yang memenuhi antarmuka `encoding.TextUnmarshaler` dari pustaka standar Go. Kemudian tentukan `parser=encoding.TextUnmarshaler` dalam tag `uri`/`form` dari field yang diikat.

```go
package main

import (
  "encoding"
  "strings"

  "github.com/gin-gonic/gin"
)

type Birthday string

func (b *Birthday) UnmarshalText(text []byte) error {
  *b = Birthday(strings.Replace(string(text), "-", "/", -1))
  return nil
}

var _ encoding.TextUnmarshaler = (*Birthday)(nil)

func main() {
  route := gin.Default()
  var request struct {
    Birthday         Birthday   `form:"birthday,parser=encoding.TextUnmarshaler"`
    Birthdays        []Birthday `form:"birthdays,parser=encoding.TextUnmarshaler" collection_format:"csv"`
    BirthdaysDefault []Birthday `form:"birthdaysDef,default=2020-09-01;2020-09-02,parser=encoding.TextUnmarshaler" collection_format:"csv"`
  }
  route.GET("/test", func(ctx *gin.Context) {
    _ = ctx.BindQuery(&request)
    ctx.JSON(200, request)
  })
  _ = route.Run(":8088")
}
```

Uji dengan:

```sh
curl 'localhost:8088/test?birthday=2000-01-01&birthdays=2000-01-01,2000-01-02'
```

Hasil:

```sh
{"Birthday":"2000/01/01","Birthdays":["2000/01/01","2000/01/02"],"BirthdaysDefault":["2020/09/01","2020/09/02"]}
```

> **Catatan:** Jika `parser=encoding.TextUnmarshaler` ditentukan untuk tipe yang **tidak** mengimplementasikan `encoding.TextUnmarshaler`, Gin akan mengabaikannya dan melanjutkan dengan logika binding default-nya.

### Menggunakan BindUnmarshaler

Jika sebuah tipe sudah mengimplementasikan `encoding.TextUnmarshaler` tetapi Anda ingin menyesuaikan cara Gin mengikat tipe tersebut secara berbeda (mis., untuk mengubah pesan error yang dikembalikan), Anda dapat mengimplementasikan antarmuka `BindUnmarshaler` yang khusus.

```go
package main

import (
  "strings"

  "github.com/gin-gonic/gin"
  "github.com/gin-gonic/gin/binding"
)

type Birthday string

func (b *Birthday) UnmarshalParam(param string) error {
  *b = Birthday(strings.Replace(param, "-", "/", -1))
  return nil
}

var _ binding.BindUnmarshaler = (*Birthday)(nil)

func main() {
  route := gin.Default()
  var request struct {
    Birthday         Birthday   `form:"birthday"`
    Birthdays        []Birthday `form:"birthdays" collection_format:"csv"`
    BirthdaysDefault []Birthday `form:"birthdaysDef,default=2020-09-01;2020-09-02" collection_format:"csv"`
  }
  route.GET("/test", func(ctx *gin.Context) {
    _ = ctx.BindQuery(&request)
    ctx.JSON(200, request)
  })
  _ = route.Run(":8088")
}
```

> **Catatan:** Jika sebuah tipe mengimplementasikan baik `encoding.TextUnmarshaler` maupun `BindUnmarshaler`, Gin akan menggunakan `BindUnmarshaler` secara default kecuali Anda menentukan `parser=encoding.TextUnmarshaler` dalam tag binding.

---

## File: src/content/docs/id/docs/binding/bind-default-values.md

---
title: "Bind nilai default untuk field form"
sidebar:
  order: 5
---

Terkadang Anda ingin sebuah field kembali ke nilai default ketika klien tidak mengirim nilai. Binding form Gin mendukung default melalui opsi `default` dalam tag struct `form`. Ini bekerja untuk skalar dan, mulai Gin v1.11, untuk koleksi (slice/array) dengan format koleksi eksplisit.

Poin penting:

- Letakkan default tepat setelah kunci form: `form:"name,default=William"`.
- Untuk koleksi, tentukan cara memisahkan nilai dengan `collection_format:"multi|csv|ssv|tsv|pipes"`.
- Untuk `multi` dan `csv`, gunakan titik koma dalam default untuk memisahkan nilai (mis., `default=1;2;3`). Gin mengonversinya ke koma secara internal agar parser tag tetap tidak ambigu.
- Untuk `ssv` (spasi), `tsv` (tab), dan `pipes` (|), gunakan pemisah alami dalam default.

Contoh:

```go
package main

import (
  "net/http"

  "github.com/gin-gonic/gin"
)

type Person struct {
  Name      string    `form:"name,default=William"`
  Age       int       `form:"age,default=10"`
  Friends   []string  `form:"friends,default=Will;Bill"`                     // multi/csv: use ; in defaults
  Addresses [2]string `form:"addresses,default=foo bar" collection_format:"ssv"`
  LapTimes  []int     `form:"lap_times,default=1;2;3" collection_format:"csv"`
}

func main() {
  r := gin.Default()
  r.POST("/person", func(c *gin.Context) {
    var req Person
    if err := c.ShouldBind(&req); err != nil { // infers binder by Content-Type
      c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
      return
    }
    c.JSON(http.StatusOK, req)
  })
  _ = r.Run(":8080")
}
```

Jika Anda POST tanpa body apapun, Gin merespons dengan nilai default:

```sh
curl -X POST http://localhost:8080/person
```

Respons (contoh):

```json
{
  "Name": "William",
  "Age": 10,
  "Friends": ["Will", "Bill"],
  "Addresses": ["foo", "bar"],
  "LapTimes": [1, 2, 3]
}
```

Catatan dan peringatan:

- Koma digunakan oleh sintaks tag struct Go untuk memisahkan opsi; hindari koma di dalam nilai default.
- Untuk `multi` dan `csv`, titik koma memisahkan nilai default; jangan sertakan titik koma di dalam default individual untuk format ini.
- Nilai `collection_format` yang tidak valid akan menghasilkan error binding.

Perubahan terkait:

- Format koleksi untuk binding form (`multi`, `csv`, `ssv`, `tsv`, `pipes`) ditingkatkan sekitar v1.11.
- Nilai default untuk koleksi ditambahkan di v1.11 (PR #4048).

---

## File: src/content/docs/id/docs/binding/bind-form-data-custom-struct-tag.md

---
title: "Bind form-data dengan tag struct kustom"
sidebar:
  order: 14
---

Secara default, Gin menggunakan tag struct `form` untuk melakukan bind data form. Ketika Anda perlu melakukan bind struct yang menggunakan tag berbeda — misalnya, tipe eksternal yang tidak dapat Anda modifikasi — Anda dapat membuat binding kustom yang membaca dari tag Anda sendiri.

Ini berguna saat berintegrasi dengan pustaka pihak ketiga yang struct-nya menggunakan tag seperti `url`, `query`, atau nama kustom lainnya sebagai pengganti `form`.

```go
package main

import (
  "net/http"

  "github.com/gin-gonic/gin"
  "github.com/gin-gonic/gin/binding"
)

const (
  customerTag   = "url"
  defaultMemory = 32 << 20
)

type customerBinding struct{}

func (customerBinding) Name() string {
  return "form"
}

func (customerBinding) Bind(req *http.Request, obj any) error {
  if err := req.ParseForm(); err != nil {
    return err
  }
  if err := req.ParseMultipartForm(defaultMemory); err != nil {
    if err != http.ErrNotMultipart {
      return err
    }
  }
  if err := binding.MapFormWithTag(obj, req.Form, customerTag); err != nil {
    return err
  }
  return validate(obj)
}

func validate(obj any) error {
  if binding.Validator == nil {
    return nil
  }
  return binding.Validator.ValidateStruct(obj)
}

// FormA is an external type that we can't modify its tag
type FormA struct {
  FieldA string `url:"field_a"`
}

func main() {
  router := gin.Default()

  router.GET("/list", func(c *gin.Context) {
    var urlBinding = customerBinding{}
    var opt FormA
    if err := c.MustBindWith(&opt, urlBinding); err != nil {
      return
    }
    c.JSON(http.StatusOK, gin.H{"field_a": opt.FieldA})
  })

  router.Run(":8080")
}
```

## Uji coba

```sh
# The custom binding reads from the "url" struct tag instead of "form"
curl "http://localhost:8080/list?field_a=hello"
# Output: {"field_a":"hello"}

# Missing parameter -- empty string
curl "http://localhost:8080/list"
# Output: {"field_a":""}
```

:::note
Binding kustom mengimplementasikan interface `binding.Binding`, yang memerlukan metode `Name() string` dan metode `Bind(*http.Request, any) error`. Helper `binding.MapFormWithTag` melakukan pekerjaan sebenarnya untuk memetakan nilai form ke field struct menggunakan tag kustom Anda.
:::

## Lihat juga

- [Binding dan validasi](/id/docs/binding/binding-and-validation/)
- [Bind permintaan form-data dengan struct kustom](/id/docs/binding/bind-form-data-request-with-custom-struct/)