githubEdit

Concepts

This page introduces the fundamental concepts you need to understand when working with SOM.

Nodes

A Node represents a database record (similar to a row in SQL or a document in NoSQL). Any Go struct that embeds som.Node[T] becomes a SurrealDB table.

type User struct {
    som.Node[som.ULID]  // Required - makes this a database record

    Name  string
    Email string
}

The embedded som.Node[T] provides:

  • An ID() method returning the node's ID (type depends on T)

  • Table name derived from struct name (User -> user)

ID Types

The type parameter T determines the ID format:

Type
Description
Example ID

som.ULID

ULID-based IDs (default choice)

01HQMV8K2P...

som.UUID

UUID-based IDs

550e8400-e29b-...

som.Rand

Random string IDs

abc123def

Custom struct

Complex array/object IDs

[city, date]

Note: ID() returns the raw ID value. The repository automatically prefixes it with the table name (e.g. user:01HQMV8K2P...) when storing and querying records.

Timestamps

Embed som.Timestamps for automatic time tracking:

This adds:

  • CreatedAt time.Time - Set automatically on creation (readonly)

  • UpdatedAt time.Time - Updated automatically on every modification

These fields are managed by SurrealDB and cannot be manually set.

Edges

An Edge represents a graph relationship between two nodes. SurrealDB has first-class graph support, and SOM leverages this via structs that embed som.Edge.

Edges automatically have:

  • ID - Unique identifier for the edge itself

  • In - The source node (where the relationship starts)

  • Out - The target node (where the relationship points)

Creating edges uses the RELATE statement:

ID Handling

Repositories

For each Node and Edge, SOM generates a Repository with standard operations:

Access repositories through the client:

Query Builder

The Query Builder provides a fluent, type-safe API for constructing database queries:

Execution Methods

Method
Returns
Description

All(ctx)

([]*Model, error)

All matching records

First(ctx)

(*Model, error)

First match (returns ErrNotFound if none)

Count(ctx)

(int, error)

Count of matches

Exists(ctx)

(bool, error)

Whether any exist

Live(ctx)

(<-chan LiveResult, error)

Real-time stream

Async Variants

Every method has an async version:

Filters (filter)

Type-safe conditions for queries. Import from gen/som/filter:

Sorting (by)

Order results by fields. Import from gen/som/by:

Code Generation

SOM works through code generation. The workflow:

  1. Define models as Go structs with som.Node[T] or som.Edge

  2. Run generator: som -i ./model

  3. Import and use the generated packages

Benefits:

  • Compile-time safety - Typos caught by compiler

  • Zero reflection - No runtime type inspection

  • IDE support - Full autocompletion

  • Performance - No overhead from ORM magic

Regenerate whenever you:

  • Add or remove model fields

  • Create new Node or Edge types

  • Change field types

Last updated