PMDb
Personal Movie Database
This is a project that helped me put together everything I’ve learned at Boot.dev, discover new things about how the web works, uncover new patterns in Go application design, and try out a new tech stack that is relatively hot right now: Turso, Tailwind, Templ, and HTMX.
So, allow me to walk you through the development of PMDb!
Description
Building this project came from a desire to put my back-end skills to the test, string together the technologies I’m interested in trying, and built a connected whole. Thanks to services like Turso and Render, I could even take that idea to its maximum and make the whole project deployed live. (You can check the live deployment but due to Render’s free tier it might take ~20 seconds to load.)
Instead of having to populate my database with every movie detail possible, leveraging an external API seemed like a much better approach. Thanks to the TMDB API, now I can access movie information, posters, cast, and even “now playing” lists. It made me write the app faster, improve the UX, and add new features.
The basic functionality took off, then I added more features until the simple flat code structure didn’t work anymore. This led me to go on a tangent for a couple of days to learn about Go application design patterns and dependency injection in order to refactor my app into a better structure. Eventually I did the refactor which made my code much more well-structured.
Technologies
Architectural Design
This diagram shows a simplified representation of the architecture.
The core of it is a number of services, where each service is defined in its own Go package. A “service” in this context defines routing, handlers, and HTML templates for a related group of pages. For example, the reviews
package defines routes for viewing the review feed and CRUD operations for a specific review. It also includes the HTML templates needed for rendering these pages.
The way services interact with each other is via dependency injection. In each service’s package, the service struct type is defined with dependencies as fields. Dependencies can be other services, database query layer (generated by SQLC), or environment variables. Here is a snippet of the reviews service as an example.
package reviews
// import ...
type Service struct {
auth *auth.Service
tmdb *tmdbapi.Service
db *database.Queries
}
UI
The idea of the front-end part of this stack is to take the “conventional” way of server-side rendering via template engine (used in Rails, Django, etc.) and maximize interactivity and user experience without hurting developer productivity.
Templ is a step up from Go’s standard library templates which has type safety, a better component model, and ability to write Go code inside the templates. TailwindCSS uses utility classes written in-line in the HTML to make CSS easier and more maintainable. Finally, HTMX adds a lot of power and flexibility for UI interactivity by handling AJAX requests. It’s great a great tool for back-end developers because 1) it extends HTML syntax, keeping everything simple and declarative, and 2) it is tied strongly to the client-server model which is etched in back-end developers way of thinking.
Skills
- HTTP server (routing, handlers, middleware)
- HTML templates (Templ)
- Authentication
- Calling 3rd-party APIs
- Concurrency with goroutines
- HTTP cookies
- SQL
- Service-based application design
- Dependency injection
- Automation with GNU Make
- UI interactivity (HTMX)