Build Go APIs withArchitecture that Scales
Ginject brings NestJS-inspired dependency injection to Go. Module-based architecture, deterministic cancellation, zero goroutine leaks.
Latest: v1.0 — MIT License
// Just name your handlers — routing is automatic
func (c *UserController) READ(exec *ctx.ExecutionContext, q ctx.Query) []User { // GET /users
return c.UserService.FindAll(exec)
}
func (c *UserController) READ_BY_ID(exec *ctx.ExecutionContext, p ctx.Param) User { // GET /users/:id
return c.UserService.FindOne(exec, p.Get("id"))
}Everything you need
Production-grade primitives built into the framework
Dependency Injection
NestJS-inspired Module→Controller→Provider pattern with reflection-based auto-injection. No tags, no annotations.
Execution Context
Every request carries a cancellable context.Context with metadata and hierarchical SLA timeouts. WS connections get their own root context.
Zero Goroutine Leaks
GoSafe launcher checks ctx.Done() before spawning. All WS goroutines are lifecycle-safe and cancel when the connection closes.
WebSocket Native
WS controllers with event-based routing. Per-connection ExecutionContext, per-message SLA timeouts, and clean shutdown.
Pipeline Architecture
Composable middleware, guards, interceptors, and exception filters — global or per-route. Built-in CORS, Helmet, CSRF, RequestLogger.
Built-in Cache
Sharded in-memory LFU cache with TTL, SetNX atomic operations, Keys/TTL introspection, and a pluggable interface.
Request Pipeline
Every request flows through a deterministic, composable pipeline. Each layer can short-circuit, transform, or enrich the response.
Middleware Scope
- Global middlewares (app-level)
- Module middlewares (module-level)
- Built-in: CORS, Helmet, RequestLogger, CSRF
Handler Injection
*ctx.ExecutionContextctx.Body, ctx.Query, ctx.Paramctx.Header, ctx.Form, ctx.File
Aggregation Operators
- Transform — map response shape
- Tap — side-effects without mutation
- Timeout — per-route SLA enforcement
- Error — typed exception handling
context.Background()
├── HTTP: r.Context() (client disconnect → cancels all descendants)
│ └── exec (30s request SLA)
│ └── child (10s downstream SLA)
│ └── GoSafe goroutine
│
└── WS: context.Background()
└── ws.exec (connection lifetime)
└── msgExec (5s per-message SLA)
└── GoSafe goroutineFamiliar patterns, idiomatic Go
Method names become routes. Types become injected dependencies. No annotations, no code generation.
// user.controller."text-violet-400">go "text-violet-400">type UserController "text-violet-400">struct { common.REST UserService } "text-violet-400">func (c UserController) NewController() common.Controller { c.Prefix("users") // Bind middleware to specific handlers c.BindMiddleware(LogRequests, c.READ, c.READ_BY_ID) // Bind guard globally on this controller c.BindGuard(AuthGuard) "text-violet-400">return c } // GET /users "text-violet-400">func (c *UserController) READ( exec *ctx.ExecutionContext, query ctx.Query, ) []User { "text-violet-400">return c.UserService.FindAll(exec) } // GET /users/:id "text-violet-400">func (c *UserController) READ_BY_ID( exec *ctx.ExecutionContext, param ctx.Param, ) User { id := param.Get("id") "text-violet-400">return c.UserService.FindOne(exec, id) } // POST /users "text-violet-400">func (c *UserController) CREATE( exec *ctx.ExecutionContext, body ctx.Body, ) User { "text-violet-400">var dto CreateUserDTO body.Bind(&dto) "text-violet-400">return c.UserService.Create(exec, dto) } // DELETE /users/:id "text-violet-400">func (c *UserController) DELETE_BY_ID( exec *ctx.ExecutionContext, param ctx.Param, ) "text-blue-300">bool { id := param.Get("id") "text-violet-400">return c.UserService.Delete(exec, id) }