--- page-title: "How to build a fullstack application with Go, Templ, and HTMX - DEV Community" url: https://dev.to/hackmamba/how-to-build-a-fullstack-application-with-go-templ-and-htmx-4444 date: "2024-07-02 21:52:39" --- Go is a statically typed, compiled high-level programming language for building systems, command-line interfaces (CLI), and more. It is typically designed for use on the backend; however, there are times when you want to use the same language to build a full-stack application with a functional backend and a visual frontend. In most cases, Go developers opt for frontend frameworks/libraries like React, Vue, Angular, etc., to build the frontend part of the application. This means they must learn JavaScript/TypeScript, framework-specific paradigms, and other frontend-related overheads. In this guide, you’ll learn how to build a fullstack application with Go using Templ, HTMX, and Xata. ## [](https://dev.to/hackmamba/how-to-build-a-fullstack-application-with-go-templ-and-htmx-4444#technology-overview)Technology Overview **Templ**: is a templating engine that lets you build HTML with Go. It also lets you use Go syntax like `if`, `switch`, and `for` statements to build a robust frontend. You will use Templ to build reusable components and pages for the frontend. **HTMX**: is a frontend library that lets you access modern browser features directly using HTML rather than JavaScript. You will use HTMX to process the form submission and perform other dynamic operations. **Xata**: is a serverless database with analytics and free-text search support that makes a wide range of applications easy to build. ## [](https://dev.to/hackmamba/how-to-build-a-fullstack-application-with-go-templ-and-htmx-4444#prerequisites)Prerequisites To follow along with this tutorial, the following are needed: - [Go version 1.20 or higher installed](https://go.dev/dl/) - Basic understanding of Go - Xata account. [Signup is free](https://app.xata.io/signin?mode=signup?utm_source=fullstackwriter&utm_medium=fullstackwriter-blog) ## [](https://dev.to/hackmamba/how-to-build-a-fullstack-application-with-go-templ-and-htmx-4444#getting-started)Getting started To get started, you need to install Templ binary. The binary generates Go code from a Templ file. ``` go install github.com/a-h/templ/cmd/templ@latest ``` Enter fullscreen mode Exit fullscreen mode Create a directory. ``` mkdir go_fullstack && cd go_fullstack ``` Enter fullscreen mode Exit fullscreen mode Next, initialize a Go module to manage project dependencies. ``` go mod init go-fullstack ``` Enter fullscreen mode Exit fullscreen mode Finally, we proceed to install the required dependencies with: ``` go get github.com/gin-gonic/gin github.com/a-h/templ github.com/joho/godotenv ``` Enter fullscreen mode Exit fullscreen mode `github.com/gin-gonic/gin` is a framework for building web applications. `github.com/a-h/templ` is the Templ library used in the project. `github.com/joho/godotenv` is a library for loading environment variables. ## [](https://dev.to/hackmamba/how-to-build-a-fullstack-application-with-go-templ-and-htmx-4444#structuring-the-application)Structuring the application To do this, create a `cmd`, `internals`, and `views` folder in our project directory. `cmd` is for structuring the application entry point. `internals` is for structuring API-related files. `views` is for structuring frontend-related files. ## [](https://dev.to/hackmamba/how-to-build-a-fullstack-application-with-go-templ-and-htmx-4444#setup-the-database-on-xata)Setup the database on Xata Log into the [Xata workspace](https://app.xata.io/workspaces) and create a `todo` database. Inside the `todo` database, create a `Todo` table and add a `description` column of type `String`. [![create project](https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsnohlapfqs2x519j2klo.png)](https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsnohlapfqs2x519j2klo.png) ## [](https://dev.to/hackmamba/how-to-build-a-fullstack-application-with-go-templ-and-htmx-4444#get-the-database-url-and-set-up-the-api-key)Get the Database URL and set up the API Key To get the database URL, click the **Get code snippet** button and copy the URL. Then click the **API Key** link, add a new key, save and copy the API key. [![](https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpgs4uphc5u9sewh2wf86.png)](https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpgs4uphc5u9sewh2wf86.png) [![](https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffhi01luxfz88eh9hy5cm.png)](https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffhi01luxfz88eh9hy5cm.png) **Setup environment variable** Create a `.env` file in the root directory and add the copied URL and API key. ``` XATA_DATABASE_URL= XATA_API_KEY= ``` Enter fullscreen mode Exit fullscreen mode ## [](https://dev.to/hackmamba/how-to-build-a-fullstack-application-with-go-templ-and-htmx-4444#build-the-application-frontend)Build the application Frontend To build the frontend, you’ll use Templ and HTMX to structure the application and add dynamism to a Todo application. ## [](https://dev.to/hackmamba/how-to-build-a-fullstack-application-with-go-templ-and-htmx-4444#create-the-application-components)Create the application components Inside the `views` folder, create a `components/header.templ` file and add the snippet below: ``` package components templ Header() { GO Fullstack } ``` Enter fullscreen mode Exit fullscreen mode The snippet creates a `Header` component and adds HTMX and TailwindCSS CDNs. TailwindCSS is a low-level framework for styling. Next, create a `components/footer.templ` file to create the application footer and style using TailwindCSS classes. ``` package components templ Footer() {
© Go Fullstack
} ``` Enter fullscreen mode Exit fullscreen mode Finally, create an `index.templ` file inside the same `views` folder and add the snippet below: ``` package views import ( "fmt" "go_fullstack/views/components" ) type Todo struct { Id string Description string } templ Index(todos []*Todo) { @components.Header()
// FORM PROCESSING
// LOOP THROUGH THE TODOS
    for _, todo := range todos {
  • Todo item { todo.Id }

    { todo.Description }

    Edit

  • }
@components.Footer() } ``` Enter fullscreen mode Exit fullscreen mode The snippet above does the following: - Imports the required dependencies - Creates a `Todo` struct to represent the response data coming from the backend - Creates an `Index` component that uses the `Header` and `Footer` components to structure the page. Then, it uses the HTMX attributes to process form submissions and deletion of todos by calling the respective endpoints `/` and `/{todo.Id}` ## [](https://dev.to/hackmamba/how-to-build-a-fullstack-application-with-go-templ-and-htmx-4444#generating-go-files-from-the-templ-files)Generating Go files from the Templ files Next, use the Templ binary you installed earlier to Generate Go codes from the views created above by running the command below in your terminal: ``` templ generate ``` Enter fullscreen mode Exit fullscreen mode After you run this command, you’ll see new Go files generated for each view. You use generated file to render your frontend in next section. [![Generated files](https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0n91b8w6mchs03qq761h.png)](https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0n91b8w6mchs03qq761h.png) > The generated files are not to be edited. ## [](https://dev.to/hackmamba/how-to-build-a-fullstack-application-with-go-templ-and-htmx-4444#putting-it-together-and-building-the-backend)Putting it together and building the backend With that done, you can use it to build the backend and render the required page. ## [](https://dev.to/hackmamba/how-to-build-a-fullstack-application-with-go-templ-and-htmx-4444#create-the-api-models-and-helper-function)Create the API models and helper function To represent the application data, create a `model.go` file inside the `internals` folder and add the snippet below: ``` package internals type Todo struct { Id string `json:"id,omitempty"` Description string `json:"description,omitempty"` } type TodoRequest struct { Description string `json:"description,omitempty"` } type TodoResponse struct { Id string `json:"id,omitempty"` } ``` Enter fullscreen mode Exit fullscreen mode The snippet above creates a `Todo`, `TodoRequest`, and `TodoResponse` struct with the required properties to describe requests and response types. Finally, create a `helpers.go` file with a reusable function to load environment variables. ``` package internals import ( "log" "os" "github.com/joho/godotenv" ) func GetEnvVariable(key string) string { err := godotenv.Load() if err != nil { log.Fatal("Error loading .env file") } return os.Getenv(key) } ``` Enter fullscreen mode Exit fullscreen mode ## [](https://dev.to/hackmamba/how-to-build-a-fullstack-application-with-go-templ-and-htmx-4444#create-the-application-and-api-routes)Create the application and API routes Create a `route.go` file for configuring the API routes and add the snippet below: ``` package api import "github.com/gin-gonic/gin" type Config struct { Router *gin.Engine } func (app *Config) Routes() { //routes will come here } ``` Enter fullscreen mode Exit fullscreen mode The snippet above does the following: - Imports the required dependency - Creates a `Config` struct with a `Router` property to configure the application methods - Creates a `Routes` function that takes in the `Config` struct as a pointer ## [](https://dev.to/hackmamba/how-to-build-a-fullstack-application-with-go-templ-and-htmx-4444#create-the-api-services)Create the API services With that done, create a `xata_service.go` file for the application and update it by doing the following: First, import the required dependencies and create a helper function: ``` package internals import ( "bytes" "encoding/json" "fmt" "net/http" ) var xataAPIKey = GetEnvVariable("XATA_API_KEY") var baseURL = GetEnvVariable("XATA_DATABASE_URL") func createRequest(method, url string, bodyData *bytes.Buffer) (*http.Request, error) { var req *http.Request var err error' if method == "GET" || method == "DELETE" || bodyData == nil { req, err = http.NewRequest(method, url, nil) } else { req, err = http.NewRequest(method, url, bodyData) } if err != nil { return nil, err } req.Header.Add("Content-Type", "application/json") req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", xataAPIKey)) return req, nil } ``` Enter fullscreen mode Exit fullscreen mode The snippet above does the following: - Imports the required dependencies - Creates required environment variables - Creates a `createRequest` function that creates HTTP requests with the required headers Lastly, add a `createTodoService`, `deleteTodoService`, and `getAllTodosService` methods to create, delete, and get the list of todos. ``` //imports goes here func createRequest(method, url string, bodyData *bytes.Buffer) (*http.Request, error) { //createRequest code goes here } func (app *Config) createTodoService(newTodo *TodoRequest) (*TodoResponse, error) { createTodo := TodoResponse{} jsonData := Todo{ Description: newTodo.Description, } bodyData := new(bytes.Buffer) json.NewEncoder(bodyData).Encode(jsonData) fullURL := fmt.Sprintf("%s:main/tables/Todo/data", baseURL) req, err := createRequest("POST", fullURL, bodyData) if err != nil { return nil, err } client := &http.Client{} resp, err := client.Do(req) if err != nil { return nil, err } defer resp.Body.Close() if err := json.NewDecoder(resp.Body).Decode(&createTodo); err != nil { return nil, err } return &createTodo, nil } func (app *Config) deleteTodoService(id string) (string, error) { fullURL := fmt.Sprintf("%s:main/tables/Todo/data/%s", baseURL, id) client := &http.Client{} req, err := createRequest("DELETE", fullURL, nil) if err != nil { return "", err } resp, err := client.Do(req) if err != nil { return "", err } defer resp.Body.Close() return id, nil } func (app *Config) getAllTodosService() ([]*Todo, error) { var todos []*Todo fullURL := fmt.Sprintf("%s:main/tables/Todo/query", baseURL) client := &http.Client{} req, err := createRequest("POST", fullURL, nil) if err != nil { return nil, err } resp, err := client.Do(req) if err != nil { return nil, err } defer resp.Body.Close() var response struct { Records []*Todo `json:"records"` } decoder := json.NewDecoder(resp.Body) if err := decoder.Decode(&response); err != nil { return nil, err } todos = response.Records return todos, nil } ``` Enter fullscreen mode Exit fullscreen mode ## [](https://dev.to/hackmamba/how-to-build-a-fullstack-application-with-go-templ-and-htmx-4444#create-the-api-handlers)Create the API handlers With that done, you can use the services to create the API handlers. Create a `handler.go` file inside `internals` folder and add the snippet below: ``` package internals import ( "context" "fmt" "go_fullstack/views" "net/http" "time" "github.com/a-h/templ" "github.com/gin-gonic/gin" ) const appTimeout = time.Second * 10 func render(ctx *gin.Context, status int, template templ.Component) error { ctx.Status(status) return template.Render(ctx.Request.Context(), ctx.Writer) } func (app *Config) indexPageHandler() gin.HandlerFunc { return func(ctx *gin.Context) { _, cancel := context.WithTimeout(context.Background(), appTimeout) defer cancel() todos, err := app.getAllTodosService() if err != nil { ctx.JSON(http.StatusBadRequest, err.Error()) return } var viewsTodos []*views.Todo for _, todo := range todos { viewsTodo := &views.Todo{ Id: todo.Id, Description: todo.Description, } viewsTodos = append(viewsTodos, viewsTodo) } render(ctx, http.StatusOK, views.Index(viewsTodos)) } } func (app *Config) createTodoHandler() gin.HandlerFunc { return func(ctx *gin.Context) { _, cancel := context.WithTimeout(context.Background(), appTimeout) description := ctx.PostForm("description") defer cancel() newTodo := TodoRequest{ Description: description, } data, err := app.createTodoService(&newTodo) if err != nil { ctx.JSON(http.StatusBadRequest, err.Error()) return } ctx.JSON(http.StatusCreated, data) } } func (app *Config) deleteTodoHandler() gin.HandlerFunc { return func(ctx *gin.Context) { _, cancel := context.WithTimeout(context.Background(), appTimeout) id := ctx.Param("id") defer cancel() data, err := app.deleteTodoService(id) if err != nil { ctx.JSON(http.StatusBadRequest, err.Error()) return } ctx.JSON(http.StatusAccepted, fmt.Sprintf("Todo with ID: %s deleted successfully!!", data)) } } ``` Enter fullscreen mode Exit fullscreen mode The snippet above does the following - Imports the required dependencies - Creates a `render` function that uses the `Templ` package to render matching template - Creates an `indexPageHandler` function that returns a Gin-gonic handler and takes in the `Config` struct as a pointer. Inside the returned handler, use the `getAllTodosService` service to get the list of todos and then render the appropriate page using the generated code from the views package (frontend) - Creates a `createdTodoHandler` and `deleteProjectHandler` functions that return a Gin-gonic handler and take in the `Config` struct as a pointer. Use the service created earlier to perform the corresponding action inside the returned handler ## [](https://dev.to/hackmamba/how-to-build-a-fullstack-application-with-go-templ-and-htmx-4444#update-the-api-routes-to-use-handlers)Update the API routes to use handlers Update the `routes.go` file with the handlers as shown below: ``` package internals import ( "github.com/gin-gonic/gin" ) type Config struct { Router *gin.Engine } func (app *Config) Routes() { //views app.Router.GET("/", app.indexPageHandler()) //apis app.Router.POST("/", app.createTodoHandler()) app.Router.DELETE("/:id", app.deleteTodoHandler()) } ``` Enter fullscreen mode Exit fullscreen mode ## [](https://dev.to/hackmamba/how-to-build-a-fullstack-application-with-go-templ-and-htmx-4444#putting-it-all-together)Putting it all together Create the application entry point to use to serve the routes. To do this, create a `main.go` file inside the `cmd` folder and add the snippet below: ``` package main import ( "go_fullstack/internals" "github.com/gin-gonic/gin" ) func main() { router := gin.Default() //initialize config app := internals.Config{Router: router} //routes app.Routes() router.Run(":8080") } ``` Enter fullscreen mode Exit fullscreen mode The snippet above does the following: - Imports the required dependencies - Creates a Gin router using the `Default` configuration - Initialize the `Config` struct by passing in the `Router` - Adds the route and run the application on port `:8080` With that done, you can start a development server using the command below: ``` go run cmd/main.go ``` Enter fullscreen mode Exit fullscreen mode ![](https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu9bl4onye652rp5it3d4.gif) The complete source code can be found on [GitHub](https://github.com/Mr-Malomz/go_fullstack). ## [](https://dev.to/hackmamba/how-to-build-a-fullstack-application-with-go-templ-and-htmx-4444#conclusion)Conclusion This post discusses how to build a fullstack application with Go, Templ, HTMX, and Xata. You can extend the application further to support viewing and editing todos. These resources may also be helpful: - [Xata documentation](https://xata.io/docs?utm_source=fullstackwriter&utm_medium=fullstackwriter-blog) - [Templ documentation](https://templ.guide/?utm_source=fullstackwriter&utm_medium=fullstackwriter-blog) - [HTMX documentation](https://htmx.org/docs/?utm_source=fullstackwriter&utm_medium=fullstackwriter-blog) - [Go + Xata](https://fullstackwriter.dev/post/xata-go-a-getting-started-guide?category=Golang)