GO + HTMX - A Crud implementation

As I was exploring htmx, I wanted to try out an interactive workflow with it. A classic example is a data list, modal to create / update data and a delete. Setup with Gofiber Initialize go module and install dependencies go mod init htmx-crud go get github.com/gofiber/fiber/v2 go get github.com/gofiber/template/html/v2 Setup Server html template engine main.go package main import ( "log" "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/log" "github.com/gofiber/template/html/v2" ) func main() { // Setup html template engine engine := html.New("./views", ".html") app := fiber.New(fiber.Config{ Views: engine, }) // Get request to render index page app.Get("/", func(c *fiber.Ctx) error { // Render index template return c.Render("index", nil, "layouts/main") }) // Start server log.Fatal(app.Listen(":3000")) } This is a simple server setup with gofiber. We are using html template engine to render the html. ...

Benchmarking GO vs Dotnet vs Nextjs

I was curious about how the server side rendering in terms of req/sec can go, dotnet and nextjs perform. So I created a simple server side rendering application in all three technologies and benchmarked them using wrk. Golang Golang was setup with gofiber and gotemplate. It performed well with around 14k req/sec. using minimal ram (~21mb) 10 threads and 400 connections Thread Stats Avg Stdev Max +/- Stdev Latency 34.05ms 31.00ms 119.93ms 77.11% Req/Sec 1.40k 411.25 4.61k 68.91% 419491 requests in 30.10s, 638.89MB read Socket errors: connect 0, read 413, write 0, timeout 0 Requests/sec: 13936.30 Transfer/sec: 21.23MB Dotnet Razor Dotnet razor performed well with around 11k req/sec. using a bit over idle ram (~53mb) ...

Razor Pages + HTMX - Handling form submissions.

Htmx allows us to handle async form submissions with ease by using hx-post attribute. In background it will issue a post request to the url specified in hx-post attribute and replace the element with the response. In this post we will try to handle form submissions in razor pages using htmx. Initialize a new razor pages project. dotnet new razor -o htmx-form In Pages/Index.cshtml we setup the layout. @page @using Htmx @model IndexModel @{ ViewData["Title"] = "Home page"; } <div class="row pb-5"> <div class="col"> Form here </div> <div class="col"> Login here </div> </div> Create a partial userform which will be used here and in the post request. ...

Standards - how they form, how I define standards for use cases

What is a standard? Standardization is a process of developing guidelines or way of doing things for common and repeated tasks. Standards are generaly opinionated by experts in the field and are widely accepted by the community. We require standards to follow to have a common ground for collaboration and organization of repeated tasks. Standards for collaboration When multiple people are working on same domain / system, it’s much easier to follow a pattern for specific tasks. Standards for organization ...

HTMX - Handling form submissions.

Htmx allows us to handle async form submissions with ease by using hx-post attribute. In background it will issue a post request to the url specified in hx-post attribute and replace the element with the response. Setup with Gofiber Initialize go module and install dependencies go mod init htmx-form go get github.com/gofiber/fiber/v2 go get github.com/gofiber/template/html/v2 Setup Server package main import ( "log" "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/log" "github.com/gofiber/template/html/v2" ) func main() { // Setup html template engine engine := html.New("./views", ".html") app := fiber.New(fiber.Config{ Views: engine, }) // Get request to render index page app.Get("/", func(c *fiber.Ctx) error { values := fiber.Map{ "EmailErr": "", "PasswordErr": "", "Success": "", "Error": "", } // Render index template return c.Render("index", values) }) // Post request to handle form submission app.Post("/form", func(c *fiber.Ctx) error { // Get form values email := c.FormValue("email") password := c.FormValue("password") // Setup values to be passed to template values := fiber.Map{ "EmailValue": email, "EmailErr": "", "PasswordErr": "", "Success": "", "Error": "", } // Validate form values if email == "" { values["EmailErr"] = "Email is required" } if password == "" { values["PasswordErr"] = "Password is required" } if email != "" && password != "" { values["EmailValue"] = "" values["Success"] = "Validation success" } else { values["Error"] = "Validation error" } // Render index template return c.Render("form", values) }) log.Fatal(app.Listen(":3000")) } Setup Views ...

Nextjs - Setup with Bootstrap

Setup nextjs with bootstrap Initialize nextjs project with typescript pnpm create next-app --typescript Install bootstrap pnpm add bootstrap Add bootstrap css to app/layout.tsx or pages/_app import "bootstrap/dist/css/bootstrap.min.css"; Create a bootstrap client component components/Bootstrap.tsx "use client"; import { useEffect } from "react"; function BootstrapClient() { useEffect(() => { require("bootstrap/dist/js/bootstrap.bundle.min.js"); }, []); return null; } export default BootstrapClient; Add bootstrap client component to app/layout.tsx or pages/_app layout.tsx might look like this. import BootstrapClient from "@/components/boostrap"; import "bootstrap/dist/css/bootstrap.css"; import type { Metadata } from "next"; export const metadata: Metadata = { title: "Create Next App", description: "Generated by create next app", }; export default function RootLayout({ children, }: Readonly<{ children: React.ReactNode; }>) { return ( <html lang="en"> <body>{children}</body> <BootstrapClient /> </html> ); } Exporting as static site. Add the following to next.config.js ...

Go - Template

Let’s check the Go template syntax and essentials. Placeholders The {{ }} is used to denote a placeholder. It can contain a variable, function, control statments like if, for, range etc. a variant of placeholder is {{- -}} which is used to trim the whitespace before the placeholder, {{- trims the whitespace before the placeholder and -}} trims the whitespace after the placeholder. Variables Variables are defined using the {{ $variableName := value }} syntax. The variable can be used in the template using {{ $variableName }}. ...

Go - Options Pattern

In go, it’s really useful pattern to pass options to functions. It’s a common usecase to have optional arguments to a function. I use it mostly to initialize something with default values and optionally override them. Primary idea is to use variadic arguments to pass options to a function. eg: func NewService(options ...ServiceOption) *Service { ... } options ...ServiceOption is the variadic argument. There are two ways to do this. Using a struct with all the options as a field. Using functions with each option value as an argument. Using a struct to pass options Example usage package main type ServiceConfig struct { Color string Size int Type string } func main() { // Initialize with default values s := NewService() // Override some of the default values s = NewService(&ServiceConfig{ Color: "red", Size: 10, }) } How to setup this options pattern You can use config... to accept any number of args in a function. Let’s create a config section with init config function. ...

Go - Setup simple gofiber server.

Gofiber is a fast lightweight web framework with simple api, inspired by expressjs. The simplicity of the api makes it easy to get started with. It’s also fast and has a lot of features. Setup Create a new project Create a new directory and initialize a go module. go mod init github.com/adharshmk96/gofiber-init Install gofiber go get -u github.com/gofiber/fiber/v2 a main.go with hello world touch main.go package main import "github.com/gofiber/fiber/v2" func main() { // gofiber with default settings app := fiber.New() // handler for root path app.Get("/", func(c *fiber.Ctx) error { return c.SendString("Hello, World 👋!") }) // start server on port 3000 app.Listen(":3000") fmt.Println("Server started on port 3000") } Dockerfile for building the app ...

Benchmarking JSON vs HTML processing functions

This benchmark compares performance of JSON serialization and HTML template rendering. In a server returning HTML or JSON the main difference is with the serialization. So I wanted to know how much difference it makes to return a JSON response vs HTML response. So I just tested json serialization vs html template rendering in Go. Benchmarking Setup The setup is simple, with go test, main_test.go package main import ( "bytes" "encoding/json" "testing" "text/template" "github.com/flosch/pongo2/v6" ) // Example struct to marshal type SimpleStruct struct { Field1 string `json:"field1"` Field2 int `json:"field2"` } // BenchmarkJsonMarshalling tests the performance of JSON marshaling. func BenchmarkJsonMarshalling(b *testing.B) { // setup any necessary data for the benchmark data := SimpleStruct{ Field1: "example", Field2: 123, } // Run the benchmark for i := 0; i < b.N; i++ { _, err := json.Marshal(data) if err != nil { b.Fatal(err) } } } func BenchmarkHTMLRenderingWithPongo(b *testing.B) { tpl, err := pongo2.FromString("Hello {{ name }}!") if err != nil { panic(err) } // Run the benchmark for i := 0; i < b.N; i++ { _, err := tpl.Execute(pongo2.Context{"name": "florian"}) if err != nil { b.Fatal(err) } } } func BenchmarkHTMLRenderingWithTemplate(b *testing.B) { data := map[string]string{ "Field1": "example", } tmpl := template.Must(template.New("test").Parse("Hello {{ .Field1 }}!")) // Run the benchmark for i := 0; i < b.N; i++ { memory := new(bytes.Buffer) err := tmpl.Execute(memory, data) if err != nil { b.Fatal(err) } } } Benchmarking Results BenchmarkJsonMarshalling-14 9746420 103.6 ns/op BenchmarkHTMLRenderingWithPongo-14 2557006 467.0 ns/op BenchmarkHTMLRenderingWithTemplate-14 7220666 168.7 ns/op My Thoughts JSON serialization is bit faster than HTML template rendering. ...