firevault

package module
v0.10.1 Latest Latest
Warning

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

Go to latest
Published: Nov 11, 2025 License: MIT Imports: 14 Imported by: 0

README

🔥 Firevault

A production-ready Firestore ODM for Go with built-in validation, type-safe queries, and effortless data modelling.

Go Report Card Go Reference Go Version


Why Firevault?

Working with Firestore's Go SDK can be verbose and error-prone. Firevault provides a clean, type-safe abstraction layer that makes Firestore feel natural in Go, without sacrificing flexibility or performance.

// Before: Verbose Firestore SDK
doc, err := client.Collection("users").Doc(id).Get(ctx)
if err != nil { /* handle */ }
var user User
doc.DataTo(&user)
// Manual validation...
// Manual transformation...
client.Collection("users").Doc(id).Set(ctx, map[string]interface{}{
    "email": strings.ToLower(user.Email),
    "age": user.Age,
    // ...
})

// After: Clean Firevault API
collection := firevault.Collection[User](connection, "users")
user, err := collection.FindOne(ctx, NewQuery().ID(id))
// Validation + transformation handled automatically ✨
✨ Key Features
  • 🎯 Type-Safe Queries - Leverage Go generics for compile-time type safety
  • ✅ Built-in Validation - Extensive validation rules (required, email, min/max, custom validators)
  • 🔄 Automatic Transformations - Transform data before storage (lowercase, trim, hash passwords, etc.)
  • 🔐 Transaction Support - First-class transaction support with clean API
  • 📦 Single Struct Design - Use the same model for Create, Read, Update operations
  • 🚀 Performance - Validation caching for zero-overhead repeated operations
  • 🛠 Extensible - Register custom validators and transformers easily
  • 📖 Rich Error Messages - Detailed validation errors with custom formatting support

📦 Installation

go get github.com/bobch27/firevault_go

Requirements: Go 1.24+ (uses generics)


🚀 Quick Start

1. Connect to Firestore
import (
    "context"
    "log"
    "github.com/bobch27/firevault_go"
)

// Sets your Google Cloud Platform project ID.
projectID := "YOUR_PROJECT_ID"
ctx := context.Background()

connection, err := firevault.Connect(ctx, projectID)
if err != nil {
	log.Fatalln("firevault initialisation failed:", err)
}
defer connection.Close()

💡 Pro Tip: A Firevault Connection is thread-safe and designed to be used as a singleton. It caches validation metadata for optimal performance.

2. Define Your Model
type User struct {
    Name     string   `firevault:"name,required,omitempty"`
    Email    string   `firevault:"email,required,email,is_unique,omitempty"`
    Password string   `firevault:"password,required,min=6,transform:hash_pass,omitempty"`
    Address  *Address `firevault:"address,omitempty"`
    Age      int      `firevault:"age,required,min=18,omitempty"`
}

type Address struct {
    Line1 string `firevault:",omitempty"`
    City  string `firevault:"-"` // Ignored field
}
3. CRUD Operations
collection := firevault.Collection[User](connection, "users")

// Create
user := User{
    Name:     "Bobby Donev",
    Email:    "hello@bobbydonev.com",
    Password: "secure123",
    Age:      26,
}
id, err := collection.Create(ctx, &user)

// Read
user, err := collection.FindOne(ctx, NewQuery().ID(id))
users, err := collection.Find(ctx, NewQuery().Where("age", ">=", 26))

// Update
updates := User{Password: "newpassword"}
err = collection.Update(ctx, NewQuery().ID(id), &updates)

// Delete
err = collection.Delete(ctx, NewQuery().ID(id))

📚 Table of Contents


🏗 Defining Models

Models are Go structs with firevault tags that define field behaviour, validation, and transformation rules.

Tag Syntax
`firevault:"fieldName,rule1,rule2=param,transform:rule3,omitempty"`

Rules are executed in order (except omitempty variants, which can be placed anywhere).

Custom Struct Tag (Optional)

By default, Firevault uses the firevault struct tag. You can customise this:

// Use "db" instead of "firevault" as the struct tag
connection, err := firevault.Connect(ctx, projectID, "db")

// Now in your structs:
type User struct {
    Name string `db:"name,required,omitempty"`  // ✅
    // Instead of:
    // Name string `firevault:"name,required,omitempty"`
}
Built-in Rules
Rule Description
omitempty Omits field if set to default value (e.g., 0 for int, "" for string)
omitempty_create Like omitempty, but only for Create method
omitempty_update Like omitempty, but only for Update method
omitempty_validate Like omitempty, but only for Validate method
dive Recursively validate nested structs in slices/maps
- Ignores the field completely
Validation Rules
Rule Description Example
required Field must not be default value required
required_create Required only on Create required_create
required_update Required only on Update required_update
required_validate Required only on Validate required_validate
min Minimum value/length min=18
max Maximum value/length max=100
email Valid email address email
Custom Validators

Register your own validation logic:

connection.RegisterValidation(
    "is_upper",
    ValidationFunc(func(fs FieldScope) (bool, error) {
        if fs.Kind() != reflect.String {
            return false, nil
        }
        s := fs.Value().String()
        return s == strings.ToUpper(s), nil
    }),
)

// Use it in your model
type User struct {
    Name string `firevault:"name,required,is_upper,omitempty"`
}

Context-aware validators for database checks:

connection.RegisterValidation(
    "is_unique",
    ValidationFuncCtxTx(func(ctx context.Context, tx *Transaction, fs FieldScope) (bool, error) {
        doc, err := Collection[User](connection, fs.Collection()).FindOne(
            ctx,
            NewQuery().Where(fs.Field(), "==", fs.Value().Interface()),
            NewOptions().Transaction(tx),
        )
        if err != nil {
            return false, err
        }
        return doc.ID == "", nil
    }),
)
Transformations

Transform field values before storage with the transform: prefix:

Built-in transformations:

  • uppercase - Convert to uppercase
  • lowercase - Convert to lowercase
  • trim_space - Remove leading/trailing whitespace

Custom transformations:

connection.RegisterTransformation(
    "hash_pass",
    TransformationFunc(func(fs FieldScope) (interface{}, error) {
        password := fs.Value().String()
        hashed, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
        return string(hashed), err
    }),
)

type User struct {
    // Password gets hashed before storage
    Email    string `firevault:"email,required,transform:lowercase,email,omitempty"`
    Password string `firevault:"password,required,min=6,transform:hash_pass,omitempty"`
}

Order Matters: Place transformations before validation rules if you want to validate the transformed value.


💾 CRUD Operations

Create

Adds a new document to Firestore after validation.

collection := firevault.Collection[User](connection, "users")

user := User{
    Name:  "Bobby Donev",
    Email: "hello@bobbydonev.com",
    Age:   26,
}

// Auto-generated ID
id, err := collection.Create(ctx, &user)

// Custom ID
id, err := collection.Create(
    ctx,
    &user,
    NewOptions().CustomID("custom-id"),
)

// Allow zero values for specific fields
user := User{Age: 0} // Normally omitted with omitempty
id, err := collection.Create(
    ctx,
    &user,
    NewOptions().AllowEmptyFields("age"),
)
Update

Updates documents matching a query.

updates := User{Password: "newpassword123"}

// Update by ID
err := collection.Update(
    ctx,
    NewQuery().ID("user-id"),
    &updates,
)

// Update multiple documents
err := collection.Update(
    ctx,
    NewQuery().Where("age", "<", 18),
    &updates,
)

// Update boolean to false (explicitly allow zero value)
updates := User{IsActive: false}
err := collection.Update(
    ctx,
    NewQuery().ID("user-id"),
    &updates,
    NewOptions().AllowEmptyFields("is_active"),
)

// Replace entire document
err := collection.Update(
    ctx,
    NewQuery().ID("user-id"),
    &user,
    NewOptions().ReplaceAll(),
)

// Replace specific nested field
updates := User{Address: &Address{Line1: "New Street"}}
err := collection.Update(
    ctx,
    NewQuery().ID("user-id"),
    &updates,
    NewOptions().ReplaceFields("address.Line1"),
)

⚠️ Important: Without omitempty or omitempty_update, non-specified fields will be set to Go's default values. Use these rules to prevent unintended updates.

Validate

Validates data without touching the database.

user := User{Email: "HELLO@EXAMPLE.COM"}

err := collection.Validate(ctx, &user)
// Email is now "hello@example.com" if transform:lowercase is used

// Validate as if creating
err := collection.Validate(ctx, &user, NewOptions().AsCreate())

// Validate as if updating
err := collection.Validate(ctx, &user, NewOptions().AsUpdate())
Delete

Deletes documents matching a query.

// Delete by ID
err := collection.Delete(ctx, NewQuery().ID("user-id"))

// Delete with precondition
err := collection.Delete(
    ctx,
    NewQuery().ID("user-id"),
    NewOptions().RequireExists(),
)

// Delete if last updated at specific time
err := collection.Delete(
    ctx,
    NewQuery().ID("user-id"),
    NewOptions().RequireLastUpdateTime(timestamp),
)
Find

Retrieves multiple documents.

users, err := collection.Find(
    ctx,
    NewQuery().
        Where("age", ">=", 18).
        OrderBy("name", Asc).
        Limit(10),
)

// Access results
for _, doc := range users {
    fmt.Println(doc.ID)        // Document ID
    fmt.Println(doc.Data.Name) // User data
    fmt.Println(doc.Metadata.CreateTime) // Metadata
}
FindOne

Retrieves a single document.

user, err := collection.FindOne(
    ctx,
    NewQuery().ID("user-id"),
)

if user.ID == "" {
    // Document not found
}
Count

Counts documents matching a query.

count, err := collection.Count(
    ctx,
    NewQuery().Where("age", ">=", 18),
)
fmt.Printf("Found %d adults\n", count)

🔍 Querying

Build complex queries with a fluent API:

query := NewQuery().
    Where("status", "==", "active").
    Where("age", ">=", 18).
    OrderBy("createdAt", Desc).
    Limit(20).
    Offset(10)

users, err := collection.Find(ctx, query)
Available Query Methods
Method Description Example
ID(ids...) Filter by document IDs ID("id1", "id2")
Where(path, op, value) Filter by field Where("age", ">=", 18)
OrderBy(path, direction) Sort results OrderBy("name", Asc)
Limit(n) Limit results Limit(10)
LimitToLast(n) Last N results LimitToLast(5)
Offset(n) Skip N results Offset(20)
StartAt(values...) Cursor start (inclusive) StartAt(25)
StartAfter(values...) Cursor start (exclusive) StartAfter(25)
EndBefore(values...) Cursor end (exclusive) EndBefore(50)
EndAt(values...) Cursor end (inclusive) EndAt(50)
Operators

==, !=, <, <=, >, >=, array-contains, array-contains-any, in, not-in

Directions

Asc, Desc


⚙️ Options

Options provide fine-grained control over operations:

options := NewOptions().
    AllowEmptyFields("is_active", "score").
    SkipValidation("password").
    ModifyOriginal().
    Transaction(tx)
Available Options
Option Applies To Description
AllowEmptyFields(paths...) Create, Update, Validate Ignore omitempty for specified fields
SkipValidation(paths...) Create, Update, Validate Skip validation for specified fields
ModifyOriginal() Create, Update, Validate Update original struct with transformed values
CustomID(id) Create Use custom document ID
ReplaceAll() Update Replace entire document
ReplaceFields(paths...) Update Replace specific fields completely
RequireExists() Delete Only delete if document exists
RequireLastUpdateTime(t) Update, Delete Optimistic locking with timestamp
Transaction(tx) All Execute within transaction
AsCreate() Validate Validate as if creating
AsUpdate() Validate Validate as if updating

🔐 Transactions

Firevault provides first-class transaction support for atomic operations:

err := connection.RunTransaction(ctx, func(ctx context.Context, tx *Transaction) error {
    opts := NewOptions().Transaction(tx)
    
    // Read documents
    docs, err := collection.Find(ctx, NewQuery().Where("age", "==", 18), opts)
    if err != nil {
        return err
    }
    
    // Build update query from doc IDs
    updateQuery := NewQuery()
    for _, doc := range docs {
        updateQuery = updateQuery.ID(doc.ID)
    }
    
    // Update within transaction
    updates := &User{Age: 19}
    return collection.Update(ctx, updateQuery, updates, opts)
})

⚠️ Transaction Limits:

  • Only ID clause is considered for Update/Delete in transactions
  • Use Find first to get IDs, then pass them to Update/Delete
  • Maximum ~500 operations per transaction (Firestore limit)

🚨 Error Handling

Firevault provides detailed validation errors through the FieldError interface:

Custom Error Formatters
connection.RegisterErrorFormatter(func(fe FieldError) error {
    switch fe.Rule() {
    case "min":
        return fmt.Errorf("%s must be at least %s characters", 
            fe.DisplayField(), fe.Param())
    case "email":
        return fmt.Errorf("%s must be a valid email address", 
            fe.DisplayField())
    default:
        return nil // Let default error through
    }
})
Manual Error Handling
id, err := collection.Create(ctx, &user)
if err != nil {
    var fErr FieldError
    if errors.As(err, &fErr) {
        fmt.Printf("Field: %s\n", fErr.Field())          // "email"
        fmt.Printf("Rule: %s\n", fErr.Rule())            // "email"
        fmt.Printf("Value: %v\n", fErr.Value())          // "invalid@"
        fmt.Printf("Param: %s\n", fErr.Param())          // ""
        fmt.Printf("Message: %s\n", fErr.Error())        // Default message
    } else {
        // Non-validation error (network, permissions, etc.)
        fmt.Println(err)
    }
}

⚡ Performance

Firevault is designed for production use with performance in mind:

  • Validation Caching: Struct validation metadata is parsed once and cached
  • Zero Allocation Queries: Query builders reuse allocations where possible
  • Efficient Reflection: Validation uses cached type information
  • Benchmarked: Comparable to industry-leading validators like go-playground/validator (both with and without caching)

See BENCHMARKS.md for detailed performance comparisons.

Best Practices
  1. Use singleton Connection: One connection per application
  2. Reuse CollectionRef instances: Lightweight, but avoid unnecessary creation
  3. Batch operations: Use queries for bulk updates instead of loops
  4. Index your queries: Follow Firestore indexing guidelines

📖 Examples

Complete User Management System
package main

import (
    "context"
    "fmt"
    "log"
    "github.com/bobch27/firevault_go"
)

type User struct {
    Name     string    `firevault:"name,required,transform:trim_space,omitempty"`
    Email    string    `firevault:"email,required,email,transform:lowercase,omitempty"`
    Password string    `firevault:"password,required,min=8,transform:hash_pass,omitempty"`
    Age      int       `firevault:"age,required,min=13,omitempty"`
    IsActive bool      `firevault:"is_active,omitempty"`
}

func main() {
    ctx := context.Background()
    
    connection, err := firevault.Connect(ctx, "your-project-id")
    if err != nil {
        log.Fatal(err)
    }
    defer connection.Close()
    
    collection := firevault.Collection[User](connection, "users")
    
    // Create user
    user := User{
        Name:     "  Bobby Donev  ", // Will be trimmed
        Email:    "HELLO@EXAMPLE.COM", // Will be lowercased
        Password: "secure123", // Will be hashed
        Age:      26,
        IsActive: true,
    }
    
    id, err := collection.Create(ctx, &user)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Created user: %s\n", id)
    
    // Find active users
    activeUsers, err := collection.Find(
        ctx,
        NewQuery().
            Where("is_active", "==", true).
            Where("age", ">=", 18).
            OrderBy("name", Asc),
    )
    
    for _, doc := range activeUsers {
        fmt.Printf("User: %s (%s)\n", doc.Data.Name, doc.Data.Email)
    }
    
    // Deactivate user
    err = collection.Update(
        ctx,
        NewQuery().ID(id),
        &User{IsActive: false},
        NewOptions().AllowEmptyFields("is_active"),
    )
}
More Examples

Check out the /examples directory (coming soon) for:

  • Transaction examples
  • Custom validators and transformers
  • Batch operations
  • Advanced querying patterns
  • Error handling strategies

🤝 Contributing

Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.


📄 License

MIT License - see LICENSE for details.


🙏 Acknowledgments

Built with ❤️ for the Go and Firebase communities.

Special thanks to the Firestore team for the excellent SDK, and to Dean Karn for the incredible go-playground/validator package, which inspired Firevault's validation system, particularly:

  • The FieldScope pattern for passing field context to validation functions
  • The FieldError interface for detailed validation error reporting
  • The validation function signatures and registration conventions

Firevault expands on these ideas with a fully custom validation engine and a complete Firestore ODM layer - including field transformations, method-specific validations, transaction support, and CRUD operations tailored for Firestore.


⭐ If you find Firevault useful, please consider giving it a star on GitHub! ⭐

Made with 🔥 by Bobby Donev

Documentation

Index

Constants

View Source
const Delete = firestore.Delete

Delete is used as a value in a call to Update to indicate that the corresponding key should be deleted.

View Source
const DocumentID = firestore.DocumentID

DocumentID is the special field name representing the ID of a document in queries.

View Source
const ServerTimestamp = firestore.ServerTimestamp

ServerTimestamp is used as a value in a call to Update to indicate that the key's value should be set to the time at which the server processed the request.

ServerTimestamp must be the value of a field directly; it cannot appear in array or struct values, or in any value that is itself inside an array or struct.

Variables

This section is empty.

Functions

func ArrayRemove added in v0.7.5

func ArrayRemove(elements ...interface{}) interface{}

ArrayRemove specifies elements to be removed from whatever array already exists.

If a value exists and it's an array, values are removed from it. All duplicate values are removed. If a value exists and it's not an array, the value is replaced by an empty array. If a value does not exist, an empty array is created.

ArrayRemove must be the value of a field directly; it cannot appear in array or struct values, or in any value that is itself inside an array or struct.

func ArrayUnion added in v0.7.5

func ArrayUnion(elements ...interface{}) interface{}

ArrayUnion specifies elements to be added to whatever array already exists, or to create an array if no value exists.

If a value exists and it's an array, values are appended to it. Any duplicate value is ignored. If a value exists and it's not an array, the value is replaced by an array of the values in the ArrayUnion. If a value does not exist, an array of the values in the ArrayUnion is created.

ArrayUnion must be the value of a field directly; it cannot appear in array or struct values, or in any value that is itself inside an array or struct.

func Increment added in v0.7.5

func Increment(n interface{}) interface{}

Increment returns a value that can be used in Update operations to increment a numeric value atomically.

If the field does not yet exist, the transformation will set the field to the given value.

The supported values are:

int, int8, int16, int32, int64
uint8, uint16, uint32
float32, float64

Types

type CollectionRef

type CollectionRef[T interface{}] struct {
	// contains filtered or unexported fields
}

CollectionRef holds a reference to a Firestore Collection and allows for the fetching and modifying (with validation) of documents in it.

CollectionRef instances are lightweight and safe to create repeatedly. They can be freely used as needed, without concern for maintaining a singleton instance, as each instance independently references the specified Firestore collection.

func Collection

func Collection[T interface{}](connection *Connection, path string) *CollectionRef[T]

Create a new CollectionRef instance.

CollectionRef holds a reference to a Firestore Collection and allows for the fetching and modifying (with validation) of documents in it.

CollectionRef instances are lightweight and safe to create repeatedly. They can be freely used as needed, without concern for maintaining a singleton instance, as each instance independently references the specified Firestore collection.

The path argument is a sequence of IDs, separated by slashes.

Returns nil if path contains an even number of IDs, or any ID is empty.

func (*CollectionRef[T]) Count

func (c *CollectionRef[T]) Count(ctx context.Context, query Query) (int64, error)

Find number of Firestore documents which match provided Query.

func (*CollectionRef[T]) Create

func (c *CollectionRef[T]) Create(ctx context.Context, data *T, opts ...Options) (string, error)

Create a Firestore document with provided data (after validation).

By default, Firestore generates a unique document ID. Use Options to change this behaviour.

An error is returned if a document with the same ID already exists, whether auto-generated (unlikely), or provided.

To use inside a transaction, pass a transaction instance via Options.

func (*CollectionRef[T]) Delete

func (c *CollectionRef[T]) Delete(ctx context.Context, query Query, opts ...Options) error

Delete all Firestore documents which match provided Query.

If the Query doesn't contain an ID clause, documents are read first to retrieve their IDs. If no documents match, the operation does nothing and returns no error.

If the Query does contain an ID clause, the operation skips any ID that does not correspond to an existing document, without returning an error. Deletes to matching documents are applied.

In the event a precondition is set, the operation fails for any matching document that does not meet the precondition, returning an error. Other deletes are still processed.

The operation is not atomic, unless used inside a transaction via Options.

Note: In a transaction with no ID clause, document IDs are read first, which prevents any prior writes or subsequent reads in the same transaction. To work around this, use the Find/FindOne method before calling Delete.

func (*CollectionRef[T]) Find

func (c *CollectionRef[T]) Find(ctx context.Context, query Query, opts ...Options) ([]Document[T], error)

Find all Firestore documents which match provided Query.

To use inside a transaction, pass a transaction instance via Options.

func (*CollectionRef[T]) FindOne

func (c *CollectionRef[T]) FindOne(ctx context.Context, query Query, opts ...Options) (Document[T], error)

Find the first Firestore document which matches provided Query.

Returns an empty Document[T] (empty ID string and zero-value T Data), and no error if no documents are found.

To use inside a transaction, pass a transaction instance via Options.

func (*CollectionRef[T]) Update

func (c *CollectionRef[T]) Update(ctx context.Context, query Query, data *T, opts ...Options) error

Update all Firestore documents which match provided Query (after data validation).

By default, data fields are merged, preserving existing document fields. Use Options to change this behaviour.

If the Query doesn't contain an ID clause, documents are read first to retrieve their IDs. If no documents match, the operation does nothing and returns no error.

If the Query does contain an ID clause, the operation fails for any ID not corresponding to an existing document, returning an error. Updates to matching documents are applied.

In the event a precondition is set, the operation fails for any matching document that does not meet the precondition, returning an error. Other updates are still processed.

The operation is not atomic, unless used inside a transaction via Options.

Note: In a transaction with no ID clause, document IDs are read first, which prevents any prior writes or subsequent reads in the same transaction. To work around this, use the Find/FindOne method before calling Update.

func (*CollectionRef[T]) Validate

func (c *CollectionRef[T]) Validate(ctx context.Context, data *T, opts ...Options) error

Validate and transform provided data.

To use inside a transaction, pass a transaction instance via Options.

type Connection

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

Connection provides access to Firevault services.

It is designed to be thread-safe and used as a singleton instance.

A cache is used under the hood to store struct validation metadata, parsing validation tags once per struct type. Using multiple instances defeats the purpose of caching.

func Connect

func Connect(ctx context.Context, projectID string, structTag ...string) (*Connection, error)

Create a new Connection instance.

Connection provides access to Firevault services.

It is designed to be thread-safe and used as a singleton instance.

A cache is used under the hood to store struct validation metadata, parsing validation tags once per struct type. Using multiple instances defeats the purpose of caching.

The optional structTag parameter specifies a custom struct tag key for validation rules. By default, Firevault looks for the "firevault" tag.

func (*Connection) Close

func (c *Connection) Close() error

Close closes the connection to Firevault.

Should be invoked when the connection is no longer required.

Close need not be called at program exit.

func (*Connection) RegisterErrorFormatter added in v0.6.2

func (c *Connection) RegisterErrorFormatter(errorFormatter ErrorFormatterFunc) error

Register a new error formatter.

Error formatters are used to generate a custom, user-friendly error message, whenever a FieldError is created (during a failed validation).

If none are registered, or if a formatter returns a nil error, an instance of a FieldError will be returned instead.

Registering error formatters is not thread-safe; it is intended that all such functions be registered, prior to any validation.

func (*Connection) RegisterTransformation

func (c *Connection) RegisterTransformation(
	name string,
	transformation Transformation,
	runOnNil ...bool,
) error

Register a new transformation rule.

If a transformation rule with the same name already exists, the previous one will be replaced.

If the transformation name includes a method-specific suffix ("_create", "_update", or "_validate"), the rule will be applied exclusively during calls to the corresponding method type and ignored for others.

Registering such functions is not thread-safe; it is intended that all rules be registered, prior to any validation.

func (*Connection) RegisterValidation

func (c *Connection) RegisterValidation(
	name string,
	validation Validation,
	runOnNil ...bool,
) error

Register a new validation rule.

If a validation rule with the same name already exists, the previous one will be replaced.

If the validation name includes a method-specific suffix ("_create", "_update", or "_validate"), the rule will be applied exclusively during calls to the corresponding method type and ignored for others.

Registering such functions is not thread-safe; it is intended that all rules be registered, prior to any validation.

func (*Connection) RunTransaction added in v0.8.0

func (c *Connection) RunTransaction(
	ctx context.Context,
	fn func(ctx context.Context, tx *Transaction) error,
) error

Run a Firestore transaction, ensuring all operations within the provided function are executed atomically.

If any operation fails, the transaction is retried up to Firestore’s default limit. If all retries fail, the transaction is rolled back and the error is returned.

The provided function receives a Transaction instance, which should be used for all reads and writes within the transaction.

This instance can be passed into CollectionRef methods, via Options, to ensure they execute as part of the transaction.

Returns an error if the transaction fails after all retries.

type Direction

type Direction = firestore.Direction

Direction is the sort direction for result ordering.

const Asc Direction = firestore.Asc

Asc sorts results from smallest to largest.

const Desc Direction = firestore.Desc

Desc sorts results from largest to smallest.

type Document

type Document[T interface{}] struct {
	ID       string
	Data     T
	Metadata metadata
}

Document holds the ID and data related to a fetched document.

type ErrorFormatterFunc added in v0.9.0

type ErrorFormatterFunc func(fe FieldError) error

ErrorFormatterFunc is the function that's executed to generate a custom, user-friendly error message, based on FieldError's fields.

If the function returns a nil error, an instance of FieldError will be returned instead.

type FieldError

type FieldError interface {
	// Collection returns the path of the
	// collection that contains the document
	// modeled by the top-level struct.
	Collection() string
	// Field returns the field's name, with the tag
	// name taking precedence over the field's
	// struct name.
	Field() string
	// StructField returns the field's actual name
	// from the struct.
	StructField() string
	// DisplayField returns the field's struct name
	// in a human-readable form. It splits camel,
	// pascal, and snake case names into
	// space-separated words, including separating
	// adjacent letters and numbers
	// (e.g. "FirstName" -> "First Name").
	DisplayField() string
	// Path returns the field's dot-separated path,
	// with the tag names taking precedence over the
	// fields' struct names (e.g. "names.first").
	Path() string
	// StructPath returns the field's actual
	// dot-separated path from the stuct
	// (e.g. "Names.First").
	StructPath() string
	// Value returns the field's reflect Value.
	Value() reflect.Value
	// Kind returns the Value's reflect Kind
	// (eg. time.Time's kind is a struct).
	Kind() reflect.Kind
	// Type returns the Value's reflect Type
	// (eg. time.Time's type is time.Time).
	Type() reflect.Type
	// Rule returns the validation rule that failed.
	Rule() string
	// Param returns the param value, in string form
	// for comparison.
	Param() string
	// Error returns the error message.
	Error() string
}

FieldError interface gives access to all field validation error details, which aid in constructing a custom error message.

type FieldScope added in v0.6.2

type FieldScope interface {
	// Collection returns the path of the
	// collection that contains the document
	// modeled by the top-level struct.
	Collection() string
	// Struct returns the reflected parent struct
	// of the current field, if any.
	Struct() reflect.Value
	// Field returns the field's name, with the tag
	// name taking precedence over the field's
	// struct name.
	Field() string
	// StructField returns the field's actual name
	// from the struct.
	StructField() string
	// DisplayField returns the field's struct name
	// in a human-readable form. It splits camel,
	// pascal, and snake case names into
	// space-separated words, including separating
	// adjacent letters and numbers
	// (e.g. "FirstName" -> "First Name").
	DisplayField() string
	// Path returns the field's dot-separated path,
	// with the tag names taking precedence over the
	// fields' actual names (e.g. "names.first").
	Path() string
	// StructPath returns the field's actual
	// dot-separated path from the stuct
	// (e.g. "Names.First").
	StructPath() string
	// Value returns the current field's reflected
	// value to be validated.
	Value() reflect.Value
	// Kind returns the Value's reflect Kind
	// (eg. time.Time's kind is a struct).
	Kind() reflect.Kind
	// Type returns the Value's reflect Type
	// (eg. time.Time's type is time.Time).
	Type() reflect.Type
	// Rule returns the current validation's rule name.
	Rule() string
	// Param returns the param value, in string form
	// for comparison.
	Param() string
}

FieldScope interface gives access to all information needed to validate a field.

type Options

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

Options allows for the overriding of default options for CollectionRef methods.

Options values are immutable. Each Options method creates a new instance - it does not modify the old.

func NewOptions

func NewOptions() Options

Create a new Options instance.

Options allows for the overriding of default options for CollectionRef methods.

Options values are immutable. Each Options method creates a new instance - it does not modify the old.

func (Options) AllowEmptyFields

func (o Options) AllowEmptyFields(fields ...string) Options

Specify which field paths (using dot-separated strings) should ignore the "omitempty" (including method-specific) rules.

This can be useful when zero values are needed only during a specific method call.

Multiple calls to the method are cumulative.

Only applies to the Validate, Create and Update methods.

func (Options) AsCreate added in v0.7.4

func (o Options) AsCreate() Options

Allows the application of the same rules as if performing a Create operation (e.g. "required_create"), i.e. perform the same validation as the one before document creation.

Only applies to the Validate method.

func (Options) AsUpdate added in v0.7.4

func (o Options) AsUpdate() Options

Allows the application of the same rules as if performing an Update operation (e.g. "required_update"), i.e. perform the same validation as the one before document updating.

Only applies to the Validate method.

func (Options) CustomID

func (o Options) CustomID(id string) Options

Specify custom doc ID. If left empty, Firestore will automatically create one.

Only applies to the Create method.

func (Options) ModifyOriginal added in v0.7.0

func (o Options) ModifyOriginal() Options

Allows the updating of the original struct's values during transformations.

Note: Using this option makes the struct validation thread-unsafe. Use with caution.

Only applies to the Validate, Create and Update methods.

func (Options) ReplaceAll added in v0.7.7

func (o Options) ReplaceAll() Options

Ensure that the entire document is replaced with the passed in data, meaning no existing fields will be preserved. This works like Firestore's Set operation with disabled merging.

The deletion of fields is based on the provided struct, not the Firestore document itself. If the struct has changed since the document was created, some fields may not be deleted.

This option overrides any previous calls to ReplaceFields.

Only applies to the Update method.

func (Options) ReplaceFields added in v0.7.7

func (o Options) ReplaceFields(fields ...string) Options

Specify which field paths (using dot-separated strings) to be fully overwritten. Other fields on the existing document will be untouched. This works like Firestore's Set operation with specified fields to merge.

If a provided field path does not refer to a value in the data passed, it'll be ignored.

This option overrides any previous calls to ReplaceAll.

Multiple calls to the method are cumulative.

Only applies to the Update method.

func (Options) RequireExists added in v0.7.7

func (o Options) RequireExists() Options

Set a precondition that the document must exist before proceeding with the operation.

This option overrides any previous calls to RequireLastUpdateTime.

Only applies to the Delete method.

func (Options) RequireLastUpdateTime added in v0.7.7

func (o Options) RequireLastUpdateTime(t time.Time) Options

Set a precondition that the document must exist and have the specified last update timestamp before applying an update.

The operation will only proceed if the document's last update time matches the given timestamp exactly.

Timestamp must be microsecond aligned.

This option overrides any previous calls to RequireExists.

Only applies to the Update and Delete methods.

func (Options) SkipValidationFields added in v0.9.0

func (o Options) SkipValidationFields(fields ...string) Options

Skip validation for specific fields, while still enforcing the name, ignore, and omitempty rules.

If no field paths are provided, validation is skipped for all fields. Otherwise, only the specified fields (dot-separated paths) are skipped.

When used with SkipValidationRules, only the specified rules will be skipped for the provided fields.

Calling this method without arguments overrides previous calls that specified particular fields, ensuring validation is skipped for all fields.

Multiple calls to the method, with specified fields, are cumulative.

Only applies to the Validate, Create and Update methods.

func (Options) SkipValidationRules added in v0.9.0

func (o Options) SkipValidationRules(rules ...string) Options

Skip specific validation rules, while still enforcing the name, ignore, and omitempty rules.

If no rules are provided, all validation rules (including transformations) are skipped. Otherwise, only the specified rules are ignored.

When used with SkipValidationFields, the provided rules will be skipped only for the specified fields.

To skip a transformation rule, specify its name without a prefix.

Calling this method without arguments overrides previous calls that specified particular rules, ensuring all rules are skipped.

Multiple calls to the method, with specified rules, are cumulative.

Only applies to the Validate, Create and Update methods.

func (Options) Transaction added in v0.8.0

func (o Options) Transaction(tx *Transaction) Options

Execute the operation within the provided transaction.

If set, all reads and writes performed by this operation will be executed as part of the given transaction, ensuring atomicity and automatic rollback on failure.

This option overrides any previous calls to Transaction.

type Query

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

Query helps to filter and order Firestore documents.

Query values are immutable. Each Query method creates a new Query - it does not modify the old.

func NewQuery

func NewQuery() Query

Create a new Query instance.

Query helps to filter and order Firestore documents.

Query values are immutable. Each Query method creates a new Query - it does not modify the old.

func (Query) EndAt

func (q Query) EndAt(values ...interface{}) Query

EndAt returns a new Query that specifies that results should end at the document with the given field values.

EndAt should be called with one field value for each OrderBy clause, in the order that they appear.

If an OrderBy call uses the special DocumentID field path, the corresponding value should be the document ID relative to the query's collection.

Calling EndAt overrides a previous call to EndAt or EndBefore.

func (Query) EndBefore

func (q Query) EndBefore(values ...interface{}) Query

EndBefore returns a new Query that specifies that results should end just before the document with the given field values.

EndBefore should be called with one field value for each OrderBy clause, in the order that they appear.

If an OrderBy call uses the special DocumentID field path, the corresponding value should be the document ID relative to the query's collection.

Calling EndBefore overrides a previous call to EndAt or EndBefore.

func (Query) ID

func (q Query) ID(ids ...string) Query

ID returns a new Query that exclusively filters the set of results based on provided IDs.

ID takes precedence over and completely overrides any previous or subsequent calls to other Query methods, including Where - if IDs are specified, only documents matching those IDs will be returned regardless of other filter conditions.

If you need to filter by ID as well as other criteria, use the Where method with the special DocumentID field, instead of calling ID.

Multiple calls to the method are cumulative (OR condition).

func (Query) Limit

func (q Query) Limit(num int) Query

Limit returns a new Query that specifies the maximum number of first results to return. It must not be negative.

func (Query) LimitToLast

func (q Query) LimitToLast(num int) Query

LimitToLast returns a new Query that specifies the maximum number of last results to return. It must not be negative.

func (Query) Offset

func (q Query) Offset(num int) Query

Offset returns a new Query that specifies the number of initial results to skip. It must not be negative.

func (Query) OrderBy

func (q Query) OrderBy(path string, direction Direction) Query

OrderBy returns a new Query that specifies the order in which results are returned. A Query can have multiple OrderBy specifications. It appends the specification to the list of existing ones.

The path argument can be a single field or a dot-separated sequence of fields, and must not contain any of the runes "˜*/[]".

To order by document name, use the special field path DocumentID.

func (Query) StartAfter

func (q Query) StartAfter(values ...interface{}) Query

StartAfter returns a new Query that specifies that results should start just after the document with the given field values.

StartAfter should be called with one field value for each OrderBy clause, in the order that they appear.

If an OrderBy call uses the special DocumentID field path, the corresponding value should be the document ID relative to the query's collection.

Calling StartAfter overrides a previous call to StartAt or StartAfter.

func (Query) StartAt

func (q Query) StartAt(values ...interface{}) Query

StartAt returns a new Query that specifies that results should start at the document with the given field values.

StartAt should be called with one field value for each OrderBy clause, in the order that they appear.

If an OrderBy call uses the special DocumentID field path, the corresponding value should be the document ID relative to the query's collection.

Calling StartAt overrides a previous call to StartAt or StartAfter.

func (Query) Where

func (q Query) Where(path string, operator string, value interface{}) Query

Where returns a new Query that filters the set of results. A Query can have multiple filters.

The path argument can be a single field or a dot-separated sequence of fields, and must not contain any of the runes "˜*/[]".

The operator argument must be one of "==", "!=", "<", "<=", ">", ">=", "array-contains", "array-contains-any", "in" or "not-in".

type Transaction added in v0.8.0

type Transaction = firestore.Transaction

Transaction is an alias of Firestore's Transaction.

type Transformation added in v0.9.0

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

Transformation interface wraps different transformation function types.

type TransformationFunc added in v0.9.0

type TransformationFunc func(fs FieldScope) (interface{}, error)

TransformationFunc is a function that's executed during a field transformation.

type TransformationFuncCtx added in v0.9.0

type TransformationFuncCtx func(ctx context.Context, fs FieldScope) (interface{}, error)

TransformationFuncCtx is a context-aware function that's executed during a field transformation.

Useful when a transformation requires access to a context.

type TransformationFuncCtxTx added in v0.9.0

type TransformationFuncCtxTx func(ctx context.Context, tx *Transaction, fs FieldScope) (interface{}, error)

TransformationFuncCtxTx is a context-aware and transaction-aware function that's executed during a field transformation.

Useful when a transformation requires access to a context and needs to be executed inside a transaction.

The transaction argument is nil, unless explicitly provided in the Options of the calling CollectionRef method.

type TransformationFuncTx added in v0.9.0

type TransformationFuncTx func(tx *Transaction, fs FieldScope) (interface{}, error)

TransformationFuncTx is a transaction-aware function that's executed during a field transformation.

Useful when a transformation needs to be executed inside a transaction.

The transaction argument is nil, unless explicitly provided in the Options of the calling CollectionRef method.

type Validation added in v0.9.0

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

Validation interface wraps different validation function types.

type ValidationFunc added in v0.9.0

type ValidationFunc func(fs FieldScope) (bool, error)

ValidationFunc is a function that's executed during a field validation.

type ValidationFuncCtx added in v0.9.0

type ValidationFuncCtx func(ctx context.Context, fs FieldScope) (bool, error)

ValidationFuncCtx is a context-aware function that's executed during a field validation.

Useful when a validation requires access to a context.

type ValidationFuncCtxTx added in v0.9.0

type ValidationFuncCtxTx func(ctx context.Context, tx *Transaction, fs FieldScope) (bool, error)

ValidationFuncCtxTx is a context-aware and transaction-aware function that's executed during a field validation.

Useful when a validation requires access to a context and needs to be executed inside a transaction.

The transaction argument is nil, unless explicitly provided in the Options of the calling CollectionRef method.

type ValidationFuncTx added in v0.9.0

type ValidationFuncTx func(tx *Transaction, fs FieldScope) (bool, error)

ValidationFuncTx is a transaction-aware function that's executed during a field validation.

Useful when a validation needs to be executed inside a transaction.

The transaction argument is nil, unless explicitly provided in the Options of the calling CollectionRef method.

Jump to

Keyboard shortcuts

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