Pages related to “How to Build a Web App with Gin”
Testing Generated HTML with goquery
This is the twelfth in a series of articles about writing a small reading list app in Go for personal use.
When I first introduced tests for this app, I showed a strategy of checking for “fragments” in the body of the page – these are just strings, including HTML markup, that the test will verify are present in the generated page.
This approach works, but it’s fragile: trivial changes to a generated page like spaces or newlines can trigger test failures that don’t indicate real bugs in the app. The tests will only be failing because they’re too tightly coupled to the output format.
In this post I’ll show a better way to validate the contents of generated pages using the goquery package.
Session Management with Gin
This is the eleventh in a series of articles about writing a small reading list app in Go for personal use.
When a user adds a new book to the app, they just get a page-refresh back to the index page. There’s nothing that says “hey it worked”. And if the user has more than fifteen books in their list, the new book won’t even show up on the first page, so they have to flip to the back to make sure it’s there.
In this article I’ll show how to provide that user feedback using flash messages. In order to show flash messages, some kind of session management is needed, so I’ll show that too.
Using Go's Fuzz Testing with Gin
This is the tenth in a series of articles about writing a small reading list app in Go for personal use.
In the last article we looked at pagination, and I promised to share an approach to testing the pagination support with Go 1.18’s fuzzing support. That’s what we’re going to look at today.
At the end of this article you will:
- see how to run fuzz testing against a function
- see why you might want to refactor a function for more effective fuzz testing
How to do Pagination with Gin
This is the ninth in a series of articles about writing a small reading list app in Go for personal use.
Last week we wrote a tool to import books from a CSV file exported from a service like Goodreads to our book database. And we found that it takes about ten times longer to render the book listing page when there are 600 books than when there are three books.
To keep page generation times shorter, we’ll use a strategy called “pagination” to split the book listing up into multiple pages, with each page having a limited number of books on it.
When we are done with this article, we’ll have:
- the books list paginated so that it only shows 15 books at a time
- with multiple pages to show all the books
- and page-by-page navigation links at the bottom of the page
Importing from CSV to SQLite in Go
This is the eighth in a series of articles about writing a small reading list app in Go for personal use.
Sometimes there are operations that need to be done on the app that don’t need to be built into the frontend. Administrative process like backups, database migrations, or – the thing we’re going to do today – bulk data import.
I’ve got a CSV dump of my Goodreads books, and I want to import those into Aklatan’s db. I only need to do this once, so I don’t need to mess around with making a web form for it. It just needs a backend administrative command to perform the import as a one-off process.
By the end of this post we’ll have:
- a command that loads data from the Goodreads CSV export into akalatan.db
- knowledge of how to use raw SQL – we won’t use Gorm for this task
And we’ll do it in less than 75 lines of code.
Using CSS with Gin
This is the seventh in a series of articles about writing a small reading list app in Go for personal use.
So far we’ve built a sort-of usable app with Gin. We can see the list of books we have in the system, and we can add new books. But there are some serious usability problems:
- there’s no link to the “New Book” page, or to the “Book List” page – you have to know the URLs and type them in directly
- both the input form and the list suffer from poor layout, almost to the point of being unreadable
In this article we will fix those issues and set up a foundation so that future work will have established patterns for styling.
When we’re done you’ll have:
- external CSS and a way to reference it from the pages in the app
- a navbar to be able to easily click around the app
- some options for managing that external CSS with the app’s deployment
Fair warning: I’m not a CSS expert, so don’t assume that anything I’m doing with the actual CSS is “the right way”. I’m more interested in demonstrating the mechanisms and patterns that we can use for integrating these pieces. When I need stuff to actually look nice and be maintainable and scalable I talk to real designers.
Validating Forms With Gin
This is the sixth in a series of articles about writing a small reading list app in Go for personal use.
When we created a form to add new books, there were a couple of data-validation issues that I said I would handle in a future article. The future is now!
As we saw in that article, Gin has support for easily binding form data to a struct. It also has a fairly rich set of form validation capabilities, provided by go-playground/validator. Let’s explore those now.
At the end of this article, you will:
- have better data validation for the form
- have better error reporting for data validation errors
Improve CI with Static Analysis
This is the fifth in a series of articles about writing a small reading list app in Go for personal use.
A big part of developing quality software, especially in larger projects, is to make sure that your process, tools, and workflows can scale with the size of the project. The project we’re working on is tiny in the grand scheme of things, but let’s take this week to “sharpen the saw” a little bit and improve our process before diving back into functionality next week.
Earlier this week I shared some rules for semgrep, a static analyzer that we can use to find defects in Go web apps. Today we will integrate that into aklatan’s Makefile and CI pipeline.
At the end of this article you’ll:
- have semgrep with custom rules integrated into the CI pipeline
- understand the pros and cons of maintaining the rules alongside the application code (as opposed to keeping them in a separate repo)

A water pipeline in the Tagus-Segura Water Transfer in Spain. Photo by flicker user M.Peinado CC-BY
How to Handle Forms with Gin
This is the fourth in a series of articles about writing a small reading list app in Go for personal use.
(Don’t be scared off by the length – there’s a lot of test code that’s shown multiple times due to enhancements.)
This article builds the C of our CRUD app:
- add a template with a form to enter books
- add routes to GET and POST that template
By the end of this article you’ll have:
- a page with a form that you can use to add books to the database
- tests around form handling
- a quick workflow hack to speed up testing when writing a test

This is for posting a completely different kind of form. (Photo by RuthAS, CC-BY.)
How to Test Gin Web Handlers
This is the third in a series of articles about writing a small reading list app in Go for personal use.
So far our app can show a page with a list of books that are in our database. It even has a unit test… but not a very good one. Today we’ll look at what it means to do a decent job testing a Gin handler function.
When we’re done you’ll have a test suite that thoroughly exercises the book list handler. You’ll have 100% test coverage of that handler, and you’ll also understand why that’s only a starting point.
Plus a free bonus that neatens up our CI pipeline.

A different kind of test.
Integrating Gorm with Gin
This is the second in a series of articles about writing a small reading list app in Go for personal use.

The British Museum Reading Room. Image by Wikipedia User:Diliff (CC-BY).
Today we are going to make part of the R of our CRUD app:
- add a model (maps to a db table)
- add a template to display books
- add a route that displays that template
- change our default route to redirect
By the end of this article we will be able to load a page that displays the books in the database. (But we’ll still have to manually add books to the database.)