Documentation
¶
Overview ¶
Package server is the thin `net/http` wrapper that craftgo's generated routes register against. It owns a `*http.ServeMux`, a middleware stack, configurable JSON codec / logger, default per-method limits, and the `Start`/`Stop` lifecycle.
Index ¶
- Constants
- Variables
- func AcceptsEncoding(r *http.Request, coding string) bool
- func BindValue[T any](w http.ResponseWriter, r *http.Request, field, kind, raw string, dst *T, ...) bool
- func BindValuePtr[T any](w http.ResponseWriter, r *http.Request, field, kind, raw string, dst **T, ...) bool
- func BindValues[T any](w http.ResponseWriter, r *http.Request, field, kind string, raw []string, ...) bool
- func CookiePresent(r *http.Request, name string) bool
- func ParseBool[T ~bool](s string) (T, error)
- func ParseFloat[T wireFloat](s string) (T, error)
- func ParseSigned[T wireSigned](s string) (T, error)
- func ParseUnsigned[T wireUnsigned](s string) (T, error)
- func RequirePresent(w http.ResponseWriter, r *http.Request, present bool, field, kind string) bool
- func SetDefaultValidationFailed(h ValidationFailedHandler)
- func SetGlobalJSONCodec(c JSONCodec) error
- func SetHandleUnknownError(h UnknownErrorHandler)
- func SetStrictJSON(strict bool) error
- func TrailingData(buffered, rest io.Reader) error
- func WithLimits(h http.Handler, l Limits) http.Handler
- func WriteBytes(w http.ResponseWriter, status int, contentType string, body []byte) error
- func WriteError(w http.ResponseWriter, r *http.Request, err error)
- func WritePrecompressed(w http.ResponseWriter, r *http.Request, status int, contentType, coding string, ...) error
- func WriteValidationError(w http.ResponseWriter, r *http.Request, err error)
- type AccessLogOption
- type CORSOptions
- type Chain
- type CompressOptions
- type DocsOptions
- type DocsUI
- type HealthPaths
- type JSONCodec
- type Limits
- type Logger
- type Middleware
- type Option
- type ResponseHeaderWriter
- type Server
- func (s *Server) Codec() JSONCodec
- func (s *Server) Handle(pattern string, h http.Handler, mws ...Middleware) *Server
- func (s *Server) HandleFunc(pattern string, h http.HandlerFunc) *Server
- func (s *Server) Handler() http.Handler
- func (s *Server) Logger() Logger
- func (s *Server) Mux() *http.ServeMux
- func (s *Server) RegisterHealthCheck(name string, timeout time.Duration, fn func(context.Context) error) *Server
- func (s *Server) RegisterMiddleware(name string, mw Middleware) *Server
- func (s *Server) ServeDocs(opts DocsOptions) *Server
- func (s *Server) SetCORS(opts CORSOptions) *Server
- func (s *Server) SetDefaultHandlerTimeout(d time.Duration) *Server
- func (s *Server) SetDefaultMaxBodySize(bytes int64) *Server
- func (s *Server) SetDefaultMaxHeaderSize(kb int) *Server
- func (s *Server) SetDefaultReadTimeout(d time.Duration) *Server
- func (s *Server) SetDefaultWriteTimeout(d time.Duration) *Server
- func (s *Server) SetHandleNotFound(h http.Handler) *Server
- func (s *Server) SetJSONCodec(c JSONCodec) error
- func (s *Server) SetLogger(l Logger) *Server
- func (s *Server) SetStrictJSON(strict bool) error
- func (s *Server) Start(addr string) error
- func (s *Server) Stop(ctx context.Context) error
- func (s *Server) Use(mw Middleware) *Server
- func (s *Server) With(names []string, h http.HandlerFunc) http.HandlerFunc
- type StatusError
- type StrictDecoder
- type UnknownErrorHandler
- type ValidationFailedHandler
Constants ¶
const ( DefaultLivenessPath = "/healthz" DefaultReadinessPath = "/readyz" )
DefaultLivenessPath / DefaultReadinessPath are the health routes a fresh Server mounts. Exported so other layers (the analyzer's reserved-route check) can reference the same values instead of re-spelling them.
Variables ¶
var ErrNoDecoder = errors.New("server: client does not accept the stored content-coding and no decoder was supplied")
ErrNoDecoder is returned by WritePrecompressed when the client does not accept the stored content-coding and no decode function was supplied, so the body cannot be served in either form. Nothing has been written when it is returned.
Functions ¶
func AcceptsEncoding ¶ added in v1.6.0
AcceptsEncoding reports whether the client accepts the content-coding (`"zstd"`, `"gzip"`, `"br"`, ...): the token appears in the request's Accept-Encoding with a non-zero quality. Case-insensitive. Raw-response handlers that hold a body stored already compressed use it to decide whether the bytes can go out verbatim; see WritePrecompressed for the packaged form.
func BindValue ¶ added in v1.2.0
func BindValue[T any](w http.ResponseWriter, r *http.Request, field, kind, raw string, dst *T, parse func(string) (T, error)) bool
BindValue parses raw into *dst when raw is non-empty. An absent or present-but-empty value (`?x=`) leaves *dst at its zero value. A parse failure writes a validation error and returns false so the handler returns early.
func BindValuePtr ¶ added in v1.2.0
func BindValuePtr[T any](w http.ResponseWriter, r *http.Request, field, kind, raw string, dst **T, parse func(string) (T, error)) bool
BindValuePtr is the optional (`*T`) variant: it points *dst at the parsed value, leaving it nil when raw is empty.
func BindValues ¶ added in v1.2.0
func BindValues[T any](w http.ResponseWriter, r *http.Request, field, kind string, raw []string, dst *[]T, parse func(string) (T, error)) bool
BindValues parses each element of raw into *dst (repeated `?ids=1&ids=2` or a multi-value header). One bad element fails the whole bind.
func CookiePresent ¶ added in v1.2.0
CookiePresent reports whether the named cookie is on the request. `r.Cookie` returns http.ErrNoCookie when absent, so a nil error means present. Used by the generated handler to drive RequirePresent for a required cookie parameter.
func ParseFloat ¶ added in v1.2.0
ParseFloat parses s as a float sized to T (float32 / float64).
func ParseSigned ¶ added in v1.2.0
ParseSigned parses s as a signed integer sized to T and converts to T, covering the builtin int kinds and int-backed scalars.
func ParseUnsigned ¶ added in v1.2.0
ParseUnsigned is the unsigned counterpart of ParseSigned.
func RequirePresent ¶ added in v1.2.0
RequirePresent writes a 400 and returns false when a required wire parameter's key is absent. `present` is the source-specific presence test the caller computes (url.Values.Has, a non-empty header-values slice, ...); a present-but-empty value (`?q=`) counts as present, since the value may legitimately be the empty string. Mirrors the BindValue contract: the generated handler returns early when this returns false.
func SetDefaultValidationFailed ¶
func SetDefaultValidationFailed(h ValidationFailedHandler)
SetDefaultValidationFailed installs a process-wide handler invoked for every `req.Validate()` failure. Pass nil to revert to the default. The function is safe to call concurrently with handler dispatch; a single-pointer atomic swap keeps the hot path lock-free.
func SetGlobalJSONCodec ¶
SetGlobalJSONCodec installs c as the codec returned by JSON; nil restores the built-in one. Call once at startup before serving traffic; the swap itself is goroutine-safe but in-flight handlers already mid-encode keep using the codec they captured. While strict JSON is on, c must implement StrictDecoder, or the call fails and the previous codec stays installed.
func SetHandleUnknownError ¶ added in v1.3.6
func SetHandleUnknownError(h UnknownErrorHandler)
SetHandleUnknownError installs a process-wide handler for service errors that are not craftgo typed errors (no HTTPStatus) - use it to map a domain error to a status, redact, or return a uniform envelope. Pass nil to revert to the default (log the full error with trace context + 500 + opaque message). Safe to call concurrently with dispatch; a single-pointer atomic swap keeps the hot path lock-free.
func SetStrictJSON ¶ added in v1.7.0
SetStrictJSON selects how a request body that does not match the request type exactly is treated. Strict rejects an unknown field and data after the JSON value; lenient, the default, ignores both as encoding/json does. Strict needs the installed codec to implement StrictDecoder, or the call fails and the setting stays as it was.
func TrailingData ¶ added in v1.7.0
TrailingData fails when anything but whitespace follows a decoded JSON value: first in the decoder's read-ahead (buffered), then on the rest of the body. A codec's DecodeStrict calls it after decoding, so every codec reports leftover data the same way. Reading byte by byte keeps the check allocation-free; on a well-formed request the remainder is empty or a newline.
func WithLimits ¶
WithLimits returns h wrapped with the runtime guards declared in l. Zero-valued fields skip their respective wrapping so the function is a cheap pass-through when the DSL declared no limits.
Wrapping order is innermost-first: MaxBodySize wraps r.Body before the handler reads it, then Timeout wraps the whole chain so the timeout includes the body-read step.
When l sets a MaxBodySize or a Timeout the result is tagged (see [limitedHandler]) so Server.Handle / Server.HandleFunc know this route already has its own cap / deadline and skip the matching server-wide default: a per-method @maxBodySize or @timeout takes priority over the default rather than being layered on top of it.
func WriteBytes ¶ added in v1.6.0
WriteBytes writes a complete response in one go: contentType (when non-empty) and Content-Length are set, the status is written, then body. It is the building block for raw-response handlers that already hold the exact bytes they want on the wire and do not want the JSON encoder in the way.
func WriteError ¶ added in v1.3.6
func WriteError(w http.ResponseWriter, r *http.Request, err error)
WriteError is the indirection generated handlers call when service logic returns a non-nil error. It splits on whether the error is a recognised craftgo typed error:
- a StatusError anywhere in err's chain (found with errors.As, so a typed error wrapped with `%w` keeps its status) is rendered from its interface - the declared HTTP status, the optional `@header`/`@cookie` writes via ResponseHeaderWriter, then a JSON body: the codec encodes the typed error's declared body struct, or - when the error declares no body and would marshal to `{}` - a `{code, message}` envelope built from its `ErrCode()` / `Error()` so clients can still discriminate the failure. A typed error is an expected outcome (a declared 4xx/5xx), so it is NOT logged;
- anything else (a bare errors.New / fmt.Errorf) is delegated to the SetHandleUnknownError handler, whose default logs the error with the request's trace context and responds 500.
Header precedence: WriteResponseHeaders writes user-declared fields FIRST, then the framework stamps `Content-Type: application/json; charset=utf-8` LAST, so a `@header("Content-Type")` field is overridden - intentional, since the body that follows is always JSON. Use a raw-response handler for a different content type.
When the response is already committed - a raw-response handler streamed part of a body and then returned an error - the envelope cannot be written: net/http drops the second WriteHeader and the JSON would be spliced into the in-flight body. The error is logged with the request's trace context instead and the wire is left alone.
func WritePrecompressed ¶ added in v1.6.0
func WritePrecompressed(w http.ResponseWriter, r *http.Request, status int, contentType, coding string, body []byte, decode func([]byte) ([]byte, error)) error
WritePrecompressed serves a body that is stored already compressed - a cache entry, a pre-built asset - without touching it when it can. When the client's Accept-Encoding lists coding (`"zstd"`, `"gzip"`, `"br"`, ...), the bytes go out verbatim with Content-Encoding set; otherwise decode turns them back into the identity form first. Either way the response carries `Vary: Accept-Encoding` so caches keep the two shapes apart. The framework pulls in no compression library: the caller, who already has one to fill the cache, supplies decode. A nil decode with a client that does not accept coding returns ErrNoDecoder before anything is written.
The Compress middleware leaves a response that already carries Content-Encoding untouched, so the verbatim body is never re-encoded; on the decoded path Compress may gzip the identity bytes as usual.
func WriteValidationError ¶
func WriteValidationError(w http.ResponseWriter, r *http.Request, err error)
WriteValidationError is the indirection generated handlers call. Kept exported so the codegen template can name it without reflection; not intended for application use.
Types ¶
type AccessLogOption ¶ added in v1.5.2
type AccessLogOption func(*accessLogConfig)
AccessLogOption configures AccessLog.
func AccessLogFields ¶ added in v1.7.0
func AccessLogFields(fn func(r *http.Request) []log.Field) AccessLogOption
AccessLogFields appends the fields fn derives from the request to every `http access` line - the client address, the user agent, the matched route (`r.Pattern`). fn runs after the handler, so it sees the route the mux matched.
func AccessLogSkipPaths ¶ added in v1.5.2
func AccessLogSkipPaths(paths ...string) AccessLogOption
AccessLogSkipPaths keeps requests whose `r.URL.Path` equals one of paths out of the log - a `/metrics` scrape served on the API port, for example. The health probes need no entry here: they never reach the middleware chain (see Server.Handler).
type CORSOptions ¶
type CORSOptions struct {
AllowedOrigins []string
AllowedMethods []string
AllowedHeaders []string
ExposedHeaders []string
AllowCredentials bool
MaxAge time.Duration
AllowPrivateNetwork bool
}
CORSOptions configures the CORS middleware. Most fields mirror the corresponding HTTP headers; AllowedOrigins entries may use a single leading wildcard (`https://*.example.com`) or the full wildcard `*`.
func CORSPermissive ¶
func CORSPermissive() CORSOptions
CORSPermissive returns a development-mode preset that mirrors browser defaults for non-credentialed APIs. Not suitable for production.
func CORSStrict ¶
func CORSStrict(origin string) CORSOptions
CORSStrict returns a production-leaning preset locked to a single origin and a small set of common headers; toggle credentials on at the call site if needed.
type Chain ¶
type Chain []Middleware
Chain composes middlewares in outermost-first order: a chain `NewChain(A, B, C).Then(h)` yields `A(B(C(h)))`, so a request flows A → B → C → h and the response leaves in reverse.
Chains are value types - Append returns a new chain rather than mutating the receiver, so a base chain shared across routes is safe to extend per call site. Nil entries are tolerated and skipped at Then time so optional middlewares can drop into the slice without an `if mw != nil` guard at every call site.
func NewChain ¶
func NewChain(mws ...Middleware) Chain
NewChain seeds a chain with the supplied middlewares in outermost-first order. The result is a fresh slice - mutating mws after the call does not affect the chain.
func (Chain) Append ¶
func (c Chain) Append(mws ...Middleware) Chain
Append returns a new chain with mws added at the innermost end. The receiver is unchanged.
type CompressOptions ¶
type CompressOptions struct {
// MinSize is the threshold below which responses skip compression.
// Bodies smaller than this are released uncompressed because the
// CPU cost outweighs the wire-size win. Defaults to 1024.
MinSize int
// Level is the gzip / deflate compression level (1..9, or
// gzip.DefaultCompression). Defaults to gzip.DefaultCompression.
Level int
// SkipTypes overrides the default list of Content-Type prefixes
// that bypass compression (media that's already byte-compressed
// by its own format). Pass an empty slice to compress everything.
SkipTypes []string
}
CompressOptions tunes the response compression middleware.
type DocsOptions ¶ added in v1.3.8
type DocsOptions struct {
// Spec is the OpenAPI document served verbatim at SpecPath (YAML or JSON).
Spec []byte
// UI is "redoc" (default), "swagger", or "scalar".
UI string
// Path is the route for the HTML docs page (default "/docs").
Path string
// SpecPath is the route serving the raw spec (default "/openapi.yaml").
SpecPath string
// Title is the page <title> (default "API Reference").
Title string
}
DocsOptions configures Server.ServeDocs.
type DocsUI ¶ added in v1.3.8
type DocsUI string
DocsUI selects which API-reference UI Server.ServeDocs renders. All three load their assets from a CDN, so the binary stays small and no assets ship with it (the docs page needs outbound network the first time a browser opens it). Unknown values fall back to Redoc.
type HealthPaths ¶
HealthPaths is the override pair for DefaultLivenessPath and DefaultReadinessPath.
type JSONCodec ¶
JSONCodec is the small surface generated handlers and the access-log middleware delegate to when they need to (de)serialise JSON. The default implementation wraps `encoding/json`; production projects can substitute sonic, jsoniter, or any compatible alternative through SetGlobalJSONCodec. A codec that also implements StrictDecoder can serve a project with `server.strictJSON` on.
func JSON ¶
func JSON() JSONCodec
JSON returns the codec currently in effect: the one installed via SetGlobalJSONCodec (or the stdlib default when none has been set), decoding strictly while SetStrictJSON is on. Generated handlers call this every time they need to (de)serialise so a runtime swap takes effect on the next request.
type Limits ¶
type Limits struct {
// Timeout caps the full handler lifecycle (decode body → user
// logic → encode response) by deriving a context.WithTimeout
// from the request and handing it to the handler. Handlers that
// honour ctx.Done() return early on deadline; handlers that do
// not honour it run to completion. Applies to every route,
// raw / passthrough handlers included: only the context is
// cancelled, so a streaming body is never cut off by the
// framework.
//
// The middleware does NOT goroutine-isolate the handler the way
// [http.TimeoutHandler] does. That stdlib helper buffers the
// response in memory and discards any panic that fires after
// the timeout cut-off, masking real bugs as "request timed out"
// 503s. Running in-line keeps panics visible to the outer
// Recovery middleware at the cost of losing the force-cancel
// guarantee on context-deaf handlers.
Timeout time.Duration
// MaxBodySize caps the request body size in bytes. Wraps r.Body
// with [http.MaxBytesReader] before the user's handler runs;
// reads past the cap return an error, which the JSON decoder
// surfaces as 400.
MaxBodySize int64
}
Limits bundles the per-method runtime guards the DSL surfaces via `@timeout` and `@maxBodySize`. Zero values mean "no limit" for that dimension - the wrapper only applies a guard when the corresponding field is non-zero.
Transport-level deadlines (`http.Server.ReadTimeout`, `WriteTimeout`, `IdleTimeout`, `ReadHeaderTimeout`, `MaxHeaderBytes`) are NOT modelled here - those are server-wide concerns the user configures on the underlying http.Server directly when the stdlib defaults are insufficient. This package only owns guards that can be enforced per-handler.
type Logger ¶
Logger aliases the public Logger interface so handler-internal code does not need a second import.
type Middleware ¶
Middleware wraps an http.Handler. Order: outermost first, so the slice is applied in reverse during Start.
func AccessLog ¶
func AccessLog(logger log.Logger, opts ...AccessLogOption) Middleware
AccessLog logs one line per request after the response has been written: message `http access` with `method`, `path`, `status` and `latency`, plus the `trace_id` / `span_id` the request context carries (see log.Logger.WithContext). Wire the telemetry HTTP middleware before AccessLog so those ids are on the context.
Every request that reaches the middleware logs; AccessLogSkipPaths keeps chosen routes out and AccessLogFields adds fields of your own.
func BodyLimit ¶
func BodyLimit(maxBytes int64) Middleware
BodyLimit returns a middleware that caps the request body at maxBytes for every route it wraps. A request whose declared Content-Length already exceeds the cap is rejected with 413 before the handler runs; a chunked/unknown-length body is capped on read via http.MaxBytesReader (surfaced by the downstream handler, typically as 400). It shares its implementation with the per-method @maxBodySize guard ([maxBodySizeHandler]) so the global and per-method limits never drift.
func Compress ¶
func Compress(opts ...CompressOptions) Middleware
Compress returns middleware that gzip- or deflate-compresses responses when the client advertises a matching Accept-Encoding. Small bodies (< MinSize) and pre-compressed media types pass through untouched.
Vary: Accept-Encoding is added to every response so caches keep the negotiated and unnegotiated copies separate.
func Recovery ¶
func Recovery(logger log.Logger) Middleware
Recovery converts panics inside downstream handlers into a 500 response while logging a stack trace. Always installed by Server.Start as the outermost middleware. When the panic fires AFTER the handler has already committed to a status (called WriteHeader or Write), the 500 cannot be written - net/http silently drops the second WriteHeader and the body bytes would corrupt the in-flight response. In that case the middleware logs the panic loudly and lets the connection terminate; the client sees the truncated original response and the server operator sees the stack trace.
func Timeout ¶
func Timeout(d time.Duration) Middleware
Timeout enforces an upper bound on handler execution. Streaming methods should not use this - they need write-side per-message idle limits which belong to the streaming codec, not the request lifecycle.
type Option ¶
type Option func(*Server)
Option configures a Server at construction time.
func WithHealthPaths ¶
func WithHealthPaths(p HealthPaths) Option
WithHealthPaths overrides the default `/healthz` and `/readyz` routes.
func WithoutDefaultHealth ¶
func WithoutDefaultHealth() Option
WithoutDefaultHealth disables the auto-registered health endpoints.
type ResponseHeaderWriter ¶ added in v1.3.6
type ResponseHeaderWriter interface {
WriteResponseHeaders(http.ResponseWriter)
}
ResponseHeaderWriter is the optional extension a typed error implements when it declares `@header` / `@cookie` fields. WriteError calls it before the status line so those values reach the wire ahead of the JSON body.
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Server is craftgo's runtime. Methods follow a fluent style so a project `main.go` can chain configuration calls before `Start()`.
func New ¶
New returns a Server with sensible defaults: JSON codec, slog logger, `/healthz` + `/readyz` health probes, no rate-limit, no CORS. Pass any number of Option values to override.
`_` is the project's ServiceContext; it's accepted only to mirror the documented constructor signature - the runtime doesn't introspect it.
func (*Server) Handle ¶
Handle registers an http.Handler under the same Go 1.22 pattern syntax HandleFunc uses. Optional variadic middlewares wrap the handler left-to-right so the FIRST entry ends up the outermost frame - the order a reader scans matches the order a request flows through. Order chosen so:
srv.Handle("POST /x", h, Auth, RateLimit, CORS)
reads "Auth wraps RateLimit wraps CORS wraps h" - request hits Auth first, response leaves CORS last.
The variadic form keeps the route line flat regardless of chain depth, so it scans top-to-bottom in the same outermost-first order the request actually flows through.
func (*Server) HandleFunc ¶
func (s *Server) HandleFunc(pattern string, h http.HandlerFunc) *Server
HandleFunc registers a custom route on the underlying mux using Go 1.22 pattern syntax (`"VERB /path"`). The server-wide default body cap ([SetDefaultMaxBodySize]) and handler timeout ([SetDefaultHandlerTimeout]) apply unless the handler carries its own.
func (*Server) Handler ¶
Handler returns the fully-wrapped http.Handler: mux + every global middleware registered via Server.Use + CORS (when configured) + Recovery (always outermost). The health probes are answered ahead of that chain: a request whose path is exactly the liveness or readiness route goes straight to the probe handler, wrapped in Recovery alone, so probes are never access-logged, traced, measured, CORS-processed or subject to `Use` middleware. WithoutDefaultHealth removes them; a project that wants observed probes registers its own route instead.
This is the entry point both Server.Start and tests use - wrap `httptest.NewServer(srv.Handler())` to exercise the full chain without binding a real listener.
func (*Server) Mux ¶
Mux returns the underlying `*http.ServeMux`. Generated routes call HandleFunc directly via the mux to keep the dependency surface small.
func (*Server) RegisterHealthCheck ¶
func (s *Server) RegisterHealthCheck(name string, timeout time.Duration, fn func(context.Context) error) *Server
RegisterHealthCheck adds a named probe to `/readyz`. The function is invoked under a context with the supplied timeout; a non-nil error or timeout flips the readiness response to 503.
func (*Server) RegisterMiddleware ¶
func (s *Server) RegisterMiddleware(name string, mw Middleware) *Server
RegisterMiddleware maps a DSL middleware name to its concrete implementation. The codegen layer can later resolve `@middlewares(Name)` against this map.
func (*Server) ServeDocs ¶ added in v1.3.8
func (s *Server) ServeDocs(opts DocsOptions) *Server
ServeDocs registers two GET routes on the server: SpecPath serves the raw OpenAPI document, and Path serves an HTML page that loads the chosen API-reference UI from a CDN, pointed at SpecPath. It is a no-op (returns the server unchanged) when Spec is empty. Returns the server for chaining.
Generated projects wire this from main.go behind `config.docs.enabled`; it is also callable directly by hand-written servers.
func (*Server) SetCORS ¶
func (s *Server) SetCORS(opts CORSOptions) *Server
SetCORS attaches a CORS middleware configured by opts. Calling SetCORS twice replaces the previous configuration.
func (*Server) SetDefaultHandlerTimeout ¶ added in v1.5.0
SetDefaultHandlerTimeout sets the default per-handler execution deadline applied to every route registered afterwards that does not declare its own `@timeout`. A route WITH `@timeout` overrides this default (used as-is, longer or shorter). It is a soft context deadline the handler must honour via ctx.Done() - not the hard socket-level Server.SetDefaultWriteTimeout. The default is 0 (no deadline). Resolved per route at registration time, so call it before registering routes.
func (*Server) SetDefaultMaxBodySize ¶
SetDefaultMaxBodySize sets the default request-body size cap (in bytes) applied to every route registered afterwards that does not declare its own `@maxBodySize`. A route WITH `@maxBodySize` overrides this default (it is used as-is, whether larger or smaller), so the default is a fallback, not a ceiling. The default is 0 (no cap). Call it before registering routes; the cap is resolved per route at registration time.
func (*Server) SetDefaultMaxHeaderSize ¶
SetDefaultMaxHeaderSize configures the default request header size cap (in kilobytes - Go's http.Server uses a kilobyte unit internally).
func (*Server) SetDefaultReadTimeout ¶
SetDefaultReadTimeout configures the default per-method read timeout.
func (*Server) SetDefaultWriteTimeout ¶
SetDefaultWriteTimeout sets the http.Server WriteTimeout - a hard deadline on the entire response write. It defaults to 0 (unbounded) so streaming, SSE, @passthrough, and large/slow downloads are not cut off mid-response; set a ceiling here for a server that only serves bounded JSON and wants socket-level slow-drain protection on top of the per-handler Timeout middleware.
func (*Server) SetHandleNotFound ¶
SetHandleNotFound installs a per-server handler invoked for requests whose path does not match any registered route. Replaces the stdlib mux's default `404 page not found` body. Pass nil to fall back to the default.
Health endpoints, static handlers, and middleware-rejected requests still bypass this handler - only routes that reach the mux without a match trigger it.
func (*Server) SetJSONCodec ¶
SetJSONCodec swaps the JSON codec used by generated handlers, the access-log middleware, and the health endpoints. The change is process-wide via SetGlobalJSONCodec; the per-Server field is kept for callers that want to introspect via Server.Codec but the authoritative value lives on the package-level atomic. Fails, keeping the previous codec, when strict JSON is on and c has no DecodeStrict.
func (*Server) SetLogger ¶
SetLogger replaces the active Logger and mirrors it to the package-level log.Default so codegen-emitted logic files reach the same instance via `log.Default().WithContext(ctx)` without receiving a handle through ServiceContext.
func (*Server) SetStrictJSON ¶ added in v1.7.0
SetStrictJSON is SetStrictJSON; like the codec, the setting is process-wide.
func (*Server) Start ¶
Start binds the server to addr and serves until Stop is called. The handler chain is built by Server.Handler so the wrapping order is identical between live serving and httptest-driven test runs.
func (*Server) Stop ¶
Stop gracefully shuts down the running server. Safe to call before Start (it becomes a no-op).
func (*Server) Use ¶
func (s *Server) Use(mw Middleware) *Server
Use appends a middleware to the chain. Outer middlewares are added first; the chain is built in reverse at Start.
func (*Server) With ¶
func (s *Server) With(names []string, h http.HandlerFunc) http.HandlerFunc
With looks up every middleware name in the registered table and wraps h in the order given (first name = outermost). Unknown names are skipped silently so a route can declare `@middlewares(Optional)` even when the wiring isn't installed yet - the runtime will pick it up the moment RegisterMiddleware adds it.
type StatusError ¶ added in v1.3.6
StatusError is the contract every craftgo-generated typed error satisfies: a standard error that also reports an HTTP status. WriteError renders these directly. Application code can implement it on its own error types to have them rendered with a chosen status instead of falling through to the unknown-error handler.
type StrictDecoder ¶ added in v1.7.0
StrictDecoder is the optional half of a codec that can honour SetStrictJSON: DecodeStrict is Decode that rejects an unknown field (`<field>: unknown field`) and data after the JSON value (see TrailingData). The built-in codec implements it.
type UnknownErrorHandler ¶ added in v1.3.6
type UnknownErrorHandler func(w http.ResponseWriter, r *http.Request, err error)
UnknownErrorHandler renders an error that is NOT a recognised StatusError - a bare errors.New / fmt.Errorf returned by service logic that carries no HTTP status. It receives the request so it can read trace context for logging. The default logs the full error with the request's trace IDs and responds 500 with an opaque `{"message": "internal server error"}` body - the raw error text stays in the log, never on the wire; SetHandleUnknownError swaps it process-wide.
type ValidationFailedHandler ¶
type ValidationFailedHandler func(w http.ResponseWriter, r *http.Request, err error)
ValidationFailedHandler is the function shape every generated handler calls when `req.Validate()` returns a non-nil error. The default implementation mirrors `http.Error` - a 400 with the validator's message - and applications swap it in by calling SetDefaultValidationFailed once at startup.