🤓 My top 3 Go packages that I wish I'd known about earlier
Tags: go, programming, beginners, news
Published: 2024-03-01 11:36:24

## Introduction

Hello, my DEV friends! 🙌

Today, I'd like to travel back in time, find myself at the very beginning of planning my current project, and advise myself to familiarize myself with these wonderful Go packages…

Unfortunately, I can't do that! But I can introduce you to them. Here we go 👇

<a name="toc"></a>
### 📝 Table of contents

- [Templ](#templ)
- [Otter](#otter)
- [River](#river)

<a name="#templ"></a>
## Templ

{% github github.com/a-h/templ no-readme %}

### A language for writing HTML UI in Go

> ✨ In recent months, I have been developing web projects using **GOTTHA** stack: [Go](https://go.dev) + [Templ](https://templ.guide) + [Tailwind CSS](https://tailwindcss.com) + [htmx](https://htmx.org) + [Alpine.js](https://alpinejs.dev). As soon as I'm ready to talk about all the subtleties and pitfalls, I'll post it on my social networks. 
>
> By the way, you can subscribe! All links are here 👉 https://shostak.dev

Perhaps my favorite Go package in my entire practice of working with this programming language! The authors managed to combine two huge worlds at once:

1. The world of the frontend, which consists almost entirely of JSX-like web frameworks, such as **React.js**;
2. The world of the backend, where we are free to use our familiar Go principles and packages as usual.

Together with **Templ** we can write the following code:

```go
// index.templ

package main

templ hello(name string) {
	if name == "Виктор" {
		<div>Привет, Виктор! Как твои дела сегодня?</div>
	} else {
		<div>Hello, { name }</div>
	}
}

templ greeting(person Person) {
	<div class="greeting">
		@hello(person.Name)
	</div>
}

// ...
```

Next, you just need to start the process of generating all `*.templ` files:

```bash
go run github.com/a-h/templ/cmd/templ@latest generate
```

Which will be converted into a regular Go function and ready for use on your backend. For example, like this:

```go
// main.go

package main

// ...

// Simple user struct.
type Person struct {
	Name string
}

func main() {
	// Create a new user with name.
	user := Person{Name: "John"}

	// Create Templ handler.
	handler := templ.Handler(greeting(user))

	// Serve Templ handler with HTTP server.
	http.Handle("/", handler)

	// Start simple HTTP server.
	http.ListenAndServe(":3000", nil)
}
```

Open browser on `http://localhost:3000` and see the result.

Yes, it's that simple! 😉

> 🔥 **Templ** authors have taken care of supporting popular [IDE](https://templ.guide/commands-and-tools/ide-support) and special mode for [hot-reload](https://templ.guide/commands-and-tools/hot-reload) your `*.templ` files.

If you have never tried **Templ** in your code practice, then **I strongly recommend** doing it right now! It's really worth it.

[↑ Table of contents](#toc)

<a name="#otter"></a>
## Otter

{% github github.com/maypok86/otter no-readme %}

### High performance in-memory cache

I believe that the most important features of **Otter** are extremely simple API and autoconfiguration. And it works perfectly!

Just set the parameters you want in the builder and enjoy. **Otter** is automatically configured based on the parallelism of your application.

Just write like this to create a new cache instance:

```go
// cache/new.go

package cache

// ...

// Cache struct contains all caches.
type Cache struct {
	People otter.CacheWithVariableTTL[string, []Person]
}

func New() (*Cache, error) {
	// Create a new cache instance for people.
	peopleCache, err := otter.MustBuilder[string, []Person](100_000).WithVariableTTL().Build()
	if err != nil {
		return nil, err
	}

	return &Cache{
		People: peopleCache,
	}, nil
}
```

And now, you can use it on your app, like this:

```go
// main.go

// ...

func main() {
	// ...

	// Get people from database.
	people, err := dbexecutor.SelectPeople(context.Background(), "postgres://...", "SELECT * FROM people")
	if err != nil {
		slog.Error("can't select people from DB", "details", err.Error())
		return err
	}

	// Create a new cache instance.
	c, _ := cache.New() // don't forget to check errors!

	// Save people to the cache with TTL=180m.
	if c.People.Set("people", people, 180 * time.Minute) {
		slog.Info("people are successfully saved to the in-memory cache")
	}

	// ...

	// Get people from the cache.
	cachedPeople, isCached := c.People.Get("people")

	// Check, if data is cached.
	if isCached {
		// Your logic with cached data in cachedPeople here...
	}

	// ...
}
```

I would also like to point out such advantages as:

- **Generics**: yes, you can safely use any comparable types as keys and any types as values;
- **TTL**: expired values will be automatically deleted from the cache;
- **Cost-based eviction**: supports eviction based on the cost of each item;
- **Excellent throughput**: currently, **Otter** is the fastest cache library, with a huge lead over the competition;
- **Great hit ratio**: new S3-FIFO algorithm is used, which shows excellent results.

And benchmark, of course! 😊 

In this benchmark 8 threads concurrently read from a cache configured with a maximum size:

![otter benchmark](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e0b4ci467al2mj0an42l.png)

> 👋 You can find all benchmarks [here](https://github.com/maypok86/benchmarks).

[↑ Table of contents](#toc)

<a name="#river"></a>
## River

{% github github.com/riverqueue/river no-readme %}

### Fast and reliable background jobs in Go 

I'm a big fan of all the new stuff that the Go programmer community is developing. **River** is one of them.

It represents a combination of modern Go practices and ease of use, even if you have never worked with similar tools before.

> 🔥 Atomic, transaction-safe, robust job queueing for Go applications. Backed by PostgreSQL and built to scale.

And yet, this package has one of the best documentation on the market. Just read how the authors [describe](https://riverqueue.com/docs/reliable-workers) writing of reliable workers' principle! Love it 😘

Start with add a simple job that sorts a slice of strings:

```go
// jobs/sort.go

package jobs

// ...

// sortArgs is a struct that contains a slice of strings to sort.
type sortArgs struct {
	// Strings is a slice of strings to sort.
	Strings []string `json:"strings"`
}

// Kind returns the kind of the job.
func (sortArgs) Kind() string { return "sort" }

// SortWorker is a worker that sorts a slice of strings.
type SortWorker struct {
	river.WorkerDefaults[sortArgs]
}

// Work sorts a slice of strings.
func (w *SortWorker) Work(_ context.Context, job *river.Job[sortArgs]) error {
	slices.Sort(job.Args.Strings)
	fmt.Printf("Sorted strings: %+v\n", job.Args.Strings)
	return nil
}

// ...
```

Next, let's create a new **River** queue with workers pool:

```go
// queue/new.go

package queue

// ...

func pool() (*river.Workers, error) {
	// Create a new River instance.
	w := river.NewWorkers()

	// Add workers to River.
	_ = river.AddWorkerSafely(w, &jobs.SortWorker{}) // don't forget to check errors!

	return w, nil
}

func New() (*river.Client[pgx.Tx], error) {
	// Create workers pool.
	workers, _ := pool() // don't forget to check errors!

	// Create and return a new River client
	// (for PostgreSQL over pgx/v5, by the way).
	return river.NewClient(riverpgxv5.New(db), &river.Config{
		Queues: map[string]river.QueueConfig{
			river.QueueDefault: {MaxWorkers: 100},
		},
		Workers: workers,
	})
}
```

And now, you can use it on your app, like this:

```go
// main.go

// ...

func main() {
	// ...

	// Create a sample data to sort.
	str := []string{"foo", "baz", "bar"}

	// Create a new River instance.
	q, _ := queue.New() // don't forget to check errors!

	// Set job to queue.
	_, err := q.Insert(context.Background(), jobs.SortArgs{Strings: str}, nil)
	if err != nil {
		slog.Error("can't insert job to River queue", "details", err.Error())
	}

	// ...
}
```

That's all you have to do! 💥 You can create arbitrarily complex background jobs that **River** can easily complete for you.

> 💡 **River** is still under rapid development, but I recommend trying it now!

[↑ Table of contents](#toc)

<a name=""></a>
## And what do you use?

**Tell us about it in the comments**! It will be very interesting to find new Go packages and tools for yourself.

Peace to all and a successful code! ❤️

[↑ Table of contents](#toc)

<a name=""></a>
## Photos and videos by

- Vic Shóstak https://github.com/koddr

<a name=""></a>
## P.S.

If you want more articles (like this) on this blog, then post a comment below and subscribe to me. Thanks! 😻

And of course, you can help me make developers' lives even better! Just connect to one of my projects as a contributor. It's easy!

My main projects that need your help (and stars) 👇

- 🔥 [Gowebly][gowebly_url]: A next-generation CLI tool to easily build amazing web applications with Go on the backend, using htmx & hyperscript and the most popular CSS frameworks on the frontend.
- ✨ [Create Go App][cgapp_url]: Create a new production-ready project with Go backend, frontend and deploy automation by running one CLI command.

Other my small projects: [yatr][yatr_url], [gosl][gosl_url], [json2csv][json2csv_url], [csv2api][csv2api_url].

<!-- Footer links -->

[gowebly_url]: https://github.com/gowebly/gowebly
[cgapp_url]: https://github.com/create-go-app
[yatr_url]: https://github.com/koddr/yatr
[gosl_url]: https://github.com/koddr/gosl
[csv2api_url]: https://github.com/koddr/csv2api
[json2csv_url]: https://github.com/koddr/json2csv