Run the complete example
From the repository root:
go run ./examples/jsonstore
1024: Ada (version 1)
missing user: handled with errors.Is
The example creates a temporary database, closes it, and removes only its own temporary directory. The complete source below is embedded from the runnable repository example during the site build.
Define an application envelope
type User struct {
ID string `json:"id"`
Name string `json:"name"`
}
type userEnvelope struct {
Schema int `json:"schema"`
User User `json:"user"`
}
The envelope schema is an application encoding version. A record’s commit Version is a separate concept and must not be used to infer JSON shape.
Validate before encoding
func (s UserStore) Put(ctx context.Context, user User) error {
if user.ID == "" || user.Name == "" {
return errors.New("user id and name are required")
}
data, err := json.Marshal(userEnvelope{Schema: 1, User: user})
if err != nil {
return err
}
return s.DB.Put(ctx, "users", []byte(user.ID), data)
}
UserStore is application code holding a *zeedb.DB; it is not a built-in SDK type. The complete example contains the imports and type declaration.
The engine’s key/value/batch limits still apply to the encoded payload. Application validation cannot substitute for checking the returned DB error.
Decode deliberately
The example uses GetRecord, then checks:
- The engine read succeeded, with
ErrNotFoundhandled separately. - The value decodes as JSON.
- The envelope schema is supported.
- The ID inside the payload matches the requested key.
- The stored name satisfies the example’s required-field rule.
Do not silently accept a decoding error as an empty user. A good checksum does not imply valid application JSON.
Preserve error identity
Wrap application decoding errors with %w when callers need to inspect their type. Preserve DB sentinels so callers can use errors.Is(err, zeedb.ErrNotFound).
_, _, err := store.Get(ctx, "missing")
if errors.Is(err, zeedb.ErrNotFound) {
// Expected absence; choose your application's response.
} else if err != nil {
return err
}
Add updates carefully
This repository demonstrates replacement writes, not compare-and-swap. Reading a user/version and writing a changed user does not protect against another writer. Coordinate the entire read/validate/write path at the application layer when preserving an invariant matters.
For logical indexes, commit primary data and index entries together; see manual indexes.
Complete source
This is the canonical examples/jsonstore/main.go, including imports, lifecycle,
application validation, and missing-record handling. The page loads it at build
time so the complete listing stays aligned with the executable example.
// Run from the repository root: go run ./examples/jsonstore
package main
import (
"context"
"encoding/json"
"errors"
"fmt"
"os"
zeedb "github.com/Berlindraft/ZeeDB"
)
type User struct {
ID string `json:"id"`
Name string `json:"name"`
}
type userEnvelope struct {
Schema int `json:"schema"`
User User `json:"user"`
}
type UserStore struct{ DB *zeedb.DB }
func (s UserStore) Put(ctx context.Context, user User) error {
if user.ID == "" || user.Name == "" {
return errors.New("user id and name are required")
}
data, err := json.Marshal(userEnvelope{Schema: 1, User: user})
if err != nil {
return err
}
return s.DB.Put(ctx, "users", []byte(user.ID), data)
}
func (s UserStore) Get(ctx context.Context, id string) (User, uint64, error) {
r, err := s.DB.GetRecord(ctx, "users", []byte(id))
if err != nil {
return User{}, 0, err
}
var envelope userEnvelope
if err := json.Unmarshal(r.Value, &envelope); err != nil {
return User{}, 0, fmt.Errorf("decode user %q: %w", id, err)
}
if envelope.Schema != 1 {
return User{}, 0, fmt.Errorf("unsupported user schema %d", envelope.Schema)
}
if envelope.User.ID != id {
return User{}, 0, errors.New("user key/payload id mismatch")
}
if envelope.User.Name == "" {
return User{}, 0, errors.New("stored user name is required")
}
return envelope.User, r.Version, nil
}
func main() {
if err := run(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
func run() (err error) {
dir, err := os.MkdirTemp("", "zeedb-json-")
if err != nil {
return err
}
defer func() { err = errors.Join(err, os.RemoveAll(dir)) }()
db, err := zeedb.Open(dir, zeedb.Options{})
if err != nil {
return err
}
defer func() { err = errors.Join(err, db.Close()) }()
store := UserStore{DB: db}
ctx := context.Background()
if err := store.Put(ctx, User{ID: "1024", Name: "Ada"}); err != nil {
return err
}
user, version, err := store.Get(ctx, "1024")
if err != nil {
return err
}
fmt.Printf("%s: %s (version %d)\n", user.ID, user.Name, version)
_, _, err = store.Get(ctx, "missing")
if !errors.Is(err, zeedb.ErrNotFound) {
return fmt.Errorf("expected ErrNotFound, got %v", err)
}
fmt.Println("missing user: handled with errors.Is")
return nil
}