form

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: May 23, 2026 License: MIT Imports: 10 Imported by: 0

README

NLG Form

A type-safe validation framework for Go applications.

Use form to validate JSON requests, API payloads, and application input through reusable schemas, typed fields, and composable validation rules.

Go Reference Go Report Card Go Version License


Requirements

This package requires Go 1.22 or newer.

It is designed for modern Go projects and may use language and standard library features introduced in recent Go versions.

  • Go: 1.22 or newer
  • Dependencies: Standard library only

Installation

Add the package to your project using go get:

go get github.com/netlifeguru/form

Import the package into your application:

import (
	"github.com/netlifeguru/form"
	"github.com/netlifeguru/form/rules"
)

Additional optional modules are available through subpackages:

import (
	"github.com/netlifeguru/form/conditional"
	"github.com/netlifeguru/form/httpform"
	"github.com/netlifeguru/form/optional"
)

The package is designed to integrate naturally with Go structs, net/http handlers, JSON request processing, and reusable application validation workflows.

Once installed, continue with the Quick Start guide to create your first validation schema and validate incoming request data.

Getting Started

The example below demonstrates a complete validation workflow using:

  • HTTP request binding
  • Typed validation schemas
  • Reusable validation rules
  • JSON request validation
  • Structured request processing

The incoming request is a standard HTTP POST request with a JSON payload:

{
  "name": "abcdefd",
  "age": 10
}

The validation flow consists of:

  1. Defining a request structure
  2. Creating reusable typed form fields
  3. Building validation rules
  4. Binding and validating the HTTP request
  5. Returning structured JSON responses

Create the Request Schema

The schema defines reusable typed fields and validation rules for the incoming request.

Schemas are typically defined through functions instead of global variables to keep validation definitions immutable and isolated between application components.

package main

import (
	"github.com/netlifeguru/form"
	"github.com/netlifeguru/form/rules"
)

type PostRequest struct {
	Name string `json:"name"`
	Age  int    `json:"age"`
}

func PostSchema() form.Schema[PostRequest] {
	var PostForm = struct {
		Name form.StringField[PostRequest]
		Age  form.IntField[PostRequest]
	}{
		Name: form.Str[PostRequest]("name", func(r *PostRequest) string {
			return r.Name
		}),
		Age: form.Int[PostRequest]("age", func(r *PostRequest) int {
			return r.Age
		}),
	}

	var NameSchema = form.Schema[PostRequest]{
		rules.Required(PostForm.Name),
		rules.MinLen(PostForm.Name, 5),
	}

	var AgeSchema = form.Schema[PostRequest]{
		rules.RequiredInt(PostForm.Age),
		rules.Min(PostForm.Age, 8),
	}

	return form.Rules(
		NameSchema,
		AgeSchema,
	)
}

Create the HTTP Server

The HTTP handler binds the incoming JSON request into the PostRequest structure and validates it using the schema.

package main

import (
	"encoding/json"
	"fmt"
	"log/slog"
	"net/http"
	"os"

	"github.com/netlifeguru/form"
	"github.com/netlifeguru/form/httpform"
	"github.com/netlifeguru/router"
)

func main() {
	r := router.New()

	r.HandleFunc("/", "POST", func(w http.ResponseWriter, r *http.Request, ctx *router.Context) {
		var in PostRequest

		if !httpform.BindAndValidate(w, r, &in, PostSchema(), 1<<20) {
			fmt.Println("form validation failed")
			return
		}

		fmt.Println("request received:", in)

		w.Header().Set("Content-Type", "application/json")

		_ = json.NewEncoder(w).Encode(map[string]any{
			"message": "request received",
			"data":    in,
		})
	})

	form.SendTestPost(":8080/", map[string]any{
		"name": "abcdefd",
		"age":  10,
	})

	if err := r.ListenAndServe(8080); err != nil {
		slog.Error("failed to start server", "error", err)
		os.Exit(1)
	}
}

Note

The example uses the helper method:

form.SendTestPost(":8080/", map[string]any{
    "name": "abcdefd",
    "age":  10,
})

to automatically send a test HTTP POST request to the local server after startup.

This helper exists only to make the example self-contained and immediately runnable without requiring external tools such as curl or Postman.


Run the Application

Start the application:

go run .

The server starts on:

http://localhost:8080

Example Response

Successful validation returns a structured JSON response:

{
  "message": "request received",
  "data": {
    "name": "abcdefd",
    "age": 10
  }
}

If validation fails, the package automatically generates structured validation error responses through the httpform module.

Continue with the next sections to learn about validation rules, optional fields, conditional validation, custom error messages, schema composition, and advanced HTTP request processing.


Documentation

Full package documentation, guides, and examples are available at:

https://netlife.guru/docs/go/form

API reference is also available on pkg.go.dev:

https://pkg.go.dev/github.com/netlifeguru/form


Notes

  • Review package-specific concurrency behavior before using it in highly parallel workloads.
  • Check performance characteristics when using this package in latency-sensitive paths.
  • See the package documentation and examples for limitations and recommended usage patterns.

Versioning

This project follows Semantic Versioning.
See CHANGELOG.md for release history and breaking changes.


Contributing

Community contributions, feedback, and improvements are welcome.

Please read CONTRIBUTING.md before submitting pull requests or opening issues.


Code of Conduct

This project follows a Code of Conduct.

Please read CODE_OF_CONDUCT.md before contributing or participating in discussions.


Author

Created and maintained by NetLife Guru s.r.o.


License

MIT License. See LICENSE.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrInvalidJSON  = errors.New("invalid json")
	ErrMultipleJSON = errors.New("multiple json objects")
)

Functions

func BoolIsEmpty

func BoolIsEmpty(v bool) bool

func DecodeJSONBytesStrict

func DecodeJSONBytesStrict(dst any, body []byte) error

func Float64IsEmpty

func Float64IsEmpty(v float64) bool

func Int64IsEmpty

func Int64IsEmpty(v int64) bool

func IntIsEmpty

func IntIsEmpty(v int) bool

func LogRequest

func LogRequest(r *http.Request)

func OptBoolIsEmpty

func OptBoolIsEmpty(v *bool) bool

func OptIntIsEmpty

func OptIntIsEmpty(v *int) bool

func OptStrIsEmpty

func OptStrIsEmpty(v *string) bool

func OptTimeIsEmpty

func OptTimeIsEmpty(v *time.Time) bool

func PreparePost

func PreparePost[T any](port string, data T) error

func SendTestPost

func SendTestPost[T any](port string, data T)

func SliceStrIsEmpty

func SliceStrIsEmpty(v []string) bool

func StrIsEmpty

func StrIsEmpty(v string) bool

func TimeIsEmpty

func TimeIsEmpty(v time.Time) bool

func Uint64IsEmpty

func Uint64IsEmpty(v uint64) bool

func UintIsEmpty

func UintIsEmpty(v uint) bool

Types

type BoolField

type BoolField[T any] struct{ Field[T, bool] }

func Bool

func Bool[T any](name string, get func(*T) bool) BoolField[T]

type Code

type Code string
const (
	CodeRequired Code = "required"
	CodeInvalid  Code = "invalid"

	CodeMinLen  Code = "min_len"
	CodeMaxLen  Code = "max_len"
	CodeMin     Code = "min"
	CodeMax     Code = "max"
	CodeBetween Code = "between"

	CodeRegex    Code = "regex"
	CodeEmail    Code = "email"
	CodeURL      Code = "url"
	CodeUUID     Code = "uuid"
	CodeTimezone Code = "timezone"

	CodeIP   Code = "ip"
	CodeJSON Code = "json"

	CodeDistinct Code = "distinct"

	CodeContains   Code = "contains"
	CodeStartsWith Code = "starts_with"
	CodeEndsWith   Code = "ends_with"

	CodeAlpha     Code = "alpha"
	CodeAlphaNum  Code = "alnum"
	CodeAlphaDash Code = "alphadash"

	CodeLowercase Code = "lowercase"
	CodeUppercase Code = "uppercase"

	CodeCompare Code = "compare"
)

type ErrorItem

type ErrorItem struct {
	Field string `json:"field"`
	Code  Code   `json:"code"`
}

type Errors

type Errors []FieldError

func ParseAndValidateBytesStrict

func ParseAndValidateBytesStrict[T any](body []byte, dst *T, schema Schema[T]) (Errors, error)

func Validate

func Validate[T any](in *T, schema Schema[T]) Errors

func (*Errors) Add

func (e *Errors) Add(field string, code Code)

func (Errors) ByField

func (e Errors) ByField() map[string][]Code

func (Errors) ByFieldUnique

func (e Errors) ByFieldUnique() map[string][]Code

func (Errors) Empty

func (e Errors) Empty() bool

func (Errors) First

func (e Errors) First(field string) (Code, bool)

func (Errors) Has

func (e Errors) Has(field string) bool

func (Errors) HasCode

func (e Errors) HasCode(field string, code Code) bool

func (Errors) Items

func (e Errors) Items() []ErrorItem

type Field

type Field[T any, V any] struct {
	Name    string
	Get     func(*T) V
	IsEmpty func(V) bool
}

type FieldError

type FieldError struct {
	Field string `json:"field"`
	Code  Code   `json:"code"`
}

type Float64Field

type Float64Field[T any] struct{ Field[T, float64] }

func Float64

func Float64[T any](name string, get func(*T) float64) Float64Field[T]

type Int64Field

type Int64Field[T any] struct{ Field[T, int64] }

func Int64

func Int64[T any](name string, get func(*T) int64) Int64Field[T]

type IntField

type IntField[T any] struct{ Field[T, int] }

func Int

func Int[T any](name string, get func(*T) int) IntField[T]

type OptBoolField

type OptBoolField[T any] struct{ Field[T, *bool] }

func OptBool

func OptBool[T any](name string, get func(*T) *bool) OptBoolField[T]

type OptFloat64Field

type OptFloat64Field[T any] struct {
	Field[T, *float64]
}

func OptFloat64

func OptFloat64[T any](name string, get func(*T) *float64) OptFloat64Field[T]

type OptIntField

type OptIntField[T any] struct{ Field[T, *int] }

func OptInt

func OptInt[T any](name string, get func(*T) *int) OptIntField[T]

type OptStringField

type OptStringField[T any] struct{ Field[T, *string] }

--- nullable (pointer) fields ---

func OptStr

func OptStr[T any](name string, get func(*T) *string) OptStringField[T]

type OptTimeField

type OptTimeField[T any] struct{ Field[T, *time.Time] }

func OptTime

func OptTime[T any](name string, get func(*T) *time.Time) OptTimeField[T]

type Rule

type Rule[T any] interface{ Apply(in *T, out *Errors) }

type RuleFunc

type RuleFunc[T any] func(in *T, out *Errors)

func (RuleFunc[T]) Apply

func (f RuleFunc[T]) Apply(in *T, out *Errors)

type Schema

type Schema[T any] []Rule[T]

func Rules

func Rules[T any](parts ...Schema[T]) Schema[T]

type SliceStringField

type SliceStringField[T any] struct{ Field[T, []string] }

--- collection fields ---

func SliceStr

func SliceStr[T any](name string, get func(*T) []string) SliceStringField[T]

type StringField

type StringField[T any] struct{ Field[T, string] }

func Str

func Str[T any](name string, get func(*T) string) StringField[T]

--- constructors (scalars) ---

type TimeField

type TimeField[T any] struct{ Field[T, time.Time] }

func Time

func Time[T any](name string, get func(*T) time.Time) TimeField[T]

type Uint64Field

type Uint64Field[T any] struct{ Field[T, uint64] }

func Uint64

func Uint64[T any](name string, get func(*T) uint64) Uint64Field[T]

type UintField

type UintField[T any] struct{ Field[T, uint] }

func Uint

func Uint[T any](name string, get func(*T) uint) UintField[T]

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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