Skip to content

SevenityNet/octanox

Repository files navigation

Octanox

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.

Installation

go get github.com/sevenitynet/octanox

Quick Start

package 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()
}

Features

  • 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

Package Structure

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

Request Handling

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
}

Available Tags

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

Authentication

Bearer JWT

app := octanox.New()

app.Authenticate(&myUserProvider{}).Bearer("jwt-secret", "/auth")

OAuth2/OIDC

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)

Basic Auth

app.Authenticate(&myUserProvider{}).Basic()

API Key

app.Authenticate(&myUserProvider{}).ApiKey()

Serialization

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,
    }
})

Lifecycle Hooks

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)
})

Graceful Shutdown

Octanox handles SIGTERM and SIGINT signals automatically with a two-phase shutdown:

  1. Hook_Shutdown fires — stop background work (schedulers, workers)
  2. HTTP drain — in-flight requests complete within the timeout
  3. Hook_AfterShutdown fires — 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  # seconds

OAuth2 State Store

By 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)
}

Error Handling

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{}
}

TypeScript Code Generation

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.

Environment Variables

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)

Version

import "github.com/sevenitynet/octanox"

fmt.Println(octanox.Version) // "1.1.0"

License

MIT

About

Octanox is a fullstack web API framework leveraging the Gin framework.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages