0xlgmz
GoWeb AppsLearning Project

proj-goTree

A Linktree-style Go web application built as an early project to understand Go, HTTP routing, authentication, templates, and database-backed user profiles.

Role
Design and engineering
Status
Archived learning project
Published
Gonet/httpGorilla MuxGORMPostgreSQLJWTCloudflare TurnstileHTML Templates

proj-goTree was one of my first proper Go projects.

The idea was to build a small Linktree-style application where users could register, manage a public profile, and publish a handful of links from a simple settings page.

It was less about creating a polished product and more about getting hands-on with Go as a backend language. I wanted to understand how a web application fits together in Go: routing, handlers, templates, authentication, database models, email flows, and static assets.

What It Does

At a high level, the application provides:

  • Public profile pages at /user/@{username}.
  • User registration and login.
  • JWT-backed authentication using cookies.
  • Protected account settings.
  • Profile link editing.
  • Profile image upload.
  • User search.
  • Email confirmation.
  • Password reset flows.
  • Cloudflare Turnstile checks on auth forms.
  • PostgreSQL persistence through GORM.

It is a small application, but it touches a lot of the pieces that appear in real web systems.

That made it a useful learning project.

How It Was Built

The application is written in Go and uses net/http with gorilla/mux for routing.

The main entrypoint wires up routes for the public pages, authentication flows, password resets, email confirmation, user search, and protected profile settings.

The protected routes sit under /users/p and use middleware to validate a JWT from the token cookie before allowing access.

The data layer uses GORM with PostgreSQL. On startup, the application opens a database connection and auto-migrates the core models:

  • User
  • FrontProfilePage
  • Permissions
  • PasswordReset
  • RegisterConfirmation

When a user registers, the application creates the user, a profile page, permissions, password reset metadata, and an email confirmation record. That gave me a good first look at how application-level workflows often span multiple tables.

What I Learned

The main thing this project taught me was how explicit Go web development can feel.

With Go, the shape of the application is very visible. A request comes into a route, hits a handler, calls database functions, renders a template, or redirects somewhere else. There is not much magic hiding the flow.

That was useful for learning.

The project helped me understand:

  • How to structure handlers around http.ResponseWriter and *http.Request.
  • How middleware can attach authenticated user data to request context.
  • How cookies and JWT claims work in a simple session flow.
  • How to model user-owned data with GORM.
  • How server-rendered templates can keep a web app simple.
  • How email confirmation and password reset flows need expiry, tokens, and database state.
  • How much glue code exists around even a small user account system.

It also showed me where early projects get rough.

Tradeoffs and Rough Edges

Looking back, there are several things I would now approach differently.

Some configuration was originally too tied to the local environment. Static asset paths and upload directories were hardcoded in places, which makes the app harder to move between machines or deployment targets.

The JWT signing key and Turnstile configuration are examples of settings that should be consistently loaded from environment variables or a secrets manager. The repository has since had work done around environment sanitisation, but the lesson still stands: configuration design matters early.

There are also places where the code mixes user-facing responses, redirects, and application logic in the same handler. That is fine for learning, but in a larger application I would separate validation, persistence, and rendering more deliberately.

The profile model is intentionally simple, with five fixed title/link pairs. That made the project easier to build, but it would not scale well if users needed arbitrary links, ordering, analytics, or richer profile sections.

None of this makes the project a failure. It is exactly the kind of thing an early project should surface.

Why It Was Useful

This project was useful because it forced me to build across the whole stack.

It was not just a route returning JSON. It had templates, forms, cookies, database records, uploaded files, auth checks, email state, and static assets. That made it a better learning exercise than a smaller tutorial-style application.

It also gave me a clearer understanding of what I value in Go:

  • Clear request flow.
  • Simple deployment shape.
  • Strong standard library foundations.
  • Explicit error handling.
  • Easy-to-follow application structure.

The project is not something I would present as a production-ready service today. I would present it as a useful marker in the learning curve.

It was the kind of project that helped me move from reading Go examples to actually building something with Go.

What I Would Improve Now

If I came back to this project, I would focus on:

  • Moving all runtime configuration into environment-driven settings.
  • Replacing fixed profile link fields with a proper link table.
  • Adding tests around auth, profile updates, and password reset flows.
  • Improving cookie security flags.
  • Tightening upload validation.
  • Reducing repeated JWT parsing logic.
  • Splitting larger handlers into smaller service functions.
  • Adding migration management instead of relying only on auto-migrate.
  • Packaging the app for a cleaner deployment path.

Those improvements are less about changing the project goal and more about applying what I have learned since building it.

Takeaway

proj-goTree was an early Go web application built to learn by doing.

It gave me a practical route through the basics of Go backend development: routing, templates, persistence, authentication, middleware, and user workflows.

The code shows the rough edges of an early project, but it also shows the point of building early projects in the first place.

You learn the shape of the problems by putting the pieces together yourself.