Installing google/uuid
Go has no UUID package in the standard library. The de-facto standard is github.com/google/uuid:
go get github.com/google/uuid
import "github.com/google/uuid"
Generating UUIDs
package main
import (
"fmt"
"github.com/google/uuid"
)
func main() {
// UUID v4 — random, 122 bits of cryptographic randomness
id := uuid.New()
fmt.Println(id) // 550e8400-e29b-41d4-a716-446655440000
fmt.Println(id.String()) // same, with hyphens
fmt.Printf("%x\n", id[:]) // raw hex, no hyphens
// UUID v7 — time-ordered, index-friendly (google/uuid v1.6.0+)
id7, err := uuid.NewV7()
if err != nil {
panic(err)
}
fmt.Println(id7) // 018f3c4e-7a21-7b3c-9d4e-5f6a7b8c9d0e
}
uuid.UUID is a [16]byte array — not a string. It carries version and variant information and implements fmt.Stringer, json.Marshaler, json.Unmarshaler, encoding.TextMarshaler, and encoding.TextUnmarshaler out of the box.
uuid.NewV7() returns an error in the unlikely event that crypto/rand fails. In practice this never happens in a healthy OS environment, but Go’s error handling idiom means you should check it.
Parsing and validating
raw := "550e8400-e29b-41d4-a716-446655440000"
// Parse — returns error if invalid
id, err := uuid.Parse(raw)
if err != nil {
// not a valid UUID
}
// Check version
fmt.Println(id.Version()) // 4
// Parse without hyphens
id2, err := uuid.Parse("550e8400e29b41d4a716446655440000")
// Must-parse (panics on invalid input — only for trusted constants)
id3 := uuid.MustParse("550e8400-e29b-41d4-a716-446655440000")
Use uuid.Parse at API boundaries — HTTP handlers, message consumers, CLI input — where the input cannot be trusted. Use uuid.MustParse only for hard-coded constants where a bad value means a programming error, not bad user input.
Nil UUID
The nil UUID (00000000-0000-0000-0000-000000000000) represents the absence of a value:
var empty uuid.UUID // zero value — all bytes are 0x00
fmt.Println(empty == uuid.Nil) // true
fmt.Println(empty.String()) // 00000000-0000-0000-0000-000000000000
// Check for nil
if id == uuid.Nil {
// no UUID set
}
Use the nil UUID as a sentinel value instead of a pointer (*uuid.UUID) when you want to avoid heap allocation for optional IDs.
JSON serialisation
uuid.UUID marshals to and from a hyphenated string automatically:
type Event struct {
ID uuid.UUID `json:"id"`
Name string `json:"name"`
}
// Marshal
e := Event{ID: uuid.New(), Name: "page_view"}
data, _ := json.Marshal(e)
// {"id":"550e8400-e29b-41d4-a716-446655440000","name":"page_view"}
// Unmarshal — validates the UUID string
var incoming Event
err := json.Unmarshal(data, &incoming)
No custom marshaller needed — uuid.UUID implements the json.Marshaler and json.Unmarshaler interfaces directly.
PostgreSQL with pgx
pgx (the recommended PostgreSQL driver for Go) supports uuid.UUID natively in v5:
import (
"context"
"github.com/jackc/pgx/v5/pgxpool"
"github.com/google/uuid"
)
pool, _ := pgxpool.New(context.Background(), os.Getenv("DATABASE_URL"))
// Insert with a v7 UUID
id, _ := uuid.NewV7()
_, err := pool.Exec(ctx,
"INSERT INTO events (id, name) VALUES ($1, $2)",
id, "checkout",
)
// Query — scan directly into uuid.UUID
var eventID uuid.UUID
err = pool.QueryRow(ctx,
"SELECT id FROM events WHERE name = $1", "checkout",
).Scan(&eventID)
pgx handles the conversion between uuid.UUID and PostgreSQL’s native uuid type automatically. No manual hex encoding or string conversion needed.
database/sql with lib/pq
For projects using the standard database/sql interface:
import (
"database/sql"
_ "github.com/lib/pq"
"github.com/google/uuid"
)
db, _ := sql.Open("postgres", os.Getenv("DATABASE_URL"))
id, _ := uuid.NewV7()
// Insert
_, err := db.ExecContext(ctx,
"INSERT INTO events (id, name) VALUES ($1, $2)",
id.String(), "checkout", // pass as string — lib/pq maps it to uuid
)
// Query
var rawID string
err = db.QueryRowContext(ctx,
"SELECT id FROM events WHERE name = $1", "checkout",
).Scan(&rawID)
eventID, err := uuid.Parse(rawID)
With lib/pq, pass the UUID as a string and parse it back after scanning. pgx v5 is cleaner for new projects.
GORM
GORM uses string for UUID primary keys by default, but you can use uuid.UUID directly:
import (
"github.com/google/uuid"
"gorm.io/gorm"
)
type User struct {
ID uuid.UUID `gorm:"type:uuid;primaryKey"`
Name string
}
// Hook to auto-generate UUID v7 before create
func (u *User) BeforeCreate(tx *gorm.DB) error {
if u.ID == uuid.Nil {
id, err := uuid.NewV7()
if err != nil {
return err
}
u.ID = id
}
return nil
}
// Usage
user := User{Name: "Alice"}
db.Create(&user) // BeforeCreate sets user.ID automatically
GORM’s BeforeCreate hook runs before each insert — this is the idiomatic place to set UUID primary keys when not using a database-level default.
Structured logging with slog
Go 1.21 added log/slog for structured logging. UUID values log as strings automatically via fmt.Stringer:
import (
"log/slog"
"github.com/google/uuid"
)
requestID, _ := uuid.NewV7()
slog.Info("request received",
"request_id", requestID,
"method", "POST",
"path", "/orders",
)
// {"time":"...","level":"INFO","msg":"request received",
// "request_id":"018f3c4e-7a21-7b3c-9d4e-5f6a7b8c9d0e",
// "method":"POST","path":"/orders"}
Use v7 for request and trace IDs — the embedded timestamp means you can reconstruct event order from the IDs alone, without relying on log line order.
Typed UUIDs
Go’s type system allows domain-specific UUID types that prevent mixing up IDs from different entities:
type UserID uuid.UUID
type OrderID uuid.UUID
func NewUserID() (UserID, error) {
id, err := uuid.NewV7()
return UserID(id), err
}
func NewOrderID() (OrderID, error) {
id, err := uuid.NewV7()
return OrderID(id), err
}
func ProcessOrder(userID UserID, orderID OrderID) { ... }
// This would not compile — type mismatch
// ProcessOrder(orderID, userID)
The compiler catches accidental argument swaps that would otherwise cause silent bugs. The underlying value is still a [16]byte, so there is no runtime overhead.
Common mistakes
- Using
uuid.New()and ignoring sort order for database keys.uuid.New()returns v4 — random. Useuuid.NewV7()for primary keys to keep B-tree inserts sequential. - Storing UUIDs as strings in PostgreSQL. Use the native
uuidcolumn type, notVARCHAR(36). pgx handles the mapping automatically. - Checking
id == uuid.Nilafteruuid.New().uuid.New()panics on acrypto/randfailure rather than returning a nil UUID, so this check is not useful. For functions that return errors (uuid.NewV7()), check the error instead. - Passing
uuid.UUIDtofmt.Sprintf("%s", id)instead ofid.String(). Both work butid.String()is explicit and avoids relying on the%vfallback.
For how UUIDs behave in databases, see UUID in databases. For the JavaScript equivalent, see UUID in JavaScript.
Frequently asked questions
How do I generate a UUID in Go?
Install github.com/google/uuid (go get github.com/google/uuid), then call uuid.New() for a random v4 or uuid.NewV7() for a time-ordered v7. Both return a uuid.UUID value — a [16]byte array with String(), JSON marshalling and database driver support built in.
What Go package should I use for UUIDs?
github.com/google/uuid is the standard choice. It supports v1, v3, v4, v5, v6 and v7, has no external dependencies, and is integrated directly with pgx, GORM and most Go database drivers.
What column type should I use to store a UUID in a database?
Use the native type where one exists — uuid in PostgreSQL, uniqueidentifier in SQL Server. In MySQL use BINARY(16) and store raw bytes. Avoid VARCHAR(36): it costs 36 bytes per row versus 16 for binary and makes every comparison a string operation.
How do I generate a UUID in Go?
Install github.com/google/uuid and call uuid.New() for a random UUID v4, or uuid.NewV7() for a time-ordered UUID v7. Both return a uuid.UUID value, which is a [16]byte array with String(), MarshalJSON() and other methods built in.
What Go package should I use for UUIDs?
github.com/google/uuid is the standard choice — it is maintained by Google, widely used, supports v1, v3, v4, v5, v6 and v7, and has no dependencies. It is the package most Go ORMs and database drivers integrate with directly.
Does Go support UUID v7?
Yes. The google/uuid package added uuid.NewV7() in v1.6.0. It returns a time-ordered UUID v7 with a monotonic counter for same-millisecond ordering.
How do I store a UUID in PostgreSQL from Go?
Use the pgx driver or database/sql with lib/pq. Both accept uuid.UUID values directly as query parameters when scanning into a uuid.UUID variable. Store the column as the native uuid type in PostgreSQL for 16-byte binary storage and bitwise comparison.