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 onT)Table name derived from struct name (User ->
user)
ID Types
The type parameter T determines the ID format:
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 itselfIn- 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
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:
Define models as Go structs with
som.Node[T]orsom.EdgeRun generator:
som -i ./modelImport 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