ModusGraph supports struct validation through the StructValidator interface. This interface is
compatible with the popular
github.com/go-playground/validator/v10 package, but
you can also provide your own implementation.
type StructValidator interface {
StructCtx(ctx context.Context, s interface{}) error
}import (
"github.com/go-playground/validator/v10"
mg "github.com/matthewmcneely/modusgraph"
)
// Create a validator instance (implements StructValidator)
validate := validator.New()
// Create a client with validator
client, err := mg.NewClient("file:///path/to/db", mg.WithValidator(validate))
if err != nil {
log.Fatal(err)
}
defer client.Close()// NewValidator() returns a *validator.Validate with default settings
client, err := mg.NewClient("file:///path/to/db", mg.WithValidator(mg.NewValidator()))You can implement your own validator:
type MyValidator struct{}
func (v *MyValidator) StructCtx(ctx context.Context, s interface{}) error {
// Your custom validation logic
return nil
}
client, err := mg.NewClient("file:///path/to/db", mg.WithValidator(&MyValidator{}))Add validation tags to your struct fields:
type User struct {
UID string `json:"uid,omitempty"`
Name string `json:"name,omitempty" validate:"required,min=2,max=100"`
Email string `json:"email,omitempty" validate:"required,email"`
Age int `json:"age,omitempty" validate:"gte=0,lte=150"`
Status string `json:"status,omitempty" validate:"oneof=active inactive pending"`
Tags []string `json:"tags,omitempty"`
}The validator will automatically run before these operations:
Insert()- Validates structs before insertionInsertRaw()- Validates structs before raw insertionUpdate()- Validates structs before updateUpsert()- Validates structs before upsert
user := &User{
Name: "John Doe",
Email: "john@example.com",
Age: 30,
Status: "active",
}
err := client.Insert(ctx, user)
if err != nil {
// Handle validation errors
log.Printf("Validation failed: %v", err)
return
}You can register custom validation functions:
validate := validator.New()
// Register a custom validation
validate.RegisterValidation("custom", func(fl validator.FieldLevel) bool {
return fl.Field().String() == "custom_value"
})
type CustomStruct struct {
Field string `json:"field,omitempty" validate:"custom"`
}If you don't want validation, simply don't provide a validator:
// No validation will be performed
client, err := mg.NewClient("file:///path/to/db")When validation fails, the operation returns the validation error from the validator package. You can check for specific validation errors:
err := client.Insert(ctx, user)
if err != nil {
// Check if it's a validation error
if validationErrors, ok := err.(validator.ValidationErrors); ok {
for _, e := range validationErrors {
fmt.Printf("Field '%s' failed validation: %s\n", e.Field(), e.Tag())
}
}
}The validator package supports many built-in validation tags:
required- Field must be present and non-emptymin,max- Minimum/maximum value for numbers, length for stringsemail- Valid email formaturl- Valid URL formatoneof- Value must be one of the specified optionsgte,lte- Greater than/equal to, less than/equal to- And many more...
See the validator documentation for a complete list.
The validate tag is also used by the modusgraph-gen code generator to detect edge cardinality.
When an edge field has validate:"max=1" or validate:"len=1", the generator produces singular
edge accessors (*Type getter/setter) instead of slice accessors:
type Film struct {
UID string `json:"uid,omitempty"`
DType []string `json:"dgraph.type,omitempty"`
director []Director `json:"director,omitempty" validate:"max=1"`
}
// Generated:
// func (f *Film) Director() *Director { ... }
// func (f *Film) SetDirector(v *Director) { ... }This provides a cleaner API when a relationship is conceptually one-to-one (or zero-to-one). When
validation is enabled (via WithValidator()), the validate tag enforces cardinality at runtime.
The generated accessors express the cardinality constraint in the API surface regardless of whether
validation is enabled. See the Code Generation section in the README for full
details on private field support.