Getting started

Build a Go application

A complete program from go mod init to durable batch writes, typed error handling, scans, and clean shutdown.

Create your module

With Go 1.26 or later installed, make a new application directory:

mkdir zeedb-app
cd zeedb-app
go mod init example.com/zeedb-app
go get github.com/Berlindraft/ZeeDB

These commands create your own application module. They are separate from running the existing repository examples. If repository access is private, use your normal Git/Go private-module configuration.

Create main.go

This complete program opens ./data, commits two records together, reads one, scans the range, and closes while preserving errors. Its data directory persists between runs.

package main

import (
    "context"
    "errors"
    "fmt"
    "os"

    zeedb "github.com/Berlindraft/ZeeDB"
)

func main() {
    if err := run(); err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }
}

func run() (err error) {
    db, err := zeedb.Open("./data", zeedb.Options{
        Durability: zeedb.Durable,
    })
    if err != nil {
        return err
    }
    defer func() { err = errors.Join(err, db.Close()) }()

    ctx := context.Background()
    if err := db.WriteBatch(ctx, []zeedb.Mutation{
        {Collection: "users", Key: []byte("1024"), Value: []byte(`{"name":"Ada"}`)},
        {Collection: "users", Key: []byte("1025"), Value: []byte(`{"name":"Grace"}`)},
    }); err != nil {
        return err
    }

    value, err := db.Get(ctx, "users", []byte("1024"))
    if errors.Is(err, zeedb.ErrNotFound) {
        return errors.New("expected user 1024 is absent")
    }
    if err != nil {
        return err
    }
    fmt.Println(string(value))

    records, err := db.Scan(ctx, "users", zeedb.ScanOptions{
        Start: []byte("1024"),
        End:   []byte("1026"),
        Limit: 2,
    })
    if err != nil {
        return err
    }
    for _, record := range records {
        fmt.Printf("%s: %s (version %d)\n",
            record.Key, record.Value, record.Version)
    }
    return nil
}

Run it

go run .

The first output line is {"name":"Ada"}. The scan prints keys 1024 and 1025 with their values and shared batch version. Rerunning replaces the same keys and advances the commit sequence; versions are not fixed example IDs.

There is no need to run a server or call Flush after each mutation. Default durable acknowledgement follows WAL sync. Close syncs pending writes and releases the directory lock.

Understand the example’s boundaries

  • The byte values happen to contain JSON; no schema validation happens inside the engine.
  • The batch is atomic. The later Get and Scan are separate read snapshots.
  • The deferred close error is joined with any earlier application error.
  • A write/checkpoint I/O error can be indeterminate; reopen and reconcile before retrying non-idempotent work.
  • Only this process may own the ./data directory while the program is open.

The program intentionally keeps its data. Unlike the temporary repository examples, it never removes the application’s directory.

Next steps

Use a typed JSON repository for application validation, coordinated writes for read-modify-write invariants, and bounded pagination for larger collections. Review deployment before selecting real filesystem paths and budgets.

Something to improve?Edit this page on GitHub