Core Primer
Core Primer
Section titled “Core Primer”Open this page after Stable Roots when the change still belongs to the default service path and the real question has narrowed to the HTTP application kernel.
core owns app construction, route attachment, middleware attachment, and server lifecycle. It is the right home when the change should remain part of the smallest canonical service shape every Plumego service is expected to understand.
app := core.New(core.DefaultConfig(), core.AppDependencies{Logger: plog.NewLogger()})_ = app.Get("/ping", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { _ = contract.WriteResponse(w, r, http.StatusOK, map[string]string{"status": "ok"}, nil)}))app.Run()Start here when
Section titled “Start here when”- you are assembling an application from explicit routes and middleware
- you are changing
Prepare,Server, orShutdownlifecycle behavior - you are working on app construction or shared runtime dependency wiring
Do not start here when
Section titled “Do not start here when”- the change is really about route matching rather than the app kernel
- the change introduces feature catalogs, plugin containers, or tenant policy
- the work is persistence behavior or debug-tool payload design
First files to read in the current repository
Section titled “First files to read in the current repository”core/module.yamlcore/app.gocore/dependencies.goreference/standard-service/internal/app/app.go
Concrete ownership examples
Section titled “Concrete ownership examples”Keep it in core when the work is about | Move out when the work becomes |
|---|---|
explicit app construction through DefaultConfig() / AppConfig and AppDependencies | feature-specific wiring that belongs in app-local code or an extension |
one canonical lifecycle path: Prepare + Server + Shutdown | debug metadata transport and runtime snapshot payloads in x/observability/devtools |
route binding through App.AddRoute, App.Get, App.Post, and named-route URL lookup | raw router replacement, feature catalogs, or capability-owned runtime behavior |
| passive dependency injection such as logger wiring | logger subsystem ownership, flush policy, or readiness policy that stays app-local |
Import
Section titled “Import”import "github.com/spcent/plumego/core"Construct an application
Section titled “Construct an application”cfg := core.DefaultConfig() // baseline: :8080, 30s timeouts, HTTP/2 oncfg.Addr = ":9090" // override any field before passing to New
app := core.New(cfg, core.AppDependencies{ Logger: myLogger, // omit to discard logs})DefaultConfig returns a ready-to-use AppConfig. Override only what differs.
AppConfig fields
Section titled “AppConfig fields”| Field | Default | Description |
|---|---|---|
Addr | :8080 | Listen address |
TLS.Enabled | false | Enable TLS |
TLS.CertFile | "" | Path to TLS certificate |
TLS.KeyFile | "" | Path to TLS private key |
Router.MethodNotAllowed | false | Return 405 with Allow header on method mismatch |
ReadTimeout | 30s | Max duration for reading entire request |
ReadHeaderTimeout | 5s | Max duration for reading request headers |
WriteTimeout | 30s | Max duration for writing response |
IdleTimeout | 60s | Max idle time for keep-alive connections |
MaxHeaderBytes | 1 MiB | Max size of request headers |
HTTP2Enabled | true | Keep HTTP/2 support enabled |
DrainInterval | 500ms | Log interval for in-flight connections during drain |
Register routes
Section titled “Register routes”app.Get("/api/hello", http.HandlerFunc(handler.Hello))app.Post("/api/items", http.HandlerFunc(handler.CreateItem))app.Put("/api/items/:id", http.HandlerFunc(handler.UpdateItem))app.Delete("/api/items/:id", http.HandlerFunc(handler.DeleteItem))app.Patch("/api/items/:id", http.HandlerFunc(handler.PatchItem))
// method + path in one callapp.AddRoute(http.MethodGet, "/api/status", http.HandlerFunc(handler.Status))
// named route for reverse lookupapp.AddRoute(http.MethodGet, "/api/users/:id", http.HandlerFunc(handler.GetUser), router.WithRouteName("user-detail"))url := app.URL("user-detail", "id", "42") // → "/api/users/42"Attach middleware
Section titled “Attach middleware”import ( "github.com/spcent/plumego/middleware/requestid" "github.com/spcent/plumego/middleware/recovery" "github.com/spcent/plumego/middleware/accesslog")
recoveryMw, err := recovery.Middleware(recovery.Config{Logger: app.Logger()})if err != nil { return err}accesslogMw, err := accesslog.Middleware(accesslog.Config{Logger: app.Logger()})if err != nil { return err}app.Use(requestid.Middleware(), recoveryMw, accesslogMw)Use must be called before Prepare. Middleware runs in registration order.
Start the server
Section titled “Start the server”Combined path — for most services:
app.Run() // Prepare + ListenAndServe in one call; logs and exits on errorExplicit path — when you need to inspect or wrap the server before it starts (TLS, connection tracking, graceful shutdown with OS signals):
if err := app.Prepare(); err != nil { log.Fatalf("prepare: %v", err)}srv, err := app.Server()if err != nil { log.Fatalf("get server: %w", err)}
// Handle OS signals for graceful shutdownctx, stop := signal.NotifyContext(context.Background(), syscall.SIGTERM, syscall.SIGINT)defer stop()
serverErr := make(chan error, 1)go func() { serverErr <- srv.ListenAndServe() }()
select {case err := <-serverErr: log.Fatalf("server stopped: %v", err)case <-ctx.Done(): // signal received — begin graceful drain}
shutdownCtx, cancel := context.WithTimeout(context.Background(), 15*time.Second)defer cancel()_ = app.Shutdown(shutdownCtx)Prepare freezes the route table and builds the handler chain. Server returns the *http.Server. Shutdown drains in-flight connections and respects the context deadline. See Graceful Shutdown for the complete pattern including resource cleanup.
Why this primer exists
Section titled “Why this primer exists”core is often misread as the place for anything that feels foundational. The current repository says otherwise: core is a kernel, not a feature catalog. If the change makes the kernel learn capability semantics, it is probably in the wrong home.
Read next
Section titled “Read next”- core API Quick Reference — complete function signatures and type definitions
- Configuration Model
- Reference App
- Middleware Primer
- Repository Boundaries