gonull
is a Go package that provides type-safe handling of nullable values using generics. It's designed to work seamlessly with JSON and SQL operations, making it perfect for web services and database interactions.
- 🎯 One generic
Nullable[T]
works with any type - 💡 Omitzero support
- 🔄 Built-in JSON marshaling/unmarshaling
- 📊 SQL database compatibility
- 🔢 Handles numeric values returned as strings by SQL drivers
- 🧩 Works seamlessly with your own alias or enum types
- ✨ Zero dependencies
Nullable[T]
keeps your code concise by using Go generics for any type. You don't need separate wrappers for strings, ints or custom enumerations. Built-in sql.Scanner
and json
support make it easy to integrate with databases and APIs.
type Status string
type Task struct {
ID int
State gonull.Nullable[Status]
}
go get github.com/LukaGiorgadze/gonull/v2
package main
import (
"encoding/json"
"fmt"
"github.com/LukaGiorgadze/gonull"
)
type MyCustomInt int
type MyCustomFloat32 float32
type Person struct {
Name string `json:"name"`
Age gonull.Nullable[MyCustomInt] `json:"age"`
Address gonull.Nullable[string] `json:"address"`
Height gonull.Nullable[MyCustomFloat32] `json:"height"`
IsZero gonull.Nullable[bool] `json:"is_zero,omitzero"` // This property will be omitted from the output since it's not present in jsonData.
}
func main() {
jsonData := []byte(`
{
"name": "Alice",
"age": 15,
"address": null,
"height": null
}`)
var person Person
json.Unmarshal(jsonData, &person)
fmt.Printf("Unmarshalled Person: %+v\n", person)
marshalledData, _ := json.Marshal(person)
fmt.Printf("Marshalled JSON: %s\n", string(marshalledData))
// Output:
// Unmarshalled Person: {Name:Alice Age:15 Address: Height:0 IsZero:false}
// Marshalled JSON: {"name":"Alice","age":15,"address":null,"height":null}
// As you see, IsZero is not present in the output, because we used the omitzero tag introduced in go v1.24.
}
type User struct {
Name gonull.Nullable[string]
Age gonull.Nullable[int]
}
func main() {
// ...
rows, err := db.Query("SELECT id, name, age FROM users")
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
var user User
err := rows.Scan(&user.Name, &user.Age)
if err != nil {
log.Fatal(err)
}
fmt.Printf("ID: %d, Name: %v, Age: %v\n", user.Name.Val, user.Age.Val)
}
// ...
}
See ./examples directory.
Made with contrib.rocks.