Documentation
¶
Overview ¶
Package server runs an HTTP server with graceful shutdown and, optionally, a River job queue backed by the same SQLite database.
cfg := server.Config{Port: 7880, DB: db, Workers: workers, RiverUI: true}
if err := cfg.Parse(); err != nil { ... } // .env, env vars, then flags
srv, err := server.New(ctx, cfg)
srv.Mux.Handle("/api/", api(srv.River))
err = srv.Run(ctx)
Index ¶
- func BasicAuth(handler http.Handler, username, password, realm string) http.Handler
- func NewLogger(w io.Writer, format string, level slog.Level) (*slog.Logger, error)
- func OpenDB(ctx context.Context, path string, fsys fs.FS) (*sql.DB, error)
- func RequestError(w http.ResponseWriter, r *http.Request, err error)
- func ResponseError(w http.ResponseWriter, r *http.Request, err error)
- func WriteError(w http.ResponseWriter, status int, message string)
- type Config
- type Server
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func NewLogger ¶
NewLogger returns a logger writing to w in the given format: "color" (also "human"), "text" or "json".
func OpenDB ¶
OpenDB opens the SQLite database at path, creating it and its directory if needed, and applies the migrations in fsys (see package migrate). fsys may be nil to skip migrations.
fsys's pragmas.sql, if any, runs on every connection as it's opened, so per-connection pragmas (foreign_keys, busy_timeout) hold even if database/sql replaces the connection.
The pool is limited to one connection: SQLite allows a single writer, and sharing one connection between the app and River avoids SQLITE_BUSY. So inside a transaction, use only the transaction: a query on the *sql.DB waits for the connection the transaction holds, and never gets it.
func RequestError ¶
func RequestError(w http.ResponseWriter, r *http.Request, err error)
RequestError writes a 400 with {"error": err.Error()}. It fits oapi-codegen's RequestErrorHandlerFunc (body can't be decoded) and ErrorHandlerFunc (bad path or query parameter), where err describes what the client sent wrong.
func ResponseError ¶
func ResponseError(w http.ResponseWriter, r *http.Request, err error)
ResponseError logs err via slog's default logger, with the request's method and path, and writes a 500 with a generic message so the error's details never reach the client. It fits oapi-codegen's ResponseErrorHandlerFunc, which gets the error a handler returned; the generated default sends that error's text to the client.
func WriteError ¶
func WriteError(w http.ResponseWriter, status int, message string)
WriteError writes status with {"error": message}, the body RequestError and ResponseError send, for handlers outside oapi-codegen that want the same shape.
Types ¶
type Config ¶
type Config struct {
// Addr is the listen address. Defaults to ":<Port>".
Addr string
// Port is used when Addr is empty. Defaults to 8080. Set by -port / PORT.
Port int
// LogFormat is "color", "text" or "json", and LogLevel the minimum level
// logged. Parse builds Logger from them. Set by -log / LOG and
// -log-level / LOG_LEVEL.
LogFormat string
LogLevel slog.Level
// Logger is used by the server and River. Defaults to slog.Default().
Logger *slog.Logger
// ShutdownTimeout bounds how long Run waits for in-flight requests and
// running jobs when stopping. Defaults to 10s.
ShutdownTimeout time.Duration
// DB is the database River stores jobs in. Required if Workers is set.
// When set, the server also serves GET /health, which pings it.
DB *sql.DB
// DisableHealth stops New from registering GET /health, so the app can
// register its own.
DisableHealth bool
// Workers enables River. Leave nil for a plain HTTP server.
Workers *river.Workers
// Queues configures River's queues. Defaults to the default queue with
// 5 workers.
Queues map[string]river.QueueConfig
// RiverUI mounts the River dashboard at /riverui/. If RiverUIUsername
// and RiverUIPassword are both set, it's behind basic auth. Parse fills
// the credentials from RIVERUI_USERNAME and RIVERUI_PASSWORD (env only,
// so the password never shows up in ps).
RiverUI bool
RiverUIUsername string
RiverUIPassword string
}
Config configures a Server. Only fields that are set are used; everything else falls back to a default.
func (*Config) Parse ¶
Parse fills c from, in increasing precedence: the values already in c (the app's defaults), a .env file, environment variables, and finally command-line flags. It then builds c.Logger and makes it the slog default.
It registers -port, -log and -log-level on flag.CommandLine. Every flag on flag.CommandLine, including ones the app registers before calling Parse, can also be set by an env var named after it: -port is PORT, -log-level is LOG_LEVEL. A flag given on the command line always wins.
The .env file is loaded into the process environment, so the app's own os.Getenv calls after Parse see it too. It never overrides a variable that's already set.
type Server ¶
type Server struct {
Mux *http.ServeMux
// River is nil unless Config.Workers was set. It's usable for inserting
// jobs as soon as New returns; Run starts it working them.
River *river.Client[*sql.Tx]
// contains filtered or unexported fields
}
Server is an HTTP server plus an optional River client. Register routes on Mux between New and Run.
func New ¶
New builds a Server. If cfg.Workers is set it also applies River's schema migrations to cfg.DB and creates the River client.