go-todo

module
v0.0.0-...-ce43c02 Latest Latest
Warning

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

Go to latest
Published: Jul 31, 2026 License: MIT

README

go-todo

Actions Status PkgGoDev

A standard-library-focused Go todo application with PostgreSQL persistence, server-rendered pages, HTMX interactions, and a parallel JSON API.

Architecture

HTTP code is organized by feature under pkg/routes rather than split into page and API trees. For example, pkg/routes/login owns GET /login, POST /login, and POST /api/login, while pkg/routes/todos owns both the todo page actions and /api/todos endpoints. Page templates and their directly renderable HTMX fragments live beside the handlers that render them. Larger features may use asset/template-only page directories while retaining one Go package; for example, todo index, detail, and edit templates live under pkg/routes/todos/{index,detail,edit}.

The Go server embeds generated frontend files from pkg/web/dist. TypeScript and CSS are compiled ahead of time by esbuild; the server never invokes a JavaScript toolchain at runtime. pkg/web/global.client.ts installs htmx.org, and pkg/web/global.css layers application styles over mine.css. Feature-specific browser behavior lives in colocated page.client.ts files. The client build maps their directory paths to stable entries (such as /assets/pages/todos/index.js) and builds them together with ESM code splitting, so relative modules shared by multiple page clients are emitted once as hashed chunks.

JSON operations are registered with Huma. Their Go input and output structs provide compile-time handler contracts, request validation, JSON Schema generation, and an OpenAPI 3.1 document from the same source definitions. Browser and HTMX routes continue to use the standard library router directly.

Requirements

  • Go 1.26 or newer
  • Node.js 22 or newer and npm 10 or newer
  • PostgreSQL 17 (earlier supported PostgreSQL versions may also work)

Development

Create a database, copy the local configuration, install dependencies, and apply migrations:

createdb go_todo
cp .env.example .env
make deps
make migrate-up

Build the frontend and start the development server:

make dev

make dev runs the project-pinned Air tool. Changes to Go files or embedded HTML rebuild and restart the server; changes to browser TypeScript, pkg/web/global.css, or scripts/build-client.js rebuild the esbuild assets first and then restart Go so the new embedded assets are served.

Open http://127.0.0.1:8080. For local plain HTTP, keep SESSION_COOKIE_SECURE=false; deployments served over HTTPS should set it to true or omit it to use the secure default.

Useful commands:

make web-build     # type-check TypeScript and build embedded assets
make build         # build assets and all Go packages
make test          # build assets and run all Go tests
make validate      # install/verify dependencies, build, and test
make migrate-list  # show the current schema version
make migrate-down  # roll back one migration

Migrations are stored in the top-level migrations directory and run with Gostgrator's PostgreSQL command.

Browser routes

Method Path Purpose
GET / Landing page and service-status fragment
GET, POST /register Registration page and form action
GET, POST /login Login page and form action
POST /logout Revoke the browser session
GET /account Authenticated account page and fragment
GET, POST /todos Authenticated todo page and create action
GET /todos/{id} View an owner-scoped todo permalink
GET /todos/{id}/edit View the edit page or load its HTMX dialog fragment
POST /todos/{id} Edit a todo
POST /todos/{id}/toggle Toggle completion
POST /todos/{id}/delete Delete a todo

Forms work without JavaScript by following 303 See Other redirects. With HTMX enabled, collection mutations replace the colocated todo-list fragment. Todo edits load into a native <dialog>; successful saves directly replace the displayed list or detail card, while validation failures retarget the preserved edit form into the dialog. The same edit URL remains a complete page when JavaScript is unavailable.

JSON API

All data endpoints are explicitly prefixed with /api:

Method Path Purpose
POST /api/register Create an account and return its bearer token
POST /api/login Return an opaque bearer token
POST /api/logout Revoke the current bearer token
GET /api/account Return the authenticated user
GET, POST /api/todos List or create todos
GET, PATCH, DELETE /api/todos/{id} Read, update, or delete one owned todo

Send API credentials as Authorization: Bearer <token>. Huma rejects malformed path, query, and JSON body inputs before a handler runs and returns validation failures as RFC 9457 Problem Details.

Generated API resources are mounted beneath the same /api prefix:

Path Purpose
/api/docs Interactive API documentation
/api/openapi.json OpenAPI 3.1 JSON document
/api/openapi.yaml OpenAPI 3.1 YAML document
/api/openapi-3.0.json OpenAPI 3.0-compatible JSON document
/api/schemas/{name}.json Individual generated JSON Schemas

Authentication security

Passwords are hashed and verified inside PostgreSQL with pgcrypto; plaintext passwords are never stored. Successful registration creates the account and its first session atomically, so browser users enter /todos immediately and API clients receive a bearer token in the registration response. Unknown-account login attempts still perform dummy Blowfish work to reduce account-existence timing differences. Expensive password-hashing operations are bounded in-process so they cannot consume the entire database pool.

A login token has the form gtd_<selector>.<secret>. Only the non-secret selector and a SHA-256 digest of the random 256-bit secret are stored, allowing indexed lookup without retaining reusable token material, scanning every token digest, or spending password-hashing capacity on every authenticated request. Browser tokens are held in HttpOnly, SameSite=Lax cookies, with Secure enabled by default, and cookie-authenticated mutations require a same-origin Origin or Referer. Logout revokes the database record before clearing the cookie.

Tests

Run the complete suite with:

make test

Database-backed route tests run when DATABASE_URL is set. Their package-level TestMain clones the migrated source database, points the test process at the disposable clone, and force-drops it after the package finishes so test fixtures do not accumulate. The source database must have no active sessions while PostgreSQL uses it as a clone template. When DATABASE_URL is unset, database-backed tests skip while unit tests still run.

License

MIT

Directories

Path Synopsis
cmd
server command
Command server runs go-todo's server-rendered web application and JSON API.
Command server runs go-todo's server-rendered web application and JSON API.
pkg
auth
Package auth implements account credential, opaque-token, and authenticated session operations shared by browser and JSON routes.
Package auth implements account credential, opaque-token, and authenticated session operations shared by browser and JSON routes.
config
Package config loads and validates process configuration from environment variables and optional dotenv files.
Package config loads and validates process configuration from environment variables and optional dotenv files.
database
Package database opens the shared PostgreSQL connection pool used by commands and application packages.
Package database opens the shared PostgreSQL connection pool used by commands and application packages.
httpapi
Package httpapi is the public composition facade for go-todo's HTTP server.
Package httpapi is the public composition facade for go-todo's HTTP server.
httpx
Package httpx contains the HTTP transport helpers shared by feature route packages.
Package httpx contains the HTTP transport helpers shared by feature route packages.
models
Package models contains small application records shared across HTTP and web presentation packages.
Package models contains small application records shared across HTTP and web presentation packages.
routes
Package routes composes go-todo's feature-oriented HTTP route packages.
Package routes composes go-todo's feature-oriented HTTP route packages.
routes/account
Package account owns authenticated browser and JSON account routes with their page and refreshable account-card fragment.
Package account owns authenticated browser and JSON account routes with their page and refreshable account-card fragment.
routes/health
Package health owns the process liveness endpoint.
Package health owns the process liveness endpoint.
routes/landing
Package landing owns the public landing route, page, and HTMX fragment.
Package landing owns the public landing route, page, and HTMX fragment.
routes/login
Package login owns browser and JSON login routes with their shared operation, page, and replaceable form fragment.
Package login owns browser and JSON login routes with their shared operation, page, and replaceable form fragment.
routes/logout
Package logout owns browser and JSON token-revocation routes.
Package logout owns browser and JSON token-revocation routes.
routes/register
Package register owns browser and JSON account-registration routes with their shared validation, page, and replaceable form fragment.
Package register owns browser and JSON account-registration routes with their shared validation, page, and replaceable form fragment.
routes/todos
Package todos owns authenticated browser and JSON todo routes, their request parsing, and the server-rendered todo page and fragment.
Package todos owns authenticated browser and JSON todo routes, their request parsing, and the server-rendered todo page and fragment.
security
Package security defines opaque bearer-token primitives and credential policy.
Package security defines opaque bearer-token primitives and credential policy.
testdatabase
Package testdatabase creates disposable PostgreSQL clones for integration tests.
Package testdatabase creates disposable PostgreSQL clones for integration tests.
todos
Package todos implements owner-scoped persistence and validation for todo items.
Package todos implements owner-scoped persistence and validation for todo items.
version
Package version reports build identity for diagnostics and release tooling.
Package version reports build identity for diagnostics and release tooling.
web
Package web embeds browser assets and provides the generic server-side page renderer used by feature route packages.
Package web embeds browser assets and provides the generic server-side page renderer used by feature route packages.
web/layout
Package layout embeds the document shell shared by all server-rendered pages.
Package layout embeds the document shell shared by all server-rendered pages.

Jump to

Keyboard shortcuts

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