Documentation Index
Fetch the complete documentation index at: https://docs.go-mizu.dev/llms.txt
Use this file to discover all available pages before exploring further.
Collections provide type-safe management of synchronized entities. They wrap the sync client’s store with a convenient API for creating, reading, updating, and deleting records.
Overview
// Define your type
type Todo struct {
ID string `json:"id"`
Title string `json:"title"`
Completed bool `json:"completed"`
CreatedAt time.Time `json:"created_at"`
}
// Create a collection
client := sync.New(opts)
todos := sync.NewCollection[Todo](client, "todo")
// Use it
todo := todos.Create("abc", Todo{Title: "Buy milk"})
all := todos.All()
found := todos.Find(func(t Todo) bool { return t.Completed })
Creating a Collection
import "github.com/go-mizu/mizu/view/sync"
// Type parameter is your entity type
// Second argument is the entity name (matches server)
todos := sync.NewCollection[Todo](client, "todo")
users := sync.NewCollection[User](client, "user")
settings := sync.NewCollection[Settings](client, "settings")
The entity name should match what your server-side Mutator uses.
Collection Methods
Creates a new entity:
todo := todos.Create("todo-123", Todo{
Title: "Buy groceries",
Completed: false,
})
This:
- Stores the entity locally (immediate)
- Queues a mutation (background sync)
- Returns an Entity reference
Gets an entity reference by ID:
todo := todos.Get("todo-123")
This returns an Entity even if it doesn’t exist yet. Use Exists() to check.
Returns all entities in the collection (reactive):
allTodos := todos.All()
for _, entity := range allTodos {
todo := entity.Get()
fmt.Printf("%s: %s\n", todo.ID, todo.Title)
}
Returns the number of entities (reactive):
count := todos.Count()
fmt.Printf("%d todos\n", count)
Finds entities matching a predicate:
completed := todos.Find(func(t Todo) bool {
return t.Completed
})
urgent := todos.Find(func(t Todo) bool {
return strings.Contains(t.Title, "URGENT")
})
Checks if an entity exists:
if todos.Has("todo-123") {
fmt.Println("Todo exists")
}
Entity Methods
An Entity is a reference to a single record.
Returns the entity’s unique identifier:
entity := todos.Get("todo-123")
fmt.Println(entity.ID()) // "todo-123"
Returns the current value (reactive):
entity := todos.Get("todo-123")
todo := entity.Get()
fmt.Println(todo.Title)
fmt.Println(todo.Completed)
Updates the entity value:
entity := todos.Get("todo-123")
// Get current value
todo := entity.Get()
// Modify
todo.Title = "Updated title"
todo.Completed = true
// Save
entity.Set(todo)
This:
- Updates local store (immediate)
- Queues an update mutation (background sync)
Removes the entity:
entity := todos.Get("todo-123")
entity.Delete()
This:
- Removes from local store (immediate)
- Queues a delete mutation (background sync)
Checks if the entity exists:
entity := todos.Get("maybe-exists")
if entity.Exists() {
todo := entity.Get()
fmt.Println(todo.Title)
} else {
fmt.Println("Not found")
}
Reactive Usage
Collections and entities are reactive. Use them with Computed and Effect:
Computed Example
todos := sync.NewCollection[Todo](client, "todo")
// Computed value updates when todos change
completedCount := sync.NewComputed(func() int {
count := 0
for _, entity := range todos.All() {
if entity.Get().Completed {
count++
}
}
return count
})
// Or using Find
completedTodos := sync.NewComputed(func() []*sync.Entity[Todo] {
return todos.Find(func(t Todo) bool { return t.Completed })
})
Effect Example
// Log when todos change
sync.NewEffect(func() {
all := todos.All()
fmt.Printf("Todos updated: %d items\n", len(all))
})
// Update UI when specific todo changes
sync.NewEffect(func() {
entity := todos.Get("important-todo")
if entity.Exists() {
todo := entity.Get()
updateTodoUI(todo)
}
})
Complete Example: Todo App
package main
import (
"context"
"fmt"
"time"
"github.com/go-mizu/mizu/view/sync"
)
type Todo struct {
ID string `json:"id"`
Title string `json:"title"`
Completed bool `json:"completed"`
CreatedAt time.Time `json:"created_at"`
}
func main() {
// Create client and collection
client := sync.New(sync.Options{
BaseURL: "http://localhost:8080/_sync",
Scope: "user:demo",
})
todos := sync.NewCollection[Todo](client, "todo")
// Start sync
ctx := context.Background()
client.Start(ctx)
// Reactive stats
stats := sync.NewComputed(func() string {
all := todos.All()
completed := 0
for _, e := range all {
if e.Get().Completed {
completed++
}
}
return fmt.Sprintf("%d/%d completed", completed, len(all))
})
// Log changes
sync.NewEffect(func() {
fmt.Println("Stats:", stats.Get())
})
// Create todos
todos.Create("1", Todo{Title: "Learn Go", CreatedAt: time.Now()})
todos.Create("2", Todo{Title: "Build app", CreatedAt: time.Now()})
todos.Create("3", Todo{Title: "Deploy", CreatedAt: time.Now()})
// Complete one
entity := todos.Get("1")
todo := entity.Get()
todo.Completed = true
entity.Set(todo)
// Find incomplete
incomplete := todos.Find(func(t Todo) bool { return !t.Completed })
fmt.Printf("\nIncomplete todos:\n")
for _, e := range incomplete {
fmt.Printf("- %s\n", e.Get().Title)
}
// Delete one
todos.Get("3").Delete()
// Keep running...
select {}
}
Patterns
CRUD Operations
// Create
entity := collection.Create(id, value)
// Read
entity := collection.Get(id)
if entity.Exists() {
value := entity.Get()
}
// Update
entity := collection.Get(id)
value := entity.Get()
value.Field = newValue
entity.Set(value)
// Delete
collection.Get(id).Delete()
List with Filter
filter := sync.NewSignal("")
filteredTodos := sync.NewComputed(func() []*sync.Entity[Todo] {
f := strings.ToLower(filter.Get())
if f == "" {
return todos.All()
}
return todos.Find(func(t Todo) bool {
return strings.Contains(strings.ToLower(t.Title), f)
})
})
Sorted List
sortBy := sync.NewSignal("created_at")
sortedTodos := sync.NewComputed(func() []*sync.Entity[Todo] {
all := todos.All()
// Convert to slice for sorting
items := make([]Todo, len(all))
for i, e := range all {
items[i] = e.Get()
}
// Sort
switch sortBy.Get() {
case "title":
sort.Slice(items, func(i, j int) bool {
return items[i].Title < items[j].Title
})
case "created_at":
sort.Slice(items, func(i, j int) bool {
return items[i].CreatedAt.Before(items[j].CreatedAt)
})
}
// Convert back to entities
result := make([]*sync.Entity[Todo], len(items))
for i, item := range items {
result[i] = todos.Get(item.ID)
}
return result
})
page := sync.NewSignal(0)
pageSize := 10
paginatedTodos := sync.NewComputed(func() []*sync.Entity[Todo] {
all := todos.All()
start := page.Get() * pageSize
end := start + pageSize
if start >= len(all) {
return nil
}
if end > len(all) {
end = len(all)
}
return all[start:end]
})
totalPages := sync.NewComputed(func() int {
return (todos.Count() + pageSize - 1) / pageSize
})
Multiple Collections
projects := sync.NewCollection[Project](client, "project")
tasks := sync.NewCollection[Task](client, "task")
// Get tasks for a project
projectTasks := func(projectID string) []*sync.Entity[Task] {
return tasks.Find(func(t Task) bool {
return t.ProjectID == projectID
})
}
// Reactive task count per project
projectTaskCount := sync.NewComputed(func() map[string]int {
counts := make(map[string]int)
for _, entity := range tasks.All() {
task := entity.Get()
counts[task.ProjectID]++
}
return counts
})
Best Practices
1. Use Meaningful IDs
// Good: meaningful, unique ID
todos.Create("user-123-todo-456", todo)
// Bad: just a random string
todos.Create("xyz", todo)
2. Keep Entities Small
// Good: focused entity
type Todo struct {
ID string
Title string
Completed bool
}
// Bad: entity with embedded large data
type Todo struct {
ID string
Title string
Attachments [][]byte // Large data
}
3. Check Existence
entity := collection.Get(id)
// Always check before accessing
if entity.Exists() {
value := entity.Get()
// Use value
}
4. Use Computed for Derived Data
// Good: computed derives from collection
taskCount := sync.NewComputed(func() int {
return tasks.Count()
})
// Bad: manually tracking
taskCount := sync.NewSignal(0)
sync.NewEffect(func() {
taskCount.Set(len(tasks.All())) // Manual sync
})
// If creating multiple related entities,
// they'll be queued and synced together
project := projects.Create(projectID, Project{Name: "New Project"})
tasks.Create(task1ID, Task{ProjectID: projectID, Title: "Task 1"})
tasks.Create(task2ID, Task{ProjectID: projectID, Title: "Task 2"})
// All mutations sync in order