A lightweight, opinionated Go web framework built on top of Gin. Octanox provides a structured approach to building REST APIs with automatic request parsing, authentication, serialization, and TypeScript client code generation.
go get github.com/sevenitynet/octanoxpackage main
import "github.com/sevenitynet/octanox"
type HelloRequest struct {
octanox.GetRequest
Name string `query:"name"`
}
type HelloResponse struct {
Message string `json:"message"`
}
func main() {
app := octanox.New()
app.RegisterPublic("/hello", func(req *HelloRequest) HelloResponse {
return HelloResponse{Message: "Hello, " + req.Name}
})
app.Run()
}- Automatic Request Parsing: Define request structs with tags for path params, query params, headers, and JSON body
- Built-in Authentication: Support for Bearer JWT, Basic Auth, API Key, and OAuth2/OIDC
- Response Serialization: Register custom serializers to transform responses
- TypeScript Code Generation: Automatically generate TypeScript client code for your API
- Lifecycle Hooks: Hook into init, before_start, start, shutdown, and after_shutdown events
- Graceful Shutdown: SIGTERM/SIGINT handling with configurable HTTP drain timeout
- Pluggable OAuth2 State Store: Interface-based state storage for multi-instance deployments
Octanox is organized into focused subpackages:
| Package | Description |
|---|---|
octanox |
Core framework - Instance, routing, authentication builder |
octanox/auth |
Authentication implementations (Bearer, Basic, API Key, OAuth2) |
octanox/ctx |
Context type for passing data through serializers |
octanox/model |
User interface definition |
octanox/request |
Request types (GetRequest, PostRequest, etc.) |
octanox/router |
SubRouter for route grouping |
octanox/middleware |
CORS, Logger, Recovery middleware |
octanox/serialize |
Serializer registry |
octanox/codegen |
TypeScript client code generation |
octanox/errors |
Error handling utilities |
octanox/hook |
Lifecycle hook constants |
Define request structs with tags to automatically parse incoming requests:
type CreateUserRequest struct {
octanox.PostRequest
User octanox.User `user:"true"` // Authenticated user (required)
ID string `path:"id"` // URL path parameter
Filter string `query:"filter"` // Query parameter
Token string `header:"X-Token"` // Header value
Body *CreateUserBody `body:"true"` // JSON body
}| Tag | Description |
|---|---|
path:"name" |
Extract from URL path parameter |
query:"name" |
Extract from query string |
header:"name" |
Extract from request header |
body:"true" |
Parse JSON body into field |
user:"true" |
Inject authenticated user (required) |
user:"optional" |
Inject authenticated user (optional) |
gin:"true" |
Inject raw *gin.Context |
optional:"true" |
Mark query/header as optional |
app := octanox.New()
app.Authenticate(&myUserProvider{}).Bearer("jwt-secret", "/auth")app.Authenticate(&myOAuth2Provider{}).
BearerOAuth2(
oauth2Endpoint,
[]string{"openid", "profile"},
clientID,
clientSecret,
"https://myapp.com",
"/dashboard",
"jwt-secret",
"/auth",
).
EnableOIDCValidation("https://issuer.example.com").
EnableCookieAuth("session", ".myapp.com", true)app.Authenticate(&myUserProvider{}).Basic()app.Authenticate(&myUserProvider{}).ApiKey()Register serializers to transform database entities into DTOs:
app.RegisterSerializer(db.User{}, func(u db.User, ctx octanox.Context) UserDTO {
return UserDTO{
ID: u.ID,
Name: u.Name,
}
})app.Hook(hook.Hook_Init, func(i *octanox.Instance) {
// Called when framework initializes
})
app.Hook(hook.Hook_BeforeStart, func(i *octanox.Instance) {
// Called before server starts (register routes here)
})
app.Hook(hook.Hook_Start, func(i *octanox.Instance) {
// Called when server starts
})
app.Hook(hook.Hook_Shutdown, func(i *octanox.Instance) {
// Called on SIGTERM/SIGINT, before HTTP drain (stop schedulers here)
})
app.Hook(hook.Hook_AfterShutdown, func(i *octanox.Instance) {
// Called after HTTP server has drained (close DB, flush telemetry here)
})Octanox handles SIGTERM and SIGINT signals automatically with a two-phase shutdown:
Hook_Shutdownfires — stop background work (schedulers, workers)- HTTP drain — in-flight requests complete within the timeout
Hook_AfterShutdownfires — close database connections, flush telemetry
The default drain timeout is 30 seconds. Configure it via:
app.SetShutdownTimeout(60 * time.Second)Or via environment variable:
NOX__SHUTDOWN_TIMEOUT=60 # secondsBy default, OAuth2 state (CSRF tokens, PKCE verifiers, nonces) is stored in memory. For multi-instance deployments, plug in an external store:
import "github.com/sevenitynet/octanox/auth"
// Using FuncStateStore adapter with Redis
store := auth.NewFuncStateStore(
func(ctx context.Context, key, value string, ttl time.Duration) error {
return redisClient.Set(ctx, key, value, ttl).Err()
},
func(ctx context.Context, key string) (string, error) {
val, err := redisClient.GetDel(ctx, key).Result()
if errors.Is(err, redis.Nil) { return "", nil }
return val, err
},
)
app.Authenticate(&myProvider{}).
BearerOAuth2(...).
SetStateStore(store)The OAuthStateStore interface requires only two methods:
type OAuthStateStore interface {
Set(ctx context.Context, key, value string, ttl time.Duration) error
Pop(ctx context.Context, key string) (string, error)
}Register error handlers for centralized error logging:
app.ErrorHandler(func(err error) {
sentry.CaptureException(err)
})In handlers, use Failed() to abort with a specific status:
func handler(req *MyRequest) Response {
if !valid {
req.Failed(400, "Invalid request")
}
return Response{}
}Set environment variables and run in dry-run mode:
NOX__DRY_RUN=true NOX__CLIENT_DIR=./frontend/src/api go run .This generates a TypeScript client with typed functions for all registered routes.
| Variable | Description |
|---|---|
NOX__CORS_ALLOWED_ORIGINS |
CORS allowed origins (* for all) |
NOX__DRY_RUN |
Enable dry-run mode for code generation |
NOX__CLIENT_DIR |
Output directory for generated TypeScript |
NOX__GEN_OMIT_URL |
URL prefix to omit from generated function names |
NOX__SHUTDOWN_TIMEOUT |
HTTP drain timeout in seconds (default: 30) |
PORT |
Listen port (default: 8080) |
import "github.com/sevenitynet/octanox"
fmt.Println(octanox.Version) // "1.1.0"MIT