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.