Master Fullstack Golang

Master Fullstack Golang

Go from zero to production ready application while learning the necessary skills to work as a fullstack developer

15 modules
75 lessons
0 min
Start the course
Part 1

Golang Language Fundamentals

Provides the viewer with a quick walk-through of Go's syntax.

Variables, constants and functions

In this episode, we explore Go's strong typing system and how it handles variables through zero values instead of null or empty concepts. We learn about variable declarations, the difference between mutable variables and constants, and when to use the shorthand notation within functions. The episode also covers how to create and work with functions in Go, including how to accept arguments and return multiple values from a single function.

If statements and switch statements

In this episode, we explore control flow in Go programming through if-else statements and switch statements. We cover basic if-else syntax, the importance of limiting nesting to two levels for code readability, and shorthand forms for checking conditions. We also examine switch statements as an alternative to multiple if-else conditions, demonstrating how to use cases, default behavior, and the automatic break feature that prevents fall-through unless explicitly specified with the fall-through keyword.

Slices, maps and looping

In this episode, we explore Go's data structures for working with sequences of elements, including arrays (fixed size) and slices (dynamic size). We learn how slices are built on top of arrays and can grow dynamically using the append function. The episode also covers maps as key-value data structures and demonstrates various ways to loop through these collections using Go's for keyword with different syntax patterns.

Structs and interfaces

In this episode, we explore Go's approach to composition over inheritance, where structs are combined to build complex data structures like a bank system with users, accounts, and transactions. We learn how to add methods to structs, such as a withdraw function, and discover how Go's duck typing enables powerful interface-based programming. The key takeaway is that interfaces should be discovered organically as patterns emerge across different structs, rather than being created preemptively.

Packages, modules and privacy

In this episode, we explore Go's package system and visibility rules. We learn that package main with func main serves as the program's entry point, and that Go uses naming conventions rather than keywords to control access—lowercase names are private to their package while uppercase names are public. We also cover how to organize code into separate packages, import them using the module name from go.mod, and the importance of being conservative about what we expose from our packages.

Error handling and observability

In this episode, we explore Go's error handling and why the "if error != nil" pattern is actually a good thing. We learn that Go forces you to deal with errors where they occur rather than through catch-all functions, making it clear where things went wrong. The episode demonstrates how to handle errors at different layers of an application, when to log errors, and how to use tools like `errors.Is` and `errors.As` to examine specific error types and provide better feedback to both users and developers.

Go routines and channels

In this episode, I introduce Go routines and channels as Go's approach to concurrency, allowing multiple functions to make progress simultaneously. I demonstrate how to create go routines using the `go` keyword and explain how channels enable communication between go routines and the main program through blocking operations. I show a practical worker pattern example where multiple go routines can process data chunks independently, though I note that most web development tasks won't require direct use of go routines since many packages already handle concurrency under the hood.

Go's standard library

MVC & Application structure

Walks the viewer through the model-view-controller architecture and sets up the base of the application structure.

What's a model

In this episode, we explore the concept of models in Go beyond simple database table mappings, treating them as core system components that encapsulate business logic and data validation. We demonstrate how to create user and admin models with find and create functions, emphasizing that Go's type system allows us to enforce distinctions between conceptually different entities even when they might share a database table. We also clarify the difference between business logic (data shape and validation, which belongs in the model layer) and application logic (user operations and permissions, which belongs in controllers or services). Finally, we build a simple article model with dummy data to illustrate these concepts before connecting to a real database in future episodes.

What's a view

In this episode, the instructor explains how to build user interfaces for a Go web application using server-side rendering. While Go's standard html/template package works for embedding data into HTML, it lacks type safety and IDE support. To address these limitations, the instructor introduces Templ, a type-safe templating library inspired by JSX that stays entirely within Go, generating plain Go functions that return safe HTML while providing full compiler support and IDE assistance.

What's a controller

In this episode, we explore controllers as the traffic directors of a web application that receive requests, call models for data, and pass results to views. We start by building a simple controller using Go's standard library, then refactor it using the Echo framework to reduce boilerplate code. Echo provides helpful abstractions through its context object, making it easier to handle requests, extract route parameters, and manage errors, while maintaining the same fundamental pattern of receiving requests, calling models, and rendering views.

Separation of concerns

In this episode, we explore the principle of separation of concerns, which ties together the Model-View-Controller architecture. We learn that each component should have one clear responsibility: models handle data, views handle presentation, and controllers coordinate between them. This separation makes applications easier to maintain and grow, allowing us to modify one part without unintentionally breaking others. Understanding these boundaries helps us build more robust and scalable systems.

Router and routes

In this episode, we formalize URL routing by creating a dedicated routes package to centralize path definitions and avoid scattering magic strings throughout the codebase. We define routes for the homepage and article pages with dynamic ID parameters, then update the home view to reference these centralized routes using string replacement and conversion functions. Finally, we build a router package that configures Echo by registering our routes to their corresponding controllers, creating a clean structure that we can expand as our application grows.

Laying the foundations

In this episode, we lay the foundations of the application architecture by exploring the Model-View-Controller (MVC) pattern and how to adapt it for Go. We discuss the three core components—Models for data and business logic, Views for presentation, and Controllers for coordinating requests—and why MVC emphasizes separation of concerns, making code predictable, testable, and maintainable. We then scaffold the base project structure by creating folders for models, views, controllers, commands, routes, and config, setting up a clean foundation for building a real-world Go application in upcoming episodes.

Configuration and secrets

In this episode, we set up environment-based configuration for our application using environmental variables and Go structs. We create a config package that reads values like app host, port, environment, and project name from a .env file, using struct tags and the env package to parse them automatically. We also establish global constants for development and production environments, add a method to generate TCP addresses from our config, and prepare our application to handle different settings across development, testing, and production environments without hardcoding values.

Testable entrypoint

In this episode, we create the application entry point by setting up a main.go file in the CMD directory with a main function and a testable run function. We configure an HTTP server with the router, set timeouts, and successfully start the application on localhost:8080, confirming that our blog is working by viewing sample articles in the browser. However, we still need to set up a proper Postgres database before we can build out the full application functionality.

Postgres & database fundamentals

This module introduces PostgreSQL and database fundamentals to the viewer.

Options for installation

In this episode, you'll learn how to set up Postgres as your database for the course using two approaches: installing it directly on your system or running it in a Docker container. The tutorial covers creating Postgres roles and databases, understanding how Postgres authentication works with system users, and demonstrates both installation methods step-by-step. Docker is recommended for easier development setup, while direct installation is preferred for production environments.

Relational database concepts

In this episode, we cover the fundamentals of relational databases, explaining how they store structured data in tables with rows and columns. We explore primary keys for unique identification, foreign keys for connecting tables, and the different types of relationships like one-to-one, one-to-many, and many-to-many. We also discuss normalization for organizing data efficiently and introduce SQL as the language used to define, insert, and query data across related tables.

ACID and data integrity

In this episode, we explore how transactions and ACID properties ensure data integrity in relational databases. We learn that transactions are units of work that either completely succeed or fail, preventing partial updates that could corrupt data. The four ACID guarantees—Atomicity, Consistency, Isolation, and Durability—work together to keep data safe and reliable, even when multiple operations happen simultaneously. Using examples like flight bookings and money transfers, we see why these properties are critical for systems handling important data, and how databases like Postgres implement them through explicit transaction controls.

What about nosql?

In this episode, we explore NoSQL databases like MongoDB and Cassandra, understanding when they're useful for flexibility and scaling versus traditional relational databases. While NoSQL offers performance benefits, it comes with trade-offs like weaker consistency and missing relational features. We discuss why Postgres is the best choice for our project, as it combines relational reliability with NoSQL flexibility through JSON support, giving us the best of both worlds.

A database package

In this episode, we create a database package for our Go application that handles the connection to Postgres. We build a psql struct with a connection pool using pgxpool, which efficiently manages database connections by keeping them open and ready to use instead of constantly opening and closing them. We set up a NewPSQL function that takes a context and database URI from our config, establishes the connection pool, and returns the struct. Finally, we wire this into main.go to ensure a database connection is established when the application starts, preparing it for future queries and data operations.

Part 2

Building the article view

We build the html structure for the article view and starts breaking code into components.

Making things pretty and responsive

We setup tailwind, talk about responsive design and make the blog presentable to real readers.

Creating and managing articles

We build out a simple admin where we can manage the articles of the blog.

Authentication and authorization

This module teaches you authentication, best practices and shows you how to roll your own auth. Completely Safe.

Modern hypermedia

Introduces the viewer to htmx and datastar, discusses "modern" frontend frameworks and adds interactivity to the blog and admin.

Preparing for production

We cover seo optimizations, caching views and static assets as well as error pages and logging strategy so we safely can go to production.

Part 3

Setting up a secure VPS

In this module we cover how to configure a virtual private server so that it's secure, talk DNS and how to reverse proxy using Caddy.

Deploying a binary

We go through how to setup a database on the VPS and then deploy the binary version of our blog.

Deploying a docker image

We cover how to host our application using docker instead of systemd and setting up traefik as our reverse proxy.