Skip to content

HTTP examples

A tour of the HTTP layer — the server and its deadlines, binding a request and validating it in one call, the errors a client gets back, list endpoints, middleware, bearer-token auth, and files in and out.

Read HTTP Layer first for the concepts — these files show them wired together.

sh
git clone https://gitlab.finema.co/finema/idin-core.git
cd idin-core/v2/examples/http
go run .

Serving a request

The three things every endpoint does — come up on a port, turn a request into a value you can trust, and answer a failure in a way the caller can act on.

Shaping what comes in

Lists, guards and uploads — the three places where the request carries something the endpoint must bound before it acts on it.

Who is calling

One middleware over a pluggable verifier: what changes between a JWT service and an API-key service is the verifier, not the routes.

Putting it together

main.go mounts every example in this topic on one server, so go run . starts a service that answers all of them. It needs nothing running: with no DB_* configuration the list endpoint pages a fixture, and storage is the in-memory implementation.

go
// Command http is a runnable tour of the v2 HTTP layer: the server and its
// options, binding and validating a request, the errors a client gets back,
// list endpoints, middleware, bearer-token auth, and file upload/download.
//
// Each example lives in its own file:
//
//	01_server.go      the server, its options, groups, and how it is started
//	02_binding.go     one struct per request, one Valid, one BindWithValidate
//	03_errors.go      errors a client can act on, and where they are declared
//	04_pagination.go  paging, ordering, and what a client may sort by
//	05_middleware.go  writing middleware and choosing where to attach it
//	06_auth.go        bearer tokens, roles, and issuing one
//	07_upload.go      multipart in, streamed file out
//
// It runs with no external services: with no DB_* configuration the list
// endpoint pages a fixture, and storage is the in-memory implementation, so
// every route answers.
//
// Run it with: go run ./examples/http
package main

import (
	core "gitlab.finema.co/finema/idin-core/v2"
)

func main() {
	env, err := core.NewEnv()
	if err != nil {
		panic(err)
	}

	// The App is the process-lifetime container of pools and capabilities;
	// IContext is the per-request handle onto it. Nothing here is created per
	// request, which is why a handler can take capabilities without cost.
	//
	// NewMemoryStorage is the exported in-memory implementation every capability
	// ships — a dev box should not need MinIO to accept an upload. A real
	// service passes core.WithStorage(s3) and changes nothing else.
	app, err := core.NewApp(env,
		core.WithStorage(core.NewMemoryStorage()),
	)
	if err != nil {
		panic(err)
	}

	e := newServer(app)     // 01: options, deadlines, the standard stack
	api := mountSystem(e)   // 01: /healthz plus the /api/v1 group
	mountArticleWrites(api) // 02: POST/PUT with binding and validation
	mountArticleErrors(api) // 03: what a refused request looks like
	mountArticleList(api)   // 04: GET /api/v1/articles?page=&order_by=
	mountMiddleware(e, app) // 05: a guarded group and a per-route body limit
	mountFiles(e)           // 07: POST /files, GET /files/:id

	// 06 is the one part that cannot be faked. Rather than fall back to a
	// built-in secret — which would sign tokens anyone reading this repository
	// could mint — the authenticated routes are simply not registered, and the
	// reason is on the first screen of the log instead of in a 401 later.
	if auth, ok := newJWTAuth(env); ok {
		mountAuth(e, auth)
	} else {
		app.Log().Warn("authenticated routes are not mounted: no JWT secret",
			"hint", "set APP_JWT_SECRET to try /auth/login, /me, /feed and /admin/articles/:id")
	}

	// Blocks. Outside dev it drains in-flight requests on SIGINT/SIGTERM and
	// only then closes the pools — see startWithRunner in 01_server.go for the
	// process that also runs jobs.
	startServer(e, env)
}

Maintained by Passakon Puttasuwan & Dev Core Team.