velox

package module
v0.0.0-...-854dc90 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 15, 2026 License: MIT Imports: 10 Imported by: 0

README

Velox

CI Go Report Card GoDoc codecov

A type-safe Go ORM framework with integrated code generation for GraphQL services.

Note: This is a personal project. If you're looking for a production-ready Go ORM with code generation, I highly recommend Ent — it's mature, well-documented, and backed by a strong community. Velox was built to address specific performance bottlenecks I encountered with Ent's template-based code generation in large schemas. Velox uses Jennifer for programmatic code generation instead of text/template, which provides streaming writes, auto-managed imports, and parallel generation — resulting in significantly faster build times for large projects.

Why Velox?

Velox is API-compatible with Ent — same schema definition patterns, same generated query builder API, same hooks/interceptors/privacy model. The key difference is the code generation engine:

Ent Velox
Code generation text/template Jennifer (programmatic)
Import management goimports post-process Auto-tracked at generation time
File writes Buffer then write Streaming to disk
Parallel generation Sequential errgroup + semaphore
Generated predicates Verbose functions per field Generic predicates (~97% less code)

If you have a small-to-medium schema, use Ent. Velox only makes sense if you're hitting code generation performance limits with 50+ entity types.

Features

  • Type-Safe Query Builders — Fluent API with compile-time type checking
  • Jennifer Code Generation — Programmatic codegen with streaming writes and parallel execution
  • Multi-Database Support — PostgreSQL, MySQL, SQLite (pure Go, no CGO)
  • Relay-Style Pagination — Cursor-based and offset pagination for GraphQL
  • Privacy Layer — ORM-level authorization policies with composable rules
  • Hooks & Interceptors — Middleware-style mutation hooks and query interceptors
  • Eager Loading — Efficient relationship loading with query options
  • Generic Predicates — Compact, type-safe predicates (~97% less generated code)

Installation

go get github.com/syssam/velox

Quick Start

1. Define Schema

Create entity schemas in schema/:

// schema/user.go
package schema

import (
    "github.com/syssam/velox"
    "github.com/syssam/velox/contrib/mixin"
    "github.com/syssam/velox/schema/edge"
    "github.com/syssam/velox/schema/field"
    "github.com/syssam/velox/schema/index"
)

type User struct{ velox.Schema }

func (User) Mixin() []velox.Mixin {
    return []velox.Mixin{
        mixin.Time{}, // Adds created_at and updated_at
    }
}

func (User) Fields() []velox.Field {
    return []velox.Field{
        field.String("name").NotEmpty().MaxLen(100),
        field.String("email").Unique().NotEmpty(),
        field.Int("age").Optional().Positive(),
        field.Enum("role").Values("admin", "user", "guest").Default("user"),
    }
}

func (User) Edges() []velox.Edge {
    return []velox.Edge{
        edge.To("posts", Post.Type),                                              // One-to-Many
        edge.To("profile", Profile.Type).Unique(),                                // One-to-One
        edge.To("groups", Group.Type).Through("memberships", Membership.Type),    // Many-to-Many
    }
}

func (User) Indexes() []velox.Index {
    return []velox.Index{
        index.Fields("email").Unique(),
        index.Fields("role", "created_at"),
    }
}
2. Generate Code

Create a generate.go file:

//go:build ignore

package main

import (
    "log/slog"
    "os"

    "github.com/syssam/velox/compiler"
    "github.com/syssam/velox/compiler/gen"
)

func main() {
    cfg, err := gen.NewConfig(
        gen.WithTarget("./velox"),
        // Optional features
        gen.WithFeatures(gen.FeaturePrivacy, gen.FeatureIntercept),
    )
    if err != nil {
        slog.Error("creating config", "error", err)
        os.Exit(1)
    }

    if err := compiler.Generate("./schema", cfg); err != nil {
        slog.Error("running velox codegen", "error", err)
        os.Exit(1)
    }
}

Run it:

go run generate.go

Or use the CLI:

velox generate ./schema --target ./velox
3. Use Generated Code
package main

import (
    "context"
    "log"

    _ "github.com/lib/pq"
    "yourproject/velox"
    "yourproject/velox/user"
)

func main() {
    // Open a database connection
    client, err := velox.Open("postgres", "postgres://localhost/mydb?sslmode=disable")
    if err != nil {
        log.Fatal(err)
    }
    defer client.Close()

    // Run auto-migration
    ctx := context.Background()
    if err := client.Schema.Create(ctx); err != nil {
        log.Fatal(err)
    }

    // Create
    u, err := client.User.Create().
        SetName("Alice").
        SetEmail("alice@example.com").
        SetRole("user").
        Save(ctx)
    if err != nil {
        log.Fatal(err)
    }

    // Query with filtering and eager loading
    users, err := client.User.Query().
        Where(user.RoleField.EQ("admin")).
        WithPosts().
        Limit(10).
        All(ctx)
    if err != nil {
        log.Fatal(err)
    }
    _ = users

    // Update
    u, err = client.User.UpdateOneID(u.ID).
        SetName("Alice Smith").
        Save(ctx)
    if err != nil {
        log.Fatal(err)
    }

    // Delete
    if err := client.User.DeleteOneID(u.ID).Exec(ctx); err != nil {
        log.Fatal(err)
    }
}

Configuration

All configuration is done through Go code using functional options:

cfg, err := gen.NewConfig(
    gen.WithTarget("./velox"),                // Output directory (default: parent of schema path)
    gen.WithIDType("int64"),                  // Default ID type
    gen.WithFeatures(                         // Enable optional features
        gen.FeaturePrivacy,
        gen.FeatureIntercept,
        gen.FeatureUpsert,
        gen.FeatureNamedEdges,
        gen.FeatureVersionedMigration,
    ),
)

Field Types

field.String("name")                        // VARCHAR/TEXT
field.Text("bio")                           // TEXT (unlimited)
field.Int("count")                          // INTEGER
field.Int64("big_count")                    // BIGINT
field.Float64("price")                      // DOUBLE PRECISION
field.Bool("active")                        // BOOLEAN
field.Time("created_at")                    // TIMESTAMP
field.Enum("status").Values("a", "b")       // VARCHAR with validation
field.JSON("metadata")                      // JSONB/JSON
field.UUID("external_id", uuid.UUID{})      // UUID
field.Bytes("data")                         // BYTEA/BLOB

Field Modifiers

field.String("email").
    Unique().              // UNIQUE constraint
    NotEmpty().            // Validation: must not be empty
    MaxLen(255).           // VARCHAR(255)
    Optional().            // Not required in create input
    Nillable().            // Database NULL, Go *string
    Default("value").      // Default value
    Immutable()            // Cannot update after create

Relationships

// One-to-Many (default)
edge.To("posts", Post.Type)             // User has many Posts
edge.From("author", User.Type).Unique() // Post belongs to one User

// One-to-One
edge.To("profile", Profile.Type).Unique()

// Many-to-Many (with join table)
edge.To("groups", Group.Type).Through("memberships", Membership.Type)

Predicates

Velox generates compact, type-safe predicates using generics. All fields use the Field suffix:

// Entity package-level predicate variables (all use Field suffix)
user.NameField.EQ("Alice")              // name = 'Alice'
user.NameField.Contains("ali")          // name LIKE '%ali%'
user.NameField.HasPrefix("A")           // name LIKE 'A%'
user.AgeField.GT(18)                    // age > 18
user.AgeField.In(18, 21, 25)            // age IN (18, 21, 25)
user.EmailField.IsNil()                 // email IS NULL
user.RoleField.EQ("admin")              // enum fields

// Edge predicates (functions, no Field suffix)
user.HasPosts()                          // EXISTS (SELECT 1 FROM posts ...)
user.HasPostsWith(post.TitleField.EQ("Hello"))

// Combinators
user.And(user.AgeField.GT(18), user.RoleField.EQ("active"))
user.Or(user.RoleField.EQ("admin"), user.RoleField.EQ("moderator"))
user.Not(user.RoleField.EQ("banned"))

Eager Loading

// Load all edges
client.User.Query().
    WithPosts().
    WithProfile().
    All(ctx)

// Load with filtering
client.User.Query().
    WithPosts(func(q *velox.PostQuery) {
        q.Where(post.PublishedField.EQ(true)).
          Limit(5)
    }).
    All(ctx)

Hooks & Interceptors

Hooks use a middleware pattern wrapping the mutation chain:

// Mutation hook - logs all mutations
func LoggingHook() velox.Hook {
    return func(next velox.Mutator) velox.Mutator {
        return velox.MutateFunc(func(ctx context.Context, m velox.Mutation) (velox.Value, error) {
            slog.Info("mutation", "op", m.Op(), "type", m.Type())
            v, err := next.Mutate(ctx, m)
            if err != nil {
                slog.Error("mutation failed", "op", m.Op(), "error", err)
            }
            return v, err
        })
    }
}

// Register globally or per-entity
client.Use(LoggingHook())
client.User.Use(LoggingHook())

Query interceptors filter or modify all queries (using the generated intercept package):

// import "yourproject/velox/intercept"

// Interceptor - limit all queries to prevent unbounded results
client.Intercept(intercept.TraverseFunc(func(ctx context.Context, q intercept.Query) error {
    q.Limit(1000)
    return nil
}))

Transactions

// Using WithTx helper
err := velox.WithTx(ctx, client, func(tx *velox.Tx) error {
    user, err := tx.User.Create().
        SetEmail("user@example.com").
        SetName("Alice").
        Save(ctx)
    if err != nil {
        return err // Rollback
    }

    _, err = tx.Profile.Create().
        SetUserID(user.ID).
        Save(ctx)
    return err // Commit on nil, rollback on error
})

// Manual transaction control
tx, err := client.Tx(ctx)
// ... use tx.User, tx.Post, etc.
tx.Commit()  // or tx.Rollback()

Error Handling

import "github.com/syssam/velox"

_, err := client.User.Create().
    SetEmail("dup@example.com").
    Save(ctx)
if err != nil {
    if velox.IsNotFound(err) {
        // Entity not found
    }
    if velox.IsConstraintError(err) {
        // Unique constraint, foreign key, etc.
    }
    if velox.IsValidationError(err) {
        // Field validation failed
    }
    if velox.IsNotSingular(err) {
        // Expected exactly one result
    }
}

Privacy Layer

ORM-level authorization policies evaluated before database access:

import (
    "github.com/syssam/velox/privacy"
    "github.com/syssam/velox/schema/policy"
)

func (User) Policy() velox.Policy {
    return policy.Policy(
        policy.Mutation(
            privacy.DenyIfNoViewer(),
            privacy.HasRole("admin"),
            privacy.IsOwner("user_id"),
            privacy.AlwaysDenyRule(),
        ),
        policy.Query(
            privacy.AlwaysAllowQueryRule(),
        ),
    )
}

Mixins

Built-in mixins for common patterns:

import "github.com/syssam/velox/contrib/mixin"

mixin.ID{}         // int64 auto-increment primary key
mixin.Time{}       // created_at, updated_at timestamps
mixin.SoftDelete{} // deleted_at for soft deletes
mixin.TenantID{}   // tenant_id for multi-tenancy

Database Support

Feature PostgreSQL MySQL SQLite
CRUD Yes Yes Yes
Transactions Yes Yes Yes
RETURNING Yes No No
UUID type Yes No No
JSONB Yes JSON JSON
Upsert Yes Yes Yes

SQLite uses modernc.org/sqlite (pure Go, no CGO required).

GraphQL Integration

Optional extension for generating GraphQL schemas and resolvers (works with gqlgen):

import "github.com/syssam/velox/contrib/graphql"

ex, err := graphql.NewExtension(
    graphql.WithConfigPath("./gqlgen.yml"),
    graphql.WithSchemaPath("./velox/schema.graphql"),
)

cfg, err := gen.NewConfig(gen.WithTarget("./velox"))

compiler.Generate("./schema", cfg, compiler.Extensions(ex))

Generated GraphQL includes types, inputs, connections (Relay), filtering (WhereInput), and mutations (opt-in).

Acknowledgements

Velox is heavily inspired by Ent. The schema definition API, hooks/interceptors pattern, privacy layer, and generated query builder design all follow Ent's excellent patterns. If Ent works for your project, use it.

License

MIT

Documentation

Overview

Package velox provides core interfaces and types for defining entity schemas in the Velox ORM framework.

Velox is a type-safe Go ORM framework with integrated code generation for GraphQL and gRPC services. It generates production-ready database access code, GraphQL schemas/resolvers, and gRPC proto definitions from declarative schema definitions written in Go.

Schema Definition

To define a schema, embed the Schema type in your entity struct:

type User struct {
	velox.Schema
}

func (User) Fields() []velox.Field {
	return []velox.Field{
		field.String("name"),
		field.String("email").Unique(),
	}
}

func (User) Edges() []velox.Edge {
	return []velox.Edge{
		edge.To("posts", Post.Type),
	}
}

Core Interfaces

The package defines several core interfaces:

  • Field - Describes a field/property of an entity
  • Edge - Describes a relationship between entities
  • Index - Describes a database index
  • Mixin - Provides reusable schema components
  • Hook - Defines mutation lifecycle middleware
  • Interceptor - Defines query middleware
  • Policy - Defines privacy/authorization policies

Mutation Operations

The Mutation interface represents operations that modify the graph:

hook := func(next velox.Mutator) velox.Mutator {
	return velox.MutateFunc(func(ctx context.Context, m velox.Mutation) (velox.Value, error) {
		fmt.Printf("Type: %s, Op: %s\n", m.Type(), m.Op())
		return next.Mutate(ctx, m)
	})
}

Query Operations

The Querier and Interceptor interfaces enable query middleware:

interceptor := velox.InterceptFunc(func(next velox.Querier) velox.Querier {
	return velox.QuerierFunc(func(ctx context.Context, q velox.Query) (velox.Value, error) {
		// Pre-query logic
		value, err := next.Query(ctx, q)
		// Post-query logic
		return value, err
	})
})

Privacy Policies

Implement Policy to add authorization rules:

func (User) Policy() velox.Policy {
	return privacy.Policy{
		Query:    privacy.QueryPolicy{privacy.AlwaysAllowQueryRule()},
		Mutation: privacy.MutationPolicy{privacy.DenyIfNoViewer()},
	}
}

Index

Constants

View Source
const (
	OpQueryFirst   = "First"
	OpQueryFirstID = "FirstID"
	OpQueryOnly    = "Only"
	OpQueryOnlyID  = "OnlyID"
	OpQueryAll     = "All"
	OpQueryIDs     = "IDs"
	OpQueryCount   = "Count"
	OpQueryExist   = "Exist"
	OpQueryGroupBy = "GroupBy"
	OpQuerySelect  = "Select"
)

List of query operations used by the codegen.

Variables

View Source
var (
	// ErrNotFound is returned when a requested entity does not exist.
	ErrNotFound = errors.New("velox: entity not found")

	// ErrNotSingular is returned when a query that expects exactly one result
	// returns zero or multiple results.
	ErrNotSingular = errors.New("velox: entity not singular")

	// ErrTxStarted is returned when attempting to start a new transaction
	// within an existing transaction.
	ErrTxStarted = errors.New("velox: cannot start a transaction within a transaction")
)

Standard sentinel errors for common operations.

Functions

func IsConstraintError

func IsConstraintError(err error) bool

IsConstraintError returns true if the error is a ConstraintError.

func IsMutationError

func IsMutationError(err error) bool

IsMutationError returns true if the error is a MutationError.

func IsNotFound

func IsNotFound(err error) bool

IsNotFound returns true if the error is a NotFoundError.

func IsNotLoaded

func IsNotLoaded(err error) bool

IsNotLoaded returns true if the error is a NotLoadedError.

func IsNotSingular

func IsNotSingular(err error) bool

IsNotSingular returns true if the error is a NotSingularError.

func IsPrivacyError

func IsPrivacyError(err error) bool

IsPrivacyError returns true if the error is a PrivacyError.

func IsQueryError

func IsQueryError(err error) bool

IsQueryError returns true if the error is a QueryError.

func IsValidationError

func IsValidationError(err error) bool

IsValidationError returns true if the error is a ValidationError.

func NewAggregateError

func NewAggregateError(errs ...error) error

NewAggregateError returns a new AggregateError if there are errors, otherwise returns nil.

func NewConstraintError

func NewConstraintError(msg string, wrap error) error

NewConstraintError returns a new ConstraintError with the given message.

func NewQueryContext

func NewQueryContext(parent context.Context, c *QueryContext) context.Context

NewQueryContext returns a new context with the given QueryContext attached.

Types

type AggregateError

type AggregateError struct {
	Errors []error
}

AggregateError represents multiple errors collected during an operation.

func (*AggregateError) Error

func (e *AggregateError) Error() string

Error returns the error string.

type Cache

type Cache interface {
	// Get retrieves a value from the cache.
	// Returns nil, nil if the key doesn't exist.
	Get(ctx context.Context, key string) ([]byte, error)

	// Set stores a value in the cache with an optional TTL.
	// If ttl is 0, the value should not expire.
	Set(ctx context.Context, key string, value []byte, ttl time.Duration) error

	// Delete removes a value from the cache.
	Delete(ctx context.Context, key string) error

	// DeletePrefix removes all values with the given prefix.
	DeletePrefix(ctx context.Context, prefix string) error

	// Clear removes all values from the cache.
	Clear(ctx context.Context) error
}

Cache is the interface for caching query results. Users should implement this interface with their preferred caching solution (e.g., Redis, Memcached, in-memory).

type CacheKey

type CacheKey struct {
	Table      string
	Operation  string
	Predicates string
	OrderBy    string
	Limit      int
	Offset     int
}

CacheKey generates a cache key for a query.

func (CacheKey) String

func (k CacheKey) String() string

String returns the string representation of the cache key.

type Config deprecated

type Config struct {
	// A Table is an optional table name defined for the schema.
	Table string
}

A Config structure is used to configure an entity schema. The usage of this structure is as follows:

func (T) Config() velox.Config {
	return velox.Config{
		Table: "Name",
	}
}

Deprecated: the Config object predates the schema.Annotation method and it is planned to be removed in future versions. New code should use Annotations instead.

func (T) Annotations() []schema.Annotation {
	return []schema.Annotation{
		sqlschema.Annotation{Table: "Name"},
	}
}

type ConstraintError

type ConstraintError struct {
	// contains filtered or unexported fields
}

ConstraintError represents a database constraint violation error.

func (ConstraintError) Error

func (e ConstraintError) Error() string

Error returns the error string.

func (ConstraintError) Unwrap

func (e ConstraintError) Unwrap() error

Unwrap returns the underlying error.

type Edge

type Edge interface {
	Descriptor() *edge.Descriptor
}

An Edge interface returns an edge descriptor for vertex edges. The usage for the interface is as follows:

func (T) Edges() []velox.Edge {
	return []velox.Edge{
		edge.To("S", S.Type),
	}
}

type Field

type Field interface {
	Descriptor() *field.Descriptor
}

A Field interface returns a field descriptor for vertex fields/properties. The usage for the interface is as follows:

func (T) Fields() []velox.Field {
	return []velox.Field{
		field.Int("int"),
	}
}

type Hook

type Hook func(Mutator) Mutator

Hook defines the "mutation middleware". A function that gets a Mutator and returns a Mutator. For example:

hook := func(next velox.Mutator) velox.Mutator {
	return velox.MutateFunc(func(ctx context.Context, m velox.Mutation) (velox.Value, error) {
		fmt.Printf("Type: %s, Operation: %s, ConcreteType: %T\n", m.Type(), m.Op(), m)
		return next.Mutate(ctx, m)
	})
}

type Index

type Index interface {
	Descriptor() *index.Descriptor
}

An Index interface returns an index descriptor for vertex indexes. The usage for the interface is as follows:

func (T) Indexes() []velox.Index {
	return []velox.Index{
		index.Fields("f1", "f2").
			Unique(),
	}
}

type InterceptFunc

type InterceptFunc func(Querier) Querier

The InterceptFunc type is an adapter to allow the use of ordinary function as Interceptor. If f is a function with the appropriate signature, InterceptFunc(f) is an Interceptor that calls f.

func (InterceptFunc) Intercept

func (f InterceptFunc) Intercept(next Querier) Querier

Intercept calls f(ctx, q).

type Interceptor

type Interceptor interface {
	// Intercept is a function that gets a Querier and returns a Querier. For example:
	//
	//	velox.InterceptFunc(func(next velox.Querier) velox.Querier {
	//		return velox.QuerierFunc(func(ctx context.Context, query velox.Query) (velox.Value, error) {
	//			// Do something before the query execution.
	//			value, err := next.Query(ctx, query)
	//			// Do something after the query execution.
	//			return value, err
	//		})
	//	})
	//
	// Note that unlike Traverse functions, which are called at each traversal stage, Intercept functions
	// are invoked before the query executions. This means that using Traverse functions is a better fit
	// for adding default filters, while using Intercept functions is a better fit for implementing logging
	// or caching.
	//
	//
	//	client.User.Query().
	//		QueryGroups().	// User traverse functions applied.
	//		QueryPosts().	// Group traverse functions applied.
	//		All(ctx)	// Post traverse and intercept functions applied.
	//
	Intercept(Querier) Querier
}

Interceptor defines an execution middleware for various types of Ent queries. Contrary to Hooks, Interceptors are implemented as interfaces, allows them to intercept and modify the query at different stages, providing more fine-grained control over its behavior. For example, see the Traverser interface.

type Interface

type Interface interface {
	// Type is a dummy method, that is used in edge declaration.
	//
	// The Type method should be used as follows:
	//
	//	type S struct { velox.Schema }
	//
	//	type T struct { velox.Schema }
	//
	//	func (T) Edges() []velox.Edge {
	//		return []velox.Edge{
	//			edge.To("S", S.Type),
	//		}
	//	}
	//
	Type()
	// Fields returns the fields of the schema.
	Fields() []Field
	// Edges returns the edges of the schema.
	Edges() []Edge
	// Indexes returns the indexes of the schema.
	Indexes() []Index
	// Config returns an optional config for the schema.
	//
	// Deprecated: the Config method predates the Annotations method, and it
	// is planned be removed in v0.5.0. New code should use Annotations instead.
	//
	//	func (T) Annotations() []schema.Annotation {
	//		return []schema.Annotation{
	//			sqlschema.Annotation{Table: "Name"},
	//		}
	//	}
	//
	Config() Config
	// Mixin returns an optional list of Mixin to extends
	// the schema.
	Mixin() []Mixin
	// Hooks returns an optional list of Hook to apply on
	// the executed mutations.
	Hooks() []Hook
	// Interceptors returns an optional list of Interceptor
	// to apply on the executed queries.
	Interceptors() []Interceptor
	// Policy returns the privacy policy of the schema.
	Policy() Policy
	// Annotations returns a list of schema annotations to be used by
	// codegen extensions.
	Annotations() []schema.Annotation
}

The Interface type describes the requirements for an exported type defined in the schema package. It functions as the interface between the user's schema types and codegen loader. Users should use the Schema type for embedding as follows:

type T struct {
	velox.Schema
}

type Mixin

type Mixin interface {
	// Fields returns a slice of fields to add to the schema.
	Fields() []Field
	// Edges returns a slice of edges to add to the schema.
	Edges() []Edge
	// Indexes returns a slice of indexes to add to the schema.
	Indexes() []Index
	// Hooks returns a slice of hooks to add to the schema.
	// Note that mixin hooks are executed before schema hooks.
	Hooks() []Hook
	// Interceptors returns a slice of interceptors to add to the schema.
	// Note that mixin interceptors are executed before schema interceptors.
	Interceptors() []Interceptor
	// Policy returns a privacy policy to add to the schema.
	// Note that mixin policy are executed before schema policy.
	Policy() Policy
	// Annotations returns a list of schema annotations to add
	// to the schema annotations.
	Annotations() []schema.Annotation
}

The Mixin type describes a set of methods that can extend other methods in the schema without calling them directly.

type TimeMixin struct {}

func (TimeMixin) Fields() []velox.Field {
	return []velox.Field{
		field.Time("created_at").
			Immutable().
			Default(time.Now),
		field.Time("updated_at").
			Default(time.Now).
			UpdateDefault(time.Now),
	}
}

type T struct {
	velox.Schema
}

func(T) Mixin() []velox.Mixin {
	return []velox.Mixin{
		TimeMixin{},
	}
}

type MutateFunc

type MutateFunc func(context.Context, Mutation) (Value, error)

The MutateFunc type is an adapter to allow the use of ordinary function as Mutator. If f is a function with the appropriate signature, MutateFunc(f) is a Mutator that calls f.

func (MutateFunc) Mutate

func (f MutateFunc) Mutate(ctx context.Context, m Mutation) (Value, error)

Mutate calls f(ctx, m).

type Mutation

type Mutation interface {
	// Op returns the operation name generated by velox.
	Op() Op
	// Type returns the schema type for this mutation.
	Type() string

	// Fields returns all fields that were changed during
	// this mutation. Note that, in order to get all numeric
	// fields that were in/decremented, call AddedFields().
	Fields() []string
	// Field returns the value of a field with the given name.
	// The second boolean value indicates that this field was
	// not set, or was not defined in the schema.
	Field(name string) (Value, bool)
	// SetField sets the value for the given name. It returns an
	// error if the field is not defined in the schema, or if the
	// type mismatch the field type.
	SetField(name string, value Value) error

	// AddedFields returns all numeric fields that were incremented
	// or decremented during this mutation.
	AddedFields() []string
	// AddedField returns the numeric value that was in/decremented
	// from a field with the given name. The second value indicates
	// that this field was not set, or was not define in the schema.
	AddedField(name string) (Value, bool)
	// AddField adds the value for the given name. It returns an
	// error if the field is not defined in the schema, or if the
	// type mismatch the field type.
	AddField(name string, value Value) error

	// ClearedFields returns all nullable fields that were cleared
	// during this mutation.
	ClearedFields() []string
	// FieldCleared returns a bool indicates if this field was
	// cleared in this mutation.
	FieldCleared(name string) bool
	// ClearField clears the value for the given name. It returns an
	// error if the field is not defined in the schema.
	ClearField(name string) error

	// ResetField resets all changes in the mutation regarding the
	// given field name. It returns an error if the field is not
	// defined in the schema.
	ResetField(name string) error

	// AddedEdges returns all edge names that were set/added in this
	// mutation.
	AddedEdges() []string
	// AddedIDs returns all ids (to other nodes) that were added for
	// the given edge name.
	AddedIDs(name string) []Value

	// RemovedEdges returns all edge names that were removed in this
	// mutation.
	RemovedEdges() []string
	// RemovedIDs returns all ids (to other nodes) that were removed for
	// the given edge name.
	RemovedIDs(name string) []Value

	// ClearedEdges returns all edge names that were cleared in this
	// mutation.
	ClearedEdges() []string
	// EdgeCleared returns a bool indicates if this edge was
	// cleared in this mutation.
	EdgeCleared(name string) bool
	// ClearEdge clears the value for the given name. It returns an
	// error if the edge name is not defined in the schema.
	ClearEdge(name string) error

	// ResetEdge resets all changes in the mutation regarding the
	// given edge name. It returns an error if the edge is not
	// defined in the schema.
	ResetEdge(name string) error

	// OldField returns the old value of the field from the database.
	// An error is returned if the mutation operation is not UpdateOne,
	// or the query to the database was failed.
	OldField(ctx context.Context, name string) (Value, error)
}

Mutation represents an operation that mutate the graph. For example, adding a new node, updating many, or dropping data. The implementation is generated by velox.

type MutationError

type MutationError struct {
	Entity string // Entity type being mutated
	Op     string // Operation (e.g., "create", "update", "delete")
	Err    error  // Underlying error
}

MutationError wraps a mutation error with additional context.

func NewMutationError

func NewMutationError(entity, op string, err error) *MutationError

NewMutationError returns a new MutationError.

func (*MutationError) Error

func (e *MutationError) Error() string

Error returns the error string.

func (*MutationError) Unwrap

func (e *MutationError) Unwrap() error

Unwrap returns the underlying error.

type Mutator

type Mutator interface {
	// Mutate apply the given mutation on the graph. The returned
	// velox.Value is changing according to the mutation operation:
	//
	// OpCreate, the returned value is the created node (T).
	// OpUpdateOne, the returned value is the updated node (T).
	// OpUpdate, the returned value is the amount of updated nodes (int).
	// OpDeleteOne, OpDelete, the returned value is the amount of deleted nodes (int).
	//
	Mutate(context.Context, Mutation) (Value, error)
}

Mutator is the interface that wraps the Mutate method.

type NotFoundError

type NotFoundError struct {
	// contains filtered or unexported fields
}

NotFoundError represents an error when an entity is not found.

func NewNotFoundError

func NewNotFoundError(label string) *NotFoundError

NewNotFoundError returns a new NotFoundError for the given entity type.

func NewNotFoundErrorWithID

func NewNotFoundErrorWithID(label string, id any) *NotFoundError

NewNotFoundErrorWithID returns a new NotFoundError with the ID that was searched for.

func (*NotFoundError) Error

func (e *NotFoundError) Error() string

Error returns the error string.

func (*NotFoundError) ID

func (e *NotFoundError) ID() any

ID returns the ID that was searched for, if available.

func (*NotFoundError) Is

func (e *NotFoundError) Is(err error) bool

Is reports whether the target error matches NotFoundError. This allows errors.Is(notFoundErr, ErrNotFound) to return true.

func (*NotFoundError) Label

func (e *NotFoundError) Label() string

Label returns the entity label.

type NotLoadedError

type NotLoadedError struct {
	// contains filtered or unexported fields
}

NotLoadedError represents an error when attempting to access an edge that was not loaded (eager-loaded).

func NewNotLoadedError

func NewNotLoadedError(edge string) *NotLoadedError

NewNotLoadedError returns a new NotLoadedError for the given edge name.

func (*NotLoadedError) Error

func (e *NotLoadedError) Error() string

Error returns the error string.

type NotSingularError

type NotSingularError struct {
	// contains filtered or unexported fields
}

NotSingularError represents an error when a query expects a singular result but receives zero or multiple results.

func NewNotSingularError

func NewNotSingularError(label string) *NotSingularError

NewNotSingularError returns a new NotSingularError for the given entity type.

func NewNotSingularErrorWithCount

func NewNotSingularErrorWithCount(label string, count int) *NotSingularError

NewNotSingularErrorWithCount returns a new NotSingularError with the result count.

func (*NotSingularError) Count

func (e *NotSingularError) Count() int

Count returns the number of results, or -1 if unknown.

func (*NotSingularError) Error

func (e *NotSingularError) Error() string

Error returns the error string.

func (*NotSingularError) Is

func (e *NotSingularError) Is(err error) bool

Is reports whether the target error matches NotSingularError. This allows errors.Is(notSingularErr, ErrNotSingular) to return true.

func (*NotSingularError) Label

func (e *NotSingularError) Label() string

Label returns the entity label.

type Op

type Op uint

An Op represents a mutation operation.

const (
	OpCreate    Op = 1 << iota // node creation.
	OpUpdate                   // update nodes by predicate (if any).
	OpUpdateOne                // update one node.
	OpDelete                   // delete nodes by predicate (if any).
	OpDeleteOne                // delete one node.
)

Mutation operations.

func (Op) Is

func (i Op) Is(o Op) bool

Is reports whether o is match the given operation.

func (Op) String

func (i Op) String() string

type Policy

type Policy interface {
	EvalMutation(context.Context, Mutation) error
	EvalQuery(context.Context, Query) error
}

The Policy type defines the privacy policy of an entity. The usage for the interface is as follows:

type T struct {
	velox.Schema
}

func(T) Policy() velox.Policy {
	return privacy.AlwaysAllowRule()
}

type PrivacyError

type PrivacyError struct {
	Entity string // Entity type
	Op     string // Operation (query or mutation)
	Rule   string // Rule that denied the operation
}

PrivacyError represents a privacy policy violation.

func NewPrivacyError

func NewPrivacyError(entity, op, rule string) *PrivacyError

NewPrivacyError returns a new PrivacyError.

func (*PrivacyError) Error

func (e *PrivacyError) Error() string

Error returns the error string.

type Querier

type Querier interface {
	// Query runs the given query on the graph and returns its result.
	Query(context.Context, Query) (Value, error)
}

Querier is the interface that wraps the Query method. Calling Querier.Query(velox.Query) triggers the execution of the query.

type QuerierFunc

type QuerierFunc func(context.Context, Query) (Value, error)

The QuerierFunc type is an adapter to allow the use of ordinary function as Querier. If f is a function with the appropriate signature, QuerierFunc(f) is a Querier that calls f.

func (QuerierFunc) Query

func (f QuerierFunc) Query(ctx context.Context, q Query) (Value, error)

Query calls f(ctx, q).

type Query

type Query any

Query represents a query builder of an entity. It is usually one of the following types: <T>Query.

type QueryContext

type QueryContext struct {
	// Op defines the operation name. e.g., First, All, Count, etc.
	Op string
	// Type defines the query type as defined in the generated code.
	Type string
	// Unique indicates if the Unique modifier was set on the query and
	// its value. Calling Unique(false) sets the value of Unique to false.
	Unique *bool
	// Limit indicates if the Limit modifier was set on the query and
	// its value. Calling Limit(10) sets the value of Limit to 10.
	Limit *int
	// Offset indicates if the Offset modifier was set on the query and
	// its value. Calling Offset(10) sets the value of Offset to 10.
	Offset *int
	// Fields specifies the fields that were selected in the query.
	Fields []string
}

QueryContext contains additional information about the context in which the query is executed.

func QueryFromContext

func QueryFromContext(ctx context.Context) *QueryContext

QueryFromContext returns the QueryContext value stored in ctx, if any.

func (*QueryContext) AppendFieldOnce

func (q *QueryContext) AppendFieldOnce(f string) *QueryContext

AppendFieldOnce adds the given field to the spec if it is not already present.

func (*QueryContext) Clone

func (q *QueryContext) Clone() *QueryContext

Clone returns a deep copy of the query context.

type QueryError

type QueryError struct {
	Entity string // Entity type being queried
	Op     string // Operation (e.g., "select", "count", "exist")
	Err    error  // Underlying error
}

QueryError wraps a query error with additional context.

func NewQueryError

func NewQueryError(entity, op string, err error) *QueryError

NewQueryError returns a new QueryError.

func (*QueryError) Error

func (e *QueryError) Error() string

Error returns the error string.

func (*QueryError) Unwrap

func (e *QueryError) Unwrap() error

Unwrap returns the underlying error.

type RollbackError

type RollbackError struct {
	Err error // Original error that triggered rollback
}

RollbackError wraps an error that occurred during a transaction rollback.

func (*RollbackError) Error

func (e *RollbackError) Error() string

Error returns the error string.

func (*RollbackError) Unwrap

func (e *RollbackError) Unwrap() error

Unwrap returns the underlying error.

type Schema

type Schema struct {
	Interface
}

Schema is the default implementation for the schema Interface. It can be embedded in end-user schemas as follows:

type T struct {
	velox.Schema
}

func (Schema) Annotations

func (Schema) Annotations() []schema.Annotation

Annotations of the schema.

func (Schema) Config

func (Schema) Config() Config

Config of the schema.

func (Schema) Edges

func (Schema) Edges() []Edge

Edges of the schema.

func (Schema) Fields

func (Schema) Fields() []Field

Fields of the schema.

func (Schema) Hooks

func (Schema) Hooks() []Hook

Hooks of the schema.

func (Schema) Indexes

func (Schema) Indexes() []Index

Indexes of the schema.

func (Schema) Interceptors

func (Schema) Interceptors() []Interceptor

Interceptors of the schema.

func (Schema) Mixin

func (Schema) Mixin() []Mixin

Mixin of the schema.

func (Schema) Policy

func (Schema) Policy() Policy

Policy of the schema.

type TraverseFunc

type TraverseFunc func(context.Context, Query) error

The TraverseFunc type is an adapter to allow the use of ordinary function as Traverser. If f is a function with the appropriate signature, TraverseFunc(f) is a Traverser that calls f.

func (TraverseFunc) Intercept

func (f TraverseFunc) Intercept(next Querier) Querier

Intercept is a dummy implementation of Intercept that returns the next Querier in the pipeline.

func (TraverseFunc) Traverse

func (f TraverseFunc) Traverse(ctx context.Context, q Query) error

Traverse calls f(ctx, q).

type Traverser

type Traverser interface {
	Traverse(context.Context, Query) error
}

Traverser defines a graph-traversal middleware for various types of Ent queries. Contrary to Interceptors, the Traverse are executed on graph traversals before the query is executed. For example:

velox.TraverseFunc(func(ctx context.Context, q velox.Query) error {
	// Filter out deleted pets.
	if pq, ok := q.(*gen.PetQuery); ok {
		pq.Where(pet.DeletedAtIsNil())
	}
	return nil
})

client.Pet.Query().
	QueryOwner().	// Pet traverse functions are applied and filter deleted pets.
	All(ctx)	// User traverse and interceptor functions are applied.

type ValidationError

type ValidationError struct {
	Name string // Field or entity name
	Err  error  // Underlying validation error
}

ValidationError represents a validation error for field values.

func NewValidationError

func NewValidationError(name string, err error) *ValidationError

NewValidationError returns a new ValidationError for the given field.

func (*ValidationError) Error

func (e *ValidationError) Error() string

Error returns the error string.

func (*ValidationError) Unwrap

func (e *ValidationError) Unwrap() error

Unwrap returns the underlying error.

type Value

type Value any

Value represents a dynamic value returned by mutations or queries.

type View

type View struct {
	Schema
}

A View only schema describes an entity that all its operations are limited to read-only. For example, a database view.

Users that wants to define a view schema should embed the View struct in their schema as follows:

type V struct {
	velox.View
}

type Viewer

type Viewer interface {
	// contains filtered or unexported methods
}

Viewer is an interface that wraps the view method. Implemented by the View struct.

Directories

Path Synopsis
Package compiler provides the Velox code generation API.
Package compiler provides the Velox code generation API.
gen
Package gen provides code generation for Velox ORM schemas.
Package gen provides code generation for Velox ORM schemas.
gen/cmd/testgen command
testgen is a simple test program to demonstrate the Jennifer-based code generator.
testgen is a simple test program to demonstrate the Jennifer-based code generator.
gen/sql
Package sql provides SQL dialect code generation for the Jennifer generator.
Package sql provides SQL dialect code generation for the Jennifer generator.
load
Package load is the interface for loading a velox/schema package into a Go program.
Package load is the interface for loading a velox/schema package into a Go program.
contrib
dataloader
Package dataloader provides generic DataLoader utilities for batch loading entities.
Package dataloader provides generic DataLoader utilities for batch loading entities.
graphql
Package graphql provides GraphQL code generation for Velox schemas.
Package graphql provides GraphQL code generation for Velox schemas.
mixin
Package mixin provides common mixin implementations for Velox schemas.
Package mixin provides common mixin implementations for Velox schemas.
Package dialect provides database dialect abstraction for Velox ORM.
Package dialect provides database dialect abstraction for Velox ORM.
sql
Package sql provides wrappers around the standard database/sql package to allow the generated code to interact with a statically-typed API.
Package sql provides wrappers around the standard database/sql package to allow the generated code to interact with a statically-typed API.
sql/schema
Package schema contains all schema migration logic for SQL dialects.
Package schema contains all schema migration logic for SQL dialects.
sql/sqlgraph
Package sqlgraph provides graph abstraction capabilities on top of sql-based databases for ent codegen.
Package sqlgraph provides graph abstraction capabilities on top of sql-based databases for ent codegen.
sqlschema
Package sqlschema provides SQL-specific annotations for Velox schemas.
Package sqlschema provides SQL-specific annotations for Velox schemas.
examples
basic/schema
Package schema defines the entity schemas for the basic example.
Package schema defines the entity schemas for the basic example.
Package graph provides the internal schema representation for Velox ORM code generation.
Package graph provides the internal schema representation for Velox ORM code generation.
Package privacy provides privacy layer types and rule implementations for Velox ORM.
Package privacy provides privacy layer types and rule implementations for Velox ORM.
Package entql provides an experimental API for interacting dynamically with ent queries.
Package entql provides an experimental API for interacting dynamically with ent queries.
internal command
A codegen cmd for generating builder types from template.
A codegen cmd for generating builder types from template.
Package schema provides the building blocks for defining Velox entity schemas.
Package schema provides the building blocks for defining Velox entity schemas.
edge
Package edge provides fluent builders for defining entity relationships in Velox ORM.
Package edge provides fluent builders for defining entity relationships in Velox ORM.
field
Package field provides fluent builders for defining entity fields in Velox ORM.
Package field provides fluent builders for defining entity fields in Velox ORM.
field/internal command
gen is a codegen cmd for generating numeric build types from template.
gen is a codegen cmd for generating numeric build types from template.
mixin
Package mixin provides reusable schema components for Velox ORM.
Package mixin provides reusable schema components for Velox ORM.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL