bast

package module
v0.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 6, 2026 License: MIT Imports: 19 Imported by: 0

README

Bast Logo

Bast

A structured Go framework for building efficient, scalable, and production server-side applications.

Go Reference CI Go version License Version


Philosophy

Explicit over implicit No magic. Every dependency is wired by hand.
Errors as values Handlers return Response. No side effects, no panic.
Structure at scale Module-based organization. Enforced by the framework.
Stdlib at the boundary Bast satisfies http.Handler. The full Go ecosystem works.
Lean core Near-zero dependencies. Batteries are opt-in companion packages.

Features

  • Radix tree router — zero allocations on the hot path, static-wins-over-param priority
  • Pooled *Ctxsync.Pool based, 0 allocs per request for routing and context
  • Module system — portable, self-contained units with dependency injection at main.go
  • Typed error boundary — one ErrorHandler, consistent JSON envelopes everywhere
  • Built-in middlewareRequestID, Logger, Recover, CORS
  • Guards — pre-handler checks with SecuredGuard → OpenAPI security scheme auto-wiring
  • Streaming*StreamCtx for SSE and chunked responses, never pooled
  • Health checks/health (liveness) and /ready (readiness) with dependency checks
  • OpenAPI 3.0 — spec generated from code at startup; Swagger UI at /docs
  • Typed env configLoadConfig[T]() with env, default, required, secret tags
  • Logger — colored [Bast] boot and request logs, fully pluggable
  • bast new CLI — scaffold a working Todo API in seconds
  • basttest — unit and integration test helpers built into the framework

Quick Start

# Install the CLI
go install github.com/bastion-framework/bast/cmd/bast@latest

# Scaffold a new project (generates a working Todo API)
bast new myapp
cd myapp

# Wire the local framework and run
go mod tidy
go run .

Open http://localhost:8080/docs for the Swagger UI.


Installation

go get github.com/bastion-framework/bast@latest

Requires Go 1.22+.


CLI

bast new <appname>                  # Scaffold a new project
bast generate module <name>         # Generate a module (5 files)
bast generate guard  <name>         # Generate a guard
bast generate service <name>        # Generate a shared service
bast run                            # Run the app
bast run --watch                    # Run + rebuild/restart on file changes
bast build                          # Production binary → bin/<app>
bast build --os linux --arch arm64  # Cross-compile

bast build produces deployment-ready binaries: reproducible (-trimpath), stripped (-s -w), statically linked (CGO_ENABLED=0) — runs in scratch and distroless containers.


Testing

basttest ships with the framework — no separate install needed.

// Unit test — handler as a pure function
func TestGetUser(t *testing.T) {
    ctx := basttest.NewCtx(
        basttest.WithParam("id", "42"),
        basttest.WithStore("claims", &Claims{Role: "admin"}),
    )
    resp := controller.GetUser(ctx)
    assert.Equal(t, 200, resp.Status())
}

// Integration test — full request lifecycle
func TestCreateUser(t *testing.T) {
    app := basttest.NewApp(users.NewModule(testDB))
    app.Do("POST", "/users",
        basttest.WithJSONBody(CreateUserRequest{Name: "Kasim", Email: "k@suds.ug"}),
    ).Assert(t).StatusIs(201).BodyContains("id")
}

Benchmarks

Intel i7-9700K @ 3.60 GHz, Go 1.25, windows/amd64. Three benchmark tiers — run them all with:

cd bench && go test -bench=. -benchmem -benchtime=5s

Tier 1 — Router lookup only (no HTTP stack)

Measures pure route-matching time by calling the router's lookup API directly. Bast's flat-arena BFS layout outperforms httprouter on non-trivial routes — and never allocates for path parameters.

Benchmark bast httprouter
Static GET /ping 13 ns · 0 allocs 12 ns · 0 allocs
Param GET /users/:id 22 ns · 0 allocs 48 ns · 1 alloc
GitHub corpus (26 routes, 8 requests) 32 ns · 0 allocs 59 ns · 0 allocs

Tier 2 — Fair comparison (all frameworks, minimum-work handler)

Every framework returns status 200 with no body and no extra headers — the same workload gin and echo use in their own published benchmarks. This isolates routing + dispatch from response-writing.

GitHub API corpus
Framework ns/op allocs/op
gin 60 0
httprouter 67 0
echo 84 0
bast 139 0
chi 526 3
gorilla/mux 1 618 7
Static route — GET /ping
Framework ns/op allocs/op
httprouter 20 0
gin 37 0
echo 38 0
bast 110 0
chi 284 2
gorilla/mux 656 7

Tier 3 — Full framework stack

Bast uses a realistic handler (ctx.OK(nil)) that writes a proper JSON envelope with Content-Type: application/json. Other frameworks still use their minimum-work handler.

Benchmark bast gin echo httprouter iris stdlib chi gorilla/mux
GitHub corpus 219 ns · 0 allocs 59 83 66 178 298 519 1 548
Static 177 ns · 0 allocs 37 42 20 83 92 284 668
Param 190 ns · 0 allocs 46 47 56 146 182 331 874

Fiber (fasthttp) is excluded — its app.Test() harness pipes a full HTTP/1.1 message in-process (~7 µs overhead absent in production).


Contributing

Contributions are welcome. Please read CONTRIBUTING.md before opening a pull request.


License

Bast is MIT licensed.


Author Kasim Lyee

Documentation

Index

Constants

View Source
const (
	CodeBadRequest         = "BAD_REQUEST"
	CodeInvalidBody        = "INVALID_BODY"
	CodeUnauthorized       = "UNAUTHORIZED"
	CodeForbidden          = "FORBIDDEN"
	CodeNotFound           = "NOT_FOUND"
	CodeMethodNotAllowed   = "METHOD_NOT_ALLOWED"
	CodeTimeout            = "REQUEST_TIMEOUT"
	CodeConflict           = "CONFLICT"
	CodePayloadTooLarge    = "PAYLOAD_TOO_LARGE"
	CodeUnprocessable      = "UNPROCESSABLE_ENTITY"
	CodeValidation         = "VALIDATION_FAILED"
	CodeTooManyRequests    = "TOO_MANY_REQUESTS"
	CodeInternal           = "INTERNAL_ERROR"
	CodeNotImplemented     = "NOT_IMPLEMENTED"
	CodeServiceUnavailable = "SERVICE_UNAVAILABLE"
	CodeGatewayTimeout     = "GATEWAY_TIMEOUT"
)

Variables

This section is empty.

Functions

func BenchReleaseCtx

func BenchReleaseCtx(c *Ctx)

func ErrBadRequest

func ErrBadRequest(code, message string) error

func ErrConflict

func ErrConflict(code, message string) error

func ErrForbidden

func ErrForbidden(code, message string) error

func ErrGatewayTimeout

func ErrGatewayTimeout(message string) error

func ErrInternal

func ErrInternal(code, message string) error

func ErrInvalidBody

func ErrInvalidBody(message string) error

func ErrMethodNotAllowed

func ErrMethodNotAllowed(message string) error

func ErrNotFound

func ErrNotFound(code, message string) error

func ErrNotImplemented

func ErrNotImplemented(message string) error

func ErrPayloadTooLarge

func ErrPayloadTooLarge(message string) error

func ErrServiceUnavailable

func ErrServiceUnavailable(message string) error

func ErrTimeout

func ErrTimeout(message string) error

func ErrTooManyRequests

func ErrTooManyRequests(message string) error

func ErrUnauthorized

func ErrUnauthorized(code, message string) error

func ErrUnprocessable

func ErrUnprocessable(code, message string) error

func Get

func Get[T any](ctx *Ctx, key string) (T, bool)

Get returns a typed value from the Ctx store. No type assertion needed.

func InitTestCtx

func InitTestCtx(c *Ctx, w http.ResponseWriter, r *http.Request)

InitTestCtx wires a writer and request onto a test Ctx. For basttest only.

func LoadConfig

func LoadConfig[T any]() (T, error)

LoadConfig loads and validates configuration from environment variables. T must be a struct. Reflection runs once at startup — never on the hot path.

Supported tags:

env:"VAR_NAME"    — environment variable name
default:"value"   — used when env var is absent
required:"true"   — app refuses to start if absent and no default
secret:"true"     — value is masked in boot logs

Supported field types: string, int, int64, float64, bool, time.Duration, []string Nested structs: env tag on the struct field acts as a prefix (e.g. env:"DB" → DB_URL).

func MustGet

func MustGet[T any](ctx *Ctx, key string) T

MustGet returns a typed value and panics if missing or wrong type.

func SetStreamTestParam added in v0.2.0

func SetStreamTestParam(s *StreamCtx, key, value string)

SetStreamTestParam sets a route path parameter on a test StreamCtx. For basttest only.

func SetStreamTestStore added in v0.2.0

func SetStreamTestStore(s *StreamCtx, key string, val any)

SetStreamTestStore sets a value in the store of a test StreamCtx. For basttest only.

func SetTestBody

func SetTestBody(c *Ctx, b []byte)

SetTestBody pre-loads the body buffer on a test Ctx so readBody() is skipped. For basttest only.

func SetTestParam

func SetTestParam(c *Ctx, key, value string)

SetTestParam sets a route param on a test Ctx. For basttest only.

Types

type App

type App struct {
	// contains filtered or unexported fields
}

App is the Bast application. Satisfies http.Handler.

func New

func New(cfg Config) *App

New creates a new Bast app with the given config.

func (*App) Guard

func (a *App) Guard(guards ...Guard) *App

Guard registers global guards. Runs for every request before the handler.

func (*App) Listen

func (a *App) Listen() error

Listen starts the HTTP server on the configured port. Uses net.Listen so OnReady fires after the port is bound, before first connections.

func (*App) LivenessHandler

func (a *App) LivenessHandler() HandlerFunc

LivenessHandler returns a HandlerFunc for the liveness probe. Always returns 200 as long as the process is alive — never checks dependencies.

func (*App) OpenAPISpec

func (a *App) OpenAPISpec() map[string]any

OpenAPISpec returns the built OpenAPI spec, or nil if docs are not configured. The spec is built once when this method is first called — safe to call multiple times.

func (*App) ReadinessHandler

func (a *App) ReadinessHandler() HandlerFunc

ReadinessHandler returns a HandlerFunc for the readiness probe. Returns 200 if all checks pass, 503 if any check fails.

func (*App) Register

func (a *App) Register(modules ...Module) *App

Register mounts one or more modules onto the app.

func (*App) ServeHTTP

func (a *App) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP satisfies http.Handler.

func (*App) Shutdown

func (a *App) Shutdown(ctx context.Context) error

Shutdown gracefully shuts down the server. If ctx carries no deadline, Config.ShutdownTimeout is applied.

func (*App) Use

func (a *App) Use(mw ...MiddlewareFunc) *App

Use registers global middleware. Runs for every request.

type BastError

type BastError struct {
	Status  int    `json:"-"`
	Code    string `json:"code"`
	Message string `json:"message"`
}

BastError is the typed error Bast understands. Use the constructors.

func (*BastError) Error

func (e *BastError) Error() string

func (*BastError) JSON

func (e *BastError) JSON() []byte

JSON serializes BastError into the standard error envelope.

type BodySchema

type BodySchema struct {
	Example any
}

BodySchema holds a reflected type for OpenAPI schema generation. Populated via Body[T]() — reflection runs once at startup, never on hot path.

func Body

func Body[T any]() *BodySchema

Body captures a type for OpenAPI schema generation. Reflection runs once at startup — zero cost at request time.

type Config

type Config struct {
	Port int

	// ReadTimeout / WriteTimeout have no default: a default WriteTimeout would
	// kill long-lived SSE streams and a default ReadTimeout would kill slow
	// uploads. Set them if your app serves neither. Use HandlerTimeout for
	// per-request protection instead.
	ReadTimeout  time.Duration
	WriteTimeout time.Duration

	// IdleTimeout defaults to 120s; ReadHeaderTimeout defaults to 10s
	// (slowloris protection). Set to -1 to disable.
	IdleTimeout       time.Duration
	ReadHeaderTimeout time.Duration

	MaxBodySize int64

	// HandlerTimeout is the global per-request deadline applied to every
	// non-stream route without an explicit route-level Timeout. Handlers see it
	// via ctx.Context() cancellation. 0 means no global deadline.
	HandlerTimeout time.Duration

	// ShutdownTimeout bounds Shutdown when the caller's context has no
	// deadline of its own. Defaults to 30s.
	ShutdownTimeout time.Duration

	HookTimeout    time.Duration
	TrustedProxies []string
	ErrorHandler   ErrorHandler
	Validator      Validator
	Health         *HealthConfig
	Docs           *DocsConfig
	Logger         Logger
}

Config holds app-level settings.

Timeout semantics: 0 means "use the safe default" where one exists, a negative value disables the timeout entirely.

type Controller

type Controller interface {
	Routes() []Route
}

Controller is the interface every controller must satisfy. Routes() declares the handler mappings. That is the only contract.

type Ctx

type Ctx struct {
	Request *http.Request
	// contains filtered or unexported fields
}

Ctx is the request context passed to every Bast handler. Pooled via sync.Pool — reset and reused after every request.

CRITICAL: *Ctx does NOT implement context.Context intentionally. This means you cannot pass *Ctx where context.Context is expected. The compiler enforces correct usage, always call ctx.Context() for passing to services, goroutines, or DB calls.

func BenchAcquireCtx

func BenchAcquireCtx(w http.ResponseWriter, r *http.Request) *Ctx

BenchAcquireCtx and BenchReleaseCtx expose the pool for benchmark tests.

func NewTestCtx

func NewTestCtx() *Ctx

NewTestCtx is the exported form for basttest only.

func NewTestCtxWithPath

func NewTestCtxWithPath(path string) *Ctx

NewTestCtxWithPath builds a test Ctx with a specific URL path set.

func (*Ctx) Bind

func (c *Ctx) Bind(v any) error

Bind decodes and validates the request body into v. Returns a 400 BastError on malformed JSON, or a ValidationError on failed struct validation — both flow cleanly through the error boundary.

func (*Ctx) BindForm

func (c *Ctx) BindForm(v any) error

BindForm binds a url-encoded or multipart form into a struct pointer. Fields are matched by their `form:"name"` tag, falling back to the field name. A tag of "-" skips the field. Uses reflection — never on the hot path.

Supported field types: string, the signed/unsigned integer kinds, float32/64, bool, time.Duration, and []string (repeated form values). Invalid input yields a 400 BastError so it flows through the error boundary. Values populated by FormValue's r.Form (query + POST body) are used; multipart file parts are read via File/Files, not here.

func (*Ctx) BindJSON

func (c *Ctx) BindJSON(v any) error

BindJSON decodes JSON body into v without running validation. Returns a 400 BastError on malformed JSON so the error boundary produces a proper Bad Request response instead of a 500.

func (*Ctx) Context

func (c *Ctx) Context() context.Context

Context returns a detached context.Context safe to pass anywhere.

func (*Ctx) Cookie

func (c *Ctx) Cookie(name string) (*http.Cookie, error)

Cookie retrieves a named cookie from the request.

func (*Ctx) Cookies

func (c *Ctx) Cookies() []*http.Cookie

Cookies returns all cookies from the request.

func (*Ctx) Created

func (c *Ctx) Created(data any) Response

Created returns a 201 response with a JSON envelope.

func (*Ctx) Error

func (c *Ctx) Error(err error) Response

Error returns a response carrying an error for the boundary.

func (*Ctx) File

func (c *Ctx) File(field string) (*multipart.FileHeader, error)

File retrieves a single uploaded file by field name.

func (*Ctx) Files

func (c *Ctx) Files(field string) ([]*multipart.FileHeader, error)

Files retrieves multiple uploaded files from one field.

func (*Ctx) FormValue

func (c *Ctx) FormValue(key string) string

FormValue reads a form field (multipart or url-encoded).

func (*Ctx) Get

func (c *Ctx) Get(key string) (any, bool)

Get retrieves a value from the request-scoped store.

func (*Ctx) Header

func (c *Ctx) Header(key string) string

Header returns a request header value by name.

func (*Ctx) IP

func (c *Ctx) IP() string

IP returns the real client IP, respecting X-Forwarded-For and X-Real-IP only when the request comes from a trusted proxy.

X-Forwarded-For is walked right to left: each proxy appends the address it saw, so the rightmost entry NOT itself a trusted proxy is the real client. The leftmost entries are attacker-controlled — a client can send a forged XFF header and a trusted proxy will simply append the truth after it.

func (*Ctx) Method

func (c *Ctx) Method() string

Method returns the HTTP method of the request.

func (*Ctx) MustGet

func (c *Ctx) MustGet(key string) any

MustGet retrieves a value and panics if not found. Use only when a guard guarantees the value was set.

func (*Ctx) NoContent

func (c *Ctx) NoContent() Response

NoContent returns a 204 response with no body.

func (*Ctx) OK

func (c *Ctx) OK(data any) Response

OK returns a 200 response with a JSON envelope.

func (*Ctx) Paginated

func (c *Ctx) Paginated(data any, meta PaginationMeta) Response

Paginated returns a 200 response with data and pagination metadata.

func (*Ctx) Param

func (c *Ctx) Param(key string) string

Param returns a URL path parameter by name.

func (*Ctx) Path

func (c *Ctx) Path() string

Path returns the request URL path.

func (*Ctx) Query

func (c *Ctx) Query(key string) string

Query returns a URL query parameter by name.

func (*Ctx) QueryDefault

func (c *Ctx) QueryDefault(key, fallback string) string

QueryDefault returns a query param or a fallback if missing.

func (*Ctx) Raw

func (c *Ctx) Raw(status int, contentType string, body []byte) Response

Raw returns a response with a raw body. Used for escape-hatch streaming.

func (*Ctx) RawBody

func (c *Ctx) RawBody() ([]byte, error)

RawBody reads and returns the request body bytes. Safe to call multiple times. The slice is backed by a pooled buffer: valid until the handler returns, copy it if you need to retain it longer.

func (*Ctx) Redirect

func (c *Ctx) Redirect(url string, code int) Response

Redirect returns a redirect response.

func (*Ctx) Set

func (c *Ctx) Set(key string, val any)

Set stores a value in the request-scoped store.

func (*Ctx) WithCookie

func (c *Ctx) WithCookie(cookie *http.Cookie) Response

WithCookie chains a cookie onto a response.

func (*Ctx) WithValue

func (c *Ctx) WithValue(key, val any) *Ctx

WithValue returns a copy of Ctx with a value injected into its context. The copy is strictly request-scoped: it shares the pooled body buffer and Request with the original, so it must never outlive the handler.

type Doc

type Doc struct {
	Summary       string
	Description   string
	Tags          []string
	Params        []Param
	Body          *BodySchema
	Returns       Returns
	Deprecated    bool
	DeprecatedMsg string
}

Doc holds human-readable documentation for a route, used to build OpenAPI specs.

type DocsConfig

type DocsConfig struct {
	Enabled     bool
	Path        string // Swagger UI path, e.g. "/docs"
	JSONPath    string // Raw OpenAPI JSON path, e.g. "/openapi.json"
	Title       string
	Version     string
	Description string

	// AssetsBaseURL is where the Swagger UI css/js load from. Defaults to the
	// unpkg CDN; point it at a self-hosted copy (e.g. "/static/swagger") for
	// air-gapped or supply-chain-sensitive deployments.
	AssetsBaseURL string
}

DocsConfig configures the built-in OpenAPI documentation endpoints.

type ErrorHandler

type ErrorHandler func(ctx *Ctx, err error) Response

ErrorHandler maps an error to a Response.

type Guard

type Guard interface {
	Check(ctx *Ctx) error
}

Guard is a pre-handler check. Return nil to allow, return an error to block. Guards run before the handler and short-circuit on the first non-nil error.

type GuardFunc

type GuardFunc func(ctx *Ctx) error

GuardFunc is a function adapter for Guard.

func (GuardFunc) Check

func (f GuardFunc) Check(ctx *Ctx) error

type HandlerFunc

type HandlerFunc func(ctx *Ctx) Response

HandlerFunc is the signature every Bast handler must have. No variadic args, no interface{}, no side effects.

type HealthCheck

type HealthCheck struct {
	Name string
	// contains filtered or unexported fields
}

HealthCheck is a named dependency check run on the readiness endpoint.

func CustomCheck

func CustomCheck(name string, fn func(ctx context.Context) error) HealthCheck

CustomCheck builds a HealthCheck from a name and a check function.

type HealthConfig

type HealthConfig struct {
	LivePath  string        // liveness probe path, e.g. "/health"
	ReadyPath string        // readiness probe path, e.g. "/ready"
	Checks    []HealthCheck // dependency checks run on /ready
}

HealthConfig configures the built-in health check endpoints.

type Hooks

type Hooks interface {
	OnInit(ctx context.Context) error
	OnReady()
	OnShutdown(ctx context.Context) error
}

Hooks is an optional interface modules may implement to hook into the app lifecycle.

type Logger

type Logger interface {
	OnBoot(version string)
	OnModuleRegistered(name, prefix string)
	OnRouteRegistered(method, path string, guards []string)
	OnServiceExported(serviceType string, fromModule string)
	OnServiceResolved(serviceType string, toModule string)
	OnListening(port int)
	OnShutdown()
	OnRequest(method, path string, status int, dur time.Duration, ip string)
	OnError(ctx *Ctx, err error)
	Info(msg string, args ...any)
	Warn(msg string, args ...any)
	Error(msg string, args ...any)
	Debug(msg string, args ...any)
}

Logger is the pluggable logging interface for the Bast framework. The default implementation produces colored output to stdout. Swap it out via Config.Logger for Zap, Zerolog, or any other backend.

func NewDefaultLogger

func NewDefaultLogger() Logger

NewDefaultLogger returns the default Bast logger writing to stdout.

type MiddlewareFunc

type MiddlewareFunc func(next HandlerFunc) HandlerFunc

MiddlewareFunc wraps a HandlerFunc to form a pipeline. Call next(ctx) to pass control to the next middleware or handler. Do not call next to short-circuit (e.g. auth failure) — return a Response directly.

type Module

type Module struct {
	// Prefix is the URL prefix for all routes in this module.
	Prefix string

	// Controller declares this module's routes.
	Controller Controller

	// Middleware scoped to every route in this module.
	// Runs after global middleware, before route-level middleware.
	Middleware []MiddlewareFunc

	// Guards scoped to every route in this module.
	// Runs after global guards, before route-level guards.
	Guards []Guard

	// Nested sub-modules. Inherits parent prefix.
	Modules []Module

	// Doc metadata for Swagger tag group.
	Doc ModuleDoc
}

Module is a portable, self-contained unit of functionality. It owns its routes, guards, middleware, and optionally sub-modules.

type ModuleDoc

type ModuleDoc struct {
	Name        string
	Description string
}

ModuleDoc holds human-readable metadata for OpenAPI tag groups.

type PaginationMeta

type PaginationMeta struct {
	Page    int `json:"page"`
	PerPage int `json:"per_page"`
	Total   int `json:"total"`
	Pages   int `json:"pages"`
}

PaginationMeta holds standard pagination information.

type Param

type Param struct {
	In          string // "path", "query", "header"
	Name        string
	Description string
	Required    bool
}

Param describes a single route parameter for OpenAPI docs.

func PathParam

func PathParam(name, description string) Param

PathParam constructs a path parameter descriptor.

func QueryParam

func QueryParam(name, description string) Param

QueryParam constructs a query parameter descriptor.

type Response

type Response struct {
	// contains filtered or unexported fields
}

Response is an immutable value representing what the framework should write. Constructed via Ctx methods — never directly instantiated by user code. All methods use value receivers and return new Response values — never mutate.

Internal constructors below are used by Ctx methods and basttest.

func DefaultErrorHandler

func DefaultErrorHandler(ctx *Ctx, err error) Response

DefaultErrorHandler is the built-in error boundary. Maps BastError and ValidationError to structured JSON responses. Unknown errors become 500 without leaking internals.

func NewErrorResponse

func NewErrorResponse(err error) Response

NewErrorResponse is the exported form used by basttest and response tests.

func NewRawResponse

func NewRawResponse(status int, contentType string, body []byte) Response

NewRawResponse is the exported form used by basttest and response tests.

func (Response) Body

func (r Response) Body() []byte

Body returns the raw response body bytes.

func (Response) ContentType

func (r Response) ContentType() string

ContentType returns the Content-Type header value.

func (Response) Cookies

func (r Response) Cookies() []*http.Cookie

Cookies returns cookies to be set on the response.

func (Response) Err

func (r Response) Err() error

Err returns the underlying error, if any.

func (Response) Headers

func (r Response) Headers() map[string]string

Headers returns the response headers as a map. Built lazily — not called on the hot path. Returns nil when no headers have been set.

func (Response) IsError

func (r Response) IsError() bool

IsError reports whether this response carries an error.

func (Response) Redirect

func (r Response) Redirect() string

Redirect returns the redirect URL, if any.

func (Response) Status

func (r Response) Status() int

Status returns the HTTP status code.

func (Response) WithCookie

func (r Response) WithCookie(cookie *http.Cookie) Response

WithCookie returns a new Response with an additional cookie attached.

func (Response) WithHeader

func (r Response) WithHeader(key, value string) Response

WithHeader returns a new Response with an additional header set. Allocates a new backing slice of exactly len+1 — no capacity aliasing possible. Strips \r and \n from both key and value to prevent CRLF injection.

func (Response) WithStatus

func (r Response) WithStatus(code int) Response

WithStatus returns a new Response with a different status code.

type Returns

type Returns map[int]*BodySchema

Returns maps HTTP status codes to response body schemas.

type Route

type Route struct {
	Method      string
	Pattern     string
	Handler     HandlerFunc
	Stream      StreamHandlerFunc // non-nil when this is a streaming route
	Middleware  []MiddlewareFunc
	Guards      []Guard
	Timeout     time.Duration
	MaxBodySize int64
	Doc         Doc
}

Route binds an HTTP method + path pattern to a handler.

func DELETE

func DELETE(pattern string, handler HandlerFunc, opts ...RouteOption) Route

DELETE constructs a DELETE route.

func GET

func GET(pattern string, handler HandlerFunc, opts ...RouteOption) Route

GET constructs a GET route.

func PATCH

func PATCH(pattern string, handler HandlerFunc, opts ...RouteOption) Route

PATCH constructs a PATCH route.

func POST

func POST(pattern string, handler HandlerFunc, opts ...RouteOption) Route

POST constructs a POST route.

func PUT

func PUT(pattern string, handler HandlerFunc, opts ...RouteOption) Route

PUT constructs a PUT route.

func STREAM

func STREAM(pattern string, handler StreamHandlerFunc, opts ...RouteOption) Route

STREAM constructs a streaming route. The handler owns the connection for its lifetime.

type RouteOption

type RouteOption func(*Route)

RouteOption applies optional config to a route at construction time.

func WithDoc

func WithDoc(doc Doc) RouteOption

WithDoc attaches documentation to a route.

func WithGuards

func WithGuards(guards ...Guard) RouteOption

WithGuards attaches guards to a specific route.

func WithMaxBody

func WithMaxBody(n int64) RouteOption

WithMaxBody sets a per-route request body size limit in bytes.

func WithMiddleware

func WithMiddleware(mw ...MiddlewareFunc) RouteOption

WithMiddleware attaches middleware to a specific route.

func WithTimeout

func WithTimeout(d time.Duration) RouteOption

WithTimeout sets a per-route handler timeout.

type SecuredGuard

type SecuredGuard interface {
	Guard
	SecurityScheme() SecurityScheme
}

SecuredGuard is a Guard that also declares its OpenAPI security scheme. Bast uses this to populate securitySchemes in the OpenAPI spec automatically.

type SecurityScheme

type SecurityScheme struct {
	Type         string // "http", "apiKey", "oauth2"
	Scheme       string // "bearer", "basic"
	BearerFormat string // "JWT"
	Description  string
}

SecurityScheme describes an OpenAPI security scheme declared by a guard.

type StreamCtx

type StreamCtx struct {
	context.Context
	Request *http.Request
	// contains filtered or unexported fields
}

StreamCtx is the context for streaming handlers.

Unlike *Ctx it is intentionally NOT pooled. Stream connections are long-lived (seconds to hours), so their lifetime is unbounded and cannot be managed by a sync.Pool: the pool would either hold the slot indefinitely (defeating the pool) or return the object while the connection is still active (use-after-free). One allocation per connection is negligible compared to the cost of keeping the connection open. GC handles reclamation naturally when Done fires and the handler returns.

It embeds context.Context directly — safe to pass anywhere, for any duration.

func NewTestStreamCtx added in v0.2.0

func NewTestStreamCtx() *StreamCtx

NewTestStreamCtx creates a StreamCtx outside the pool for unit testing stream handlers. Never pool or reuse a test StreamCtx.

func (*StreamCtx) Closed

func (s *StreamCtx) Closed() <-chan struct{}

Closed returns a channel that is closed when the client disconnects. Equivalent to StreamCtx.Done() via the embedded context.

func (*StreamCtx) Flush

func (s *StreamCtx) Flush() error

Flush flushes buffered data to the client immediately. Returns an error if the underlying write fails (e.g. client disconnected).

func (*StreamCtx) Get added in v0.2.0

func (s *StreamCtx) Get(key string) (any, bool)

Get retrieves a value from the request-scoped store. Values are populated by guards that ran before the stream handler.

func (*StreamCtx) MustGet added in v0.2.0

func (s *StreamCtx) MustGet(key string) any

MustGet retrieves a value and panics if not found. Use only when a guard guarantees the value was set.

func (*StreamCtx) Param added in v0.2.0

func (s *StreamCtx) Param(key string) string

Param returns a URL path parameter by name. e.g. STREAM("/:id/events", ...) → sctx.Param("id") == "42" for /42/events

func (*StreamCtx) Send

func (s *StreamCtx) Send(event, data string) error

Send writes a Server-Sent Event to the client.

The event name has CR/LF stripped and multi-line data is emitted as one "data:" line per payload line (EventSource rejoins them with \n), so user-supplied text can never forge extra SSE frames.

func (*StreamCtx) Set added in v0.2.0

func (s *StreamCtx) Set(key string, val any)

Set stores a value in the request-scoped store.

func (*StreamCtx) SetHeader

func (s *StreamCtx) SetHeader(key, value string)

SetHeader sets a response header. Must be called before first Write or Send.

func (*StreamCtx) Status added in v0.4.0

func (s *StreamCtx) Status(code int)

Status sends the status line immediately. Call before any Write, Send, or Flush; once data is on the wire the status is fixed at 200 and Status is a no-op.

func (*StreamCtx) Write

func (s *StreamCtx) Write(p []byte) (int, error)

Write writes raw bytes to the client.

type StreamHandlerFunc

type StreamHandlerFunc func(ctx *StreamCtx)

StreamHandlerFunc is the signature for streaming handlers. Does not return a Response, the handler owns the wire for its full duration.

type ValidationError

type ValidationError struct {
	Fields map[string]string
}

ValidationError is returned by ctx.Bind() when struct validation fails. The error boundary detects it and produces field-level 422 responses automatically.

func (*ValidationError) BastStatus

func (e *ValidationError) BastStatus() int

BastStatus satisfies a convention checked by the error boundary.

func (*ValidationError) Error

func (e *ValidationError) Error() string

func (*ValidationError) JSON

func (e *ValidationError) JSON() []byte

JSON serializes ValidationError into the validation error envelope.

type Validator

type Validator interface {
	Validate(v any) error
}

Validator is a pluggable struct validation interface.

Directories

Path Synopsis
Package basttest provides test helpers for Bast handlers and apps.
Package basttest provides test helpers for Bast handlers and apps.
cmd
bast command
internal
jsonx
Package jsonx is the single seam between bast and its JSON backend.
Package jsonx is the single seam between bast and its JSON backend.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL