Skip to content

Quick Start

This guide walks you through creating your first entity and repository with Dynorm.

Step 1: Define Your Entity

Entities embed dynorm.Entity to get automatic ID generation, timestamps, and lifecycle management:

package main

import "github.com/go-gamma/dynorm"

// User entity - focus only on business fields
type User struct {
    dynorm.Entity // Provides: ID, CreatedAt, UpdatedAt, DeletedAt

    Email     string `dynorm:"gsi:ByEmail"`
    Username  string
    FirstName string
    LastName  string
    Status    string
}

What dynorm.Entity provides

  • ID: Auto-generated ULID (26 characters, sortable, unique)
  • CreatedAt: Derived from ULID timestamp on first save
  • UpdatedAt: Set to current time on every save
  • DeletedAt: For soft deletes (optional)

Step 2: Define Your Repository

Repositories embed dynorm.Repository[T] and handle all database operations:

// UserRepository - table name auto-derived as "users"
type UserRepository struct {
    dynorm.Repository[User]
}

// Global repository instance
var UserRepo = &UserRepository{}
Custom table names

Override the auto-derived table name with a struct tag:

type StaffRepository struct {
    dynorm.Repository[User] `table:"staff_members"`
}

Step 3: Use It

func main() {
    // Create a new user
    user := &User{
        Email:     "john@example.com",
        Username:  "johndoe",
        FirstName: "John",
        LastName:  "Doe",
        Status:    "active",
    }

    // Save - ID and timestamps are set automatically
    err := UserRepo.Save(user)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Created user with ID: %s\n", user.ID)
    // Output: Created user with ID: 01HQ3K4N5M6P7Q8R9S0T1U2V3W

    // Find by ID
    found, err := UserRepo.Find(user.ID)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Found: %s %s\n", found.FirstName, found.LastName)

    // Query with fluent API
    activeUsers, err := UserRepo.
        Where("Status", "=", "active").
        OrderBy("CreatedAt", "desc").
        Limit(10).
        GetAll()

    fmt.Printf("Found %d active users\n", activeUsers.Count())
}

Complete Example

Here's a full working example:

package main

import (
    "fmt"
    "log"
    "os"

    "github.com/go-gamma/dynorm"
)

// Entity
type Product struct {
    dynorm.Entity

    Name        string  `dynorm:"gsi:ByName"`
    Category    string  `dynorm:"gsi:ByCategory"`
    Price       float64
    InStock     bool
    Description string
}

// Repository
type ProductRepository struct {
    dynorm.Repository[Product]
}

var ProductRepo = &ProductRepository{}

func main() {
    // Ensure AWS region is set
    if os.Getenv("AWS_REGION") == "" {
        os.Setenv("AWS_REGION", "us-east-1")
    }

    // Create
    product := &Product{
        Name:        "Laptop",
        Category:    "Electronics",
        Price:       999.99,
        InStock:     true,
        Description: "High-performance laptop",
    }

    if err := ProductRepo.Save(product); err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Created product: %s\n", product.ID)

    // Read
    found, err := ProductRepo.Find(product.ID)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Found: %s - $%.2f\n", found.Name, found.Price)

    // Update
    product.Price = 899.99
    if err := ProductRepo.Save(product); err != nil {
        log.Fatal(err)
    }
    fmt.Println("Price updated")

    // Query
    electronics, err := ProductRepo.
        Where("Category", "=", "Electronics").
        Where("InStock", "=", true).
        OrderBy("Price", "asc").
        GetAll()

    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Found %d electronics in stock\n", electronics.Count())

    // Delete
    if err := ProductRepo.Delete(product); err != nil {
        log.Fatal(err)
    }
    fmt.Println("Product deleted")
}

Architecture Overview

graph TB
    subgraph "Your Code"
        Entity[Entity Structs]
        Repo[Repository]
    end

    subgraph "Dynorm"
        BaseEntity[dynorm.Entity]
        BaseRepo[dynorm.Repository]
        Query[Query Builder]
        Schema[Schema Parser]
        Marshal[Snake Case Marshaller]
    end

    subgraph "AWS"
        DDB[(DynamoDB)]
    end

    Entity --> BaseEntity
    Repo --> BaseRepo
    BaseRepo --> Query
    BaseRepo --> Schema
    Query --> Marshal
    Marshal --> DDB

Next Steps