Skip to content

Repository files navigation

chat

A web based chat application. The server is written in Go. The pages are rendered on the server with templ, styled with shadcn-templ components, and updated in the browser with htmx.

This is step one. The application shows one page with a button. A click on the button sends a request, and the server answers with an HTML fragment.

Technology

Part Choice Version
Language Go 1.27
Templates templ 0.3
Components shadcn-templ 2.0
Styling Tailwind CSS 4.3.3
Browser updates htmx 4.0.0
Linting golangci-lint 2.13.2
Markup check html-validate 10
Database PostgreSQL 18
Queries sqlc and pgx 1.31.1
Migrations goose 3

The file assets/js/htmx.min.js is htmx 4.0.0. The name carries no version, so read the version here. htmx 4 is not the default version on npm, so every reference must name 4.0.0.

All static files are compiled into the binary with go:embed. The program runs with no other files next to it.

Prerequisites

Install Go 1.27 or later. Then install the two command line tools:

go install github.com/go-task/task/v3/cmd/task@latest
go install github.com/axadrn/shadcn-templ/v2/cmd/shadcn-templ@latest

The templ command is a Go tool of this module, so go tool templ always uses the version in go.mod. The Tailwind command is downloaded to ./bin by the tools task, and git ignores that directory.

Development container

The repository holds a devcontainer. Open the folder in an editor that supports devcontainers, or start it from the command line:

devcontainer up --workspace-folder .

The container is the Go 1.27 image. It installs task and shadcn-templ, downloads the module dependencies and the Tailwind binary, and forwards the ports 8080 and 7331.

With the devcontainer you can skip the next section.

Development

Start the database and write the development users:

task db:up
task seed

Start the server and the file watchers:

task dev

Open http://localhost:8080. The templ watcher reloads the browser after each change.

Build the binary:

task build

Check the code before you commit:

task fix
task lint
task fmt
task html

The page follows the light or dark theme of the operating system. There is no switch.

The project follows the Chrome modern web guidance. The Baseline target is Baseline Widely available. The server sends a strict Content Security Policy, packs text answers with gzip, and serves every static file under an address that carries a hash of the content.

Run task alone to see all tasks.

Signing in

A visitor types an email address on /login.

  • A person with a passkey signs in with the passkey.
  • A person without one receives a six digit code by SMS, and the page then offers to create a passkey.

There is no SMS provider yet. The server prints the message to standard output, so the code stands in the terminal that runs task dev.

The seed writes two users, and both carry the same phone number. Sign in as arne@ejbygruppe.dk or test-jorgensen@ejbygruppe.dk.

A passkey belongs to one address. ORIGIN, or the flag -origin, must be the address that the browser shows. The default is http://localhost:8080.

Conversations

A conversation carries a subject and a fixed set of people. A signed in person sees their conversations in the sidebar, starts a new one, reads it and writes in it. The list counts the messages that this person has not read, and the sidebar also holds the name and the way out.

The page asks the server for new messages every three seconds, so an answer from somebody else appears without a reload.

The container image

task image

That builds the binaries and the image for amd64 and arm64 and publishes nothing. A push to main that passes the CI workflow writes the next patch tag and pushes the image to ghcr.io/spejder/chat.

The image starts from scratch and carries one file, the binary, which holds every static file and the root certificates. Give it DATABASE_URL and ORIGIN:

docker run --rm -p 8080:8080 \
  -e DATABASE_URL=postgres://chat:chat@postgres:5432/chat?sslmode=disable \
  -e ORIGIN=https://chat.example.com \
  ghcr.io/spejder/chat:latest

Continuous integration

GitHub Actions builds and tests the project, and runs the linter, on every push to main and on every pull request. The workflow calls the same tasks that you call, so a green build on your machine means a green build there.

Add a component

shadcn-templ add <name>
task generate
go mod tidy

The command copies the source into internal/components, so the project owns the code and you can edit it.

Layout

Path Content
cmd/chat The program that starts the server
internal/server Routes and HTTP handlers
internal/web Page templates and fragments
internal/components shadcn-templ components
internal/utils Helpers that the components need
assets CSS, JavaScript and the embed declaration

About

Experimental chat service. Work in progress.

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages