Gin — API Surface#

API types#

Library — Gin is a pure Go HTTP framework library. It exposes no CLI, no gRPC surface, and no plugin binary protocol. Its entire public API is consumed programmatically by application code. There are two integration layers: the primary object-oriented API (gin.Engine / gin.RouterGroup / gin.Context) and a thin singleton wrapper (ginS package) for quick scripts.


Library API#

Public packages#

PackageRole
github.com/gin-gonic/ginCore: Engine, RouterGroup, Context, middleware constructors, type definitions
github.com/gin-gonic/gin/bindingBinding interfaces + singleton instances (JSON, XML, Form, Query, YAML, TOML, ProtoBuf, MsgPack, BSON, URI, Header, Plain)
github.com/gin-gonic/gin/renderRender interface + render implementations (JSON, XML, HTML, YAML, TOML, ProtoBuf, MsgPack, BSON, SSE, Redirect, Data, String)
github.com/gin-gonic/gin/codec/jsonSwappable JSON backend (Core interface; implementations: stdlib, jsoniter, sonic, go-json)
github.com/gin-gonic/gin/ginSSingleton wrapper that delegates every Engine method to a lazily-initialized global gin.Default() engine

Engine construction#

// Bare engine — no default middleware
engine := gin.New(optFns ...OptionFunc) *Engine

// Engine with Logger + Recovery middleware pre-attached
engine := gin.Default(optFns ...OptionFunc) *Engine

OptionFunc func(*Engine) — functional options applied at construction time. Built-in options:

  • gin.WithMaxMultipartMemory(bytes int64) — configures multipart parse limit

Engine fields configurable after creation (direct struct assignment):

  • RedirectTrailingSlash bool (default true)
  • RedirectFixedPath bool
  • HandleMethodNotAllowed bool
  • ForwardedByClientIP bool
  • UseRawPath bool
  • UnescapePathValues bool
  • MaxMultipartMemory int64
  • UseH2C bool — enable HTTP/2 cleartext
  • ContextWithFallback bool — let c.Value(key) fall through to c.Request.Context()

Route registration#

Gin’s routing API is exposed via two interfaces:

IRoutes (routergroup.go:33):

type IRoutes interface {
    Use(...HandlerFunc) IRoutes

    Handle(string, string, ...HandlerFunc) IRoutes
    Any(string, ...HandlerFunc) IRoutes
    GET(string, ...HandlerFunc) IRoutes
    POST(string, ...HandlerFunc) IRoutes
    DELETE(string, ...HandlerFunc) IRoutes
    PATCH(string, ...HandlerFunc) IRoutes
    PUT(string, ...HandlerFunc) IRoutes
    OPTIONS(string, ...HandlerFunc) IRoutes
    HEAD(string, ...HandlerFunc) IRoutes
    Match([]string, string, ...HandlerFunc) IRoutes

    StaticFile(string, string) IRoutes
    StaticFileFS(string, string, http.FileSystem) IRoutes
    Static(string, string) IRoutes
    StaticFS(string, http.FileSystem) IRoutes
}

IRouter (routergroup.go:27) extends IRoutes with:

Group(string, ...HandlerFunc) *RouterGroup

Both *Engine and *RouterGroup implement IRouter. The difference is that engine.Use() returns *Engine while group.Use() returns *RouterGroup — controlled by the returnObj() helper that checks group.root.

Route path syntax:

  • Named parameter: /users/:id — captured as c.Param("id")
  • Wildcard: /static/*filepath — captures remainder including slashes
  • Escaped colon: /items/::sku — literal colon in path (post-processed via sync.Once)
  • Custom: Handle(httpMethod, path, handlers...) accepts any uppercase ASCII method name

Handler variadic convention: all registration methods accept ...HandlerFunc. All but the last are treated as route-scoped middleware; the last is the terminal handler. In practice any HandlerFunc in the chain can call c.Next() to delegate or c.Abort() to short-circuit.


Middleware registration#

Type: HandlerFunc func(*Context) — the same type used for both middleware and terminal handlers. This is the core design choice: no separate middleware type.

Global (engine-level):

engine.Use(Logger(), Recovery(), rateLimiter)

Group-scoped:

v1 := engine.Group("/api/v1", authMiddleware)
v1.Use(rateLimiter)

Route-scoped (inline):

engine.GET("/admin", authMiddleware, adminHandler)

All three forms ultimately concatenate HandlerFunc slices using RouterGroup.combineHandlers(). The resulting chain is stored in the radix tree node at registration time, so there is no per-request chain assembly overhead.

Built-in middleware constructors:

ConstructorFileDescription
Logger() HandlerFunclogger.go:224Colorized request/response logging to stdout
LoggerWithConfig(conf LoggerConfig) HandlerFunclogger.go:245Configurable logger (custom formatter, writer, skip paths)
LoggerWithFormatter(f LogFormatter) HandlerFunclogger.go:229Logger with custom format function
LoggerWithWriter(out io.Writer, skip ...string) HandlerFunclogger.go:237Logger to arbitrary writer
ErrorLogger() HandlerFunclogger.go:207Logs only requests that returned errors
ErrorLoggerT(typ ErrorType) HandlerFunclogger.go:212Filtered error logger by error type
Recovery() HandlerFuncrecovery.go:35Panic recovery → 500 response
CustomRecovery(handle RecoveryFunc) HandlerFuncrecovery.go:40Recovery with custom panic handler
RecoveryWithWriter(out io.Writer, ...) HandlerFuncrecovery.go:45Recovery logging to arbitrary writer
CustomRecoveryWithWriter(out, handle) HandlerFuncrecovery.go:53Full custom recovery
BasicAuth(accounts Accounts) HandlerFuncauth.go:72HTTP Basic Auth
BasicAuthForRealm(accounts, realm) HandlerFuncauth.go:48Basic Auth with custom realm
BasicAuthForProxy(accounts, realm) HandlerFuncauth.go:98Proxy authentication header

Context API#

*gin.Context is the sole argument to every HandlerFunc. It is the user-facing API for everything that happens inside a handler. It is pooled via sync.Pool and reset between requests.

Chain control:

c.Next()              // advance to next handler in chain
c.Abort()             // stop chain (sets index to abortIndex)
c.AbortWithStatus(code int)
c.AbortWithStatusJSON(code int, jsonObj any)
c.AbortWithStatusPureJSON(code int, jsonObj any)
c.AbortWithError(code int, err error) *Error
c.IsAborted() bool

Request reading:

// Path parameters
c.Param(key string) string

// Query string
c.Query(key string) string
c.DefaultQuery(key, defaultValue string) string
c.GetQuery(key string) (string, bool)
c.QueryArray(key string) []string
c.QueryMap(key string) map[string]string

// POST form
c.PostForm(key string) string
c.DefaultPostForm(key, defaultValue string) string
c.PostFormArray(key string) []string
c.PostFormMap(key string) map[string]string

// Files
c.FormFile(name string) (*multipart.FileHeader, error)
c.MultipartForm() (*multipart.Form, error)

// Raw body
c.GetRawData() ([]byte, error)

// Headers / metadata
c.GetHeader(key string) string
c.ContentType() string
c.ClientIP() string
c.RemoteIP() string
c.IsWebsocket() bool
c.Cookie(name string) (string, error)

Binding (auto-detect from Content-Type + validation):

// Panics on error (returns 400 automatically)
c.Bind(obj any) error          // auto-detect binding
c.BindJSON(obj any) error
c.BindXML(obj any) error
c.BindQuery(obj any) error
c.BindYAML(obj any) error
c.BindTOML(obj any) error
c.BindPlain(obj any) error
c.BindHeader(obj any) error
c.BindUri(obj any) error

// Error-only (does NOT abort on failure)
c.ShouldBind(obj any) error
c.ShouldBindJSON(obj any) error
c.ShouldBindXML(obj any) error
c.ShouldBindQuery(obj any) error
c.ShouldBindYAML(obj any) error
c.ShouldBindTOML(obj any) error
c.ShouldBindPlain(obj any) error
c.ShouldBindHeader(obj any) error
c.ShouldBindUri(obj any) error
c.ShouldBindWith(obj any, b binding.Binding) error
c.ShouldBindBodyWith(obj any, bb binding.BindingBody) error  // caches body for re-use
c.ShouldBindBodyWithJSON/XML/YAML/TOML/Plain(obj any) error

Response writing:

// Structured responses
c.JSON(code int, obj any)
c.IndentedJSON(code int, obj any)
c.SecureJSON(code int, obj any)      // prevents JSON hijacking
c.PureJSON(code int, obj any)        // no HTML escaping
c.AsciiJSON(code int, obj any)       // non-ASCII characters escaped
c.JSONP(code int, obj any)
c.XML(code int, obj any)
c.YAML(code int, obj any)
c.TOML(code int, obj any)
c.ProtoBuf(code int, obj any)
c.BSON(code int, obj any)
c.PDF(code int, data []byte)

// HTML templates
c.HTML(code int, name string, obj any)

// Text/binary
c.String(code int, format string, values ...any)
c.Data(code int, contentType string, data []byte)
c.DataFromReader(code int, contentLength int64, contentType string, reader io.Reader, extraHeaders map[string]string)

// Files
c.File(filepath string)
c.FileFromFS(filepath string, fs http.FileSystem)
c.FileAttachment(filepath, filename string)   // Content-Disposition: attachment

// Streaming / SSE
c.SSEvent(name string, message any)
c.Stream(step func(w io.Writer) bool) bool

// Redirect
c.Redirect(code int, location string)

// Content negotiation
c.Negotiate(code int, config Negotiate)
c.NegotiateFormat(offered ...string) string

// Low-level
c.Render(code int, r render.Render)
c.Status(code int)
c.Header(key, value string)
c.SetCookie(name, value string, maxAge int, path, domain string, secure, httpOnly bool)
c.SetCookieData(cookie *http.Cookie)
c.SetSameSite(samesite http.SameSite)

Per-request key/value store (middleware communication channel):

c.Set(key any, value any)
c.Get(key any) (value any, exists bool)
c.MustGet(key any) any
c.Delete(key any)

// Typed getters (avoid type assertions in handlers)
c.GetString(key any) string
c.GetBool(key any) bool
c.GetInt(key any) int
c.GetInt64(key any) int64
c.GetFloat64(key any) float64
c.GetTime(key any) time.Time
c.GetDuration(key any) time.Duration
c.GetStringSlice(key any) []string
c.GetStringMap(key any) map[string]any
// ... and many more typed variants for all numeric types

Error accumulation:

c.Error(err error) *Error    // attach error to request; doesn't abort
// Retrieve in middleware: c.Errors (type ErrorMsgs)

context.Context implementation: *Context implements context.Context (Deadline, Done, Err, Value), delegating to c.Request.Context() when ContextWithFallback is enabled. This allows passing c directly to libraries that accept context.Context.

Miscellaneous:

c.Copy() *Context          // safe copy for use in goroutines
c.HandlerName() string     // name of current handler function
c.HandlerNames() []string  // all handler names in chain
c.FullPath() string        // matched route pattern (e.g. "/users/:id")
c.AddParam(key, value string)  // inject path param (testing/mocking)

Engine server API#

// HTTP/1.1
engine.Run(addr ...string) error               // wraps http.ListenAndServe
engine.RunTLS(addr, certFile, keyFile string) error
engine.RunUnix(file string) error
engine.RunFd(fd int) error
engine.RunListener(listener net.Listener) error

// HTTP/2
engine.Handler() http.Handler  // returns h2c-wrapped handler when UseH2C=true

// HTTP/3 (QUIC)
engine.RunQUIC(addr, certFile, keyFile string) error

// Programmatic dispatch (testing / forwarding)
engine.ServeHTTP(w http.ResponseWriter, req *http.Request)
engine.HandleContext(c *Context)   // re-dispatch a Context (useful for 404/405 rewrites)

Engine configuration API#

engine.SetTrustedProxies([]string{...}) error  // CIDR list or nil (trust all) or empty (trust none)
engine.NoRoute(handlers ...HandlerFunc)         // custom 404 handler
engine.NoMethod(handlers ...HandlerFunc)        // custom 405 handler
engine.Routes() RoutesInfo                      // enumerate registered routes
engine.With(opts ...OptionFunc) *Engine         // apply options post-construction

// HTML templates
engine.LoadHTMLGlob(pattern string)
engine.LoadHTMLFiles(files ...string)
engine.LoadHTMLFS(fs http.FileSystem, patterns ...string)
engine.SetHTMLTemplate(templ *template.Template)
engine.SetFuncMap(funcMap template.FuncMap)
engine.Delims(left, right string) *Engine
engine.SecureJsonPrefix(prefix string) *Engine

Global configuration API (package-level)#

gin.SetMode(value string)             // "debug" | "release" | "test" (or GIN_MODE env)
gin.Mode() string
gin.IsDebugging() bool
gin.DisableBindValidation()           // skip go-playground/validator
gin.EnableJsonDecoderUseNumber()      // json.Number instead of float64
gin.EnableJsonDecoderDisallowUnknownFields()
gin.DisableConsoleColor()
gin.ForceConsoleColor()

Static file API#

// Single file
group.StaticFile("/favicon.ico", "./resources/favicon.ico") IRoutes
group.StaticFileFS("/favicon.ico", "./resources/favicon.ico", fs) IRoutes

// Directory
group.Static("/static", "/var/www") IRoutes
group.StaticFS("/static", customFS) IRoutes

// Utility
gin.Dir(root string, listDirectory bool) http.FileSystem

Stdlib interop utilities#

gin.WrapF(f http.HandlerFunc) HandlerFunc   // adapt stdlib HandlerFunc for use in gin chain
gin.WrapH(h http.Handler) HandlerFunc       // adapt stdlib Handler for use in gin chain
gin.Bind(val any) HandlerFunc               // create a middleware that binds request into val

Testing API#

gin.CreateTestContext(w http.ResponseWriter) (c *Context, r *Engine)
gin.CreateTestContextOnly(w http.ResponseWriter, r *Engine) *Context

Both are in test_helpers.go. They construct a live Context and Engine attached to the provided http.ResponseWriter, enabling unit testing of handlers without starting a TCP server. Combined with httptest.NewRecorder(), this is the idiomatic testing pattern.


ginS — singleton wrapper#

github.com/gin-gonic/gin/ginS re-exports every route/middleware/server method as package-level functions backed by a lazily-initialized global gin.Default() engine (sync.OnceValue). This allows zero-setup script-style usage:

ginS.GET("/ping", func(c *gin.Context) { c.String(200, "pong") })
ginS.Run(":8080")

The singleton is intentionally unresettable — it exists for quick demos, not production code.


Binding subsystem (extensible)#

The binding package exposes its singleton instances as package-level variables, making them both directly callable and overridable:

binding.JSON          // BindingBody — application/json
binding.XML           // BindingBody — application/xml
binding.Form          // Binding    — form / query
binding.Query         // Binding    — query string only
binding.YAML          // BindingBody
binding.TOML          // BindingBody
binding.ProtoBuf      // BindingBody
binding.MsgPack       // BindingBody
binding.BSON          // BindingBody
binding.Header        // Binding    — HTTP headers
binding.Plain         // BindingBody — text/plain
binding.Uri           // BindingUri  — URL path parameters

binding.Validator     // StructValidator — replace to swap validation engine

Custom bindings implement binding.Binding (2 methods) or binding.BindingBody (3 methods) and are passed directly to c.ShouldBindWith(obj, myBinding).


Render subsystem (extensible)#

Any type implementing render.Render can be passed to c.Render(code, myRenderer):

type Render interface {
    Render(http.ResponseWriter) error
    WriteContentType(w http.ResponseWriter)
}

Built-in renders: JSON, IndentedJSON, SecureJSON, JsonpJSON, PureJSON, AsciiJSON, XML, HTML (debug/production variants), YAML, TOML, ProtoBuf, MsgPack, BSON, SSEvent, Reader, Data, String, Redirect, PDF.


API style#

Fluent chaining on registration: all route-registration methods return IRoutes, enabling:

engine.Use(Logger()).Use(Recovery()).GET("/ping", handler)

Flat surface on Context: rather than sub-objects (c.Response.JSON(...), c.Request.BindJSON(...)), all functionality is directly on *Context. This is intentional — one receiver, maximum discoverability. The tradeoff is a large method set (~80+ methods on *Context).

No generics in the public API. All binding/rendering uses any. Typed Get* accessors exist for primitive types but the key/value store itself is map[any]any. This is a pre-generics design that has not been updated; callers who want type safety write wrapper functions.


Backward compatibility#

Gin follows SemVer at v1 (go.mod: module github.com/gin-gonic/gin). The project maintains strict backward compatibility within v1: new middleware constructors and new Context methods are added, existing signatures are not changed. The OptionFunc construction pattern gives the engine a non-breaking extension point for new configuration options.

The ginS package exists partly as a compatibility shim: older Gin tutorials used the global API before gin.Default() became the idiomatic entry point.