NetLife Guru

Open source Go packages for fast, maintainable web systems. Built with a documentation-first approach.

Product
OverviewGolang packagesNews
Documentation
DocumentationGo LoggerGo RouterGo DB Form
Company
OverviewContactNewsGitHub
Community / Support
Supportinfo@netlife.guru
© 2026 NetLife Guru. All rights reserved.
GitHubinfo@netlife.guru
NetLife GuruNetLife GuruNetLife Guru
NetLife GuruNetLife GuruNetLife Guru
OverviewDocumentationNewsSupportContact

Golang packages

About
HTTP ResponsesResponse Customization
Form ValidatorHTTP validation

HTTP Responses

Learn how NLG Form binds and validates HTTP JSON requests and returns structured validation responses using map responses, flat responses, custom messages, invalid JSON handling, and unique error codes.

The httpform package provides helpers for validating HTTP JSON requests and returning consistent API responses when validation fails.

It combines three steps into one flow:

  1. Decode the incoming JSON request body
  2. Bind the payload into a Go struct
  3. Validate the struct using a form.Schema

When validation fails, the helper writes the response automatically and returns false.

if !httpform.BindAndValidate(w, req, &in, ResponseExampleSchema(), 1<<20) {
	return
}

When validation succeeds, it returns true and the handler can continue.

The last argument defines the maximum allowed request body size. For example, 1<<20 limits the body to 1 MB.

Available Helpers

HelperDescription
BindAndValidateBinds JSON, validates the input, and returns field-based validation errors
BindAndValidateFlatBinds JSON, validates the input, and returns validation errors as a flat list
BindAndValidateWithOptionsBinds JSON, validates the input, and allows custom response format, messages, and unique error codes

Response Options

Use ResponseOptions when you need custom API behavior.

opts := httpform.ResponseOptions{
	ErrorFormat:        httpform.ErrorFormatMap,
	ValidationMessage:  "please check your input",
	InvalidJSONMessage: "request body is not valid JSON",
	UniqueCodes:        true,
}

Available options:

OptionDescription
ErrorFormatControls whether validation errors are returned as a map or flat list
ValidationMessageCustom top-level message for validation failures
InvalidJSONMessageCustom top-level message for malformed JSON requests
UniqueCodesRemoves duplicated validation error codes per field

Example Schema

package main

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

const (
	CodeEmailRequired    = form.Code("email_required")
	CodePasswordRequired = form.Code("password_required")
	CodePasswordMinLen   = form.Code("password_min_len")
)

type ResponseExampleRequest struct {
	Email    string `json:"email"`
	Password string `json:"password"`
}

func ResponseExampleSchema() form.Schema[ResponseExampleRequest] {
	ResponseExampleForm := struct {
		Email    form.StringField[ResponseExampleRequest]
		Password form.StringField[ResponseExampleRequest]
	}{
		Email: form.Str[ResponseExampleRequest]("email", func(r *ResponseExampleRequest) string {
			return r.Email
		}),
		Password: form.Str[ResponseExampleRequest]("password", func(r *ResponseExampleRequest) string {
			return r.Password
		}),
	}

	EmailSchema := form.Schema[ResponseExampleRequest]{
		rules.RequiredWithCode(ResponseExampleForm.Email, CodeEmailRequired),
		rules.Email(ResponseExampleForm.Email),
	}

	PasswordSchema := form.Schema[ResponseExampleRequest]{
		rules.RequiredWithCode(ResponseExampleForm.Password, CodePasswordRequired),
		rules.MinLenWithCode(ResponseExampleForm.Password, 8, CodePasswordMinLen),
	}

	return form.Rules(
		EmailSchema,
		PasswordSchema,
	)
}

Map Response

BindAndValidate returns validation errors grouped by field name.

r.HandleFunc("/response-map", "POST", func(w http.ResponseWriter, req *http.Request, ctx *router.Context) {
	var in ResponseExampleRequest

	if !httpform.BindAndValidate(w, req, &in, ResponseExampleSchema(), 1<<20) {
		return
	}

	w.Header().Set("Content-Type", "application/json")
	_ = json.NewEncoder(w).Encode(map[string]any{
		"message": "map response validation passed",
		"data":    in,
	})
})

This format is useful for frontend forms because each field can display its own validation errors.

Flat Response

BindAndValidateFlat returns validation errors as a flat list.

r.HandleFunc("/response-flat", "POST", func(w http.ResponseWriter, req *http.Request, ctx *router.Context) {
	var in ResponseExampleRequest

	if !httpform.BindAndValidateFlat(w, req, &in, ResponseExampleSchema(), 1<<20) {
		return
	}

	w.Header().Set("Content-Type", "application/json")
	_ = json.NewEncoder(w).Encode(map[string]any{
		"message": "flat response validation passed",
		"data":    in,
	})
})

This format is useful for APIs that display or log validation errors as a single list.

Custom Response

BindAndValidateWithOptions allows you to customize the response format and messages.

opts := httpform.ResponseOptions{
	ErrorFormat:        httpform.ErrorFormatMap,
	ValidationMessage:  "please check your input",
	InvalidJSONMessage: "request body is not valid JSON",
	UniqueCodes:        true,
}

if !httpform.BindAndValidateWithOptions(w, req, &in, ResponseExampleSchema(), 1<<20, opts) {
	return
}

Use this helper when your API needs consistent error messages, custom response formats, or deduplicated error codes.

Test Requests

The example can be tested with helper payloads:

validPayload := map[string]any{
	"email":    "john@example.com",
	"password": "secret123",
}

invalidPayload := map[string]any{
	"email":    "invalid-email",
	"password": "short",
}

emptyPayload := map[string]any{
	"email":    "",
	"password": "",
}

Example test calls:

form.SendTestPost(":8080/response-map", validPayload)
form.SendTestPost(":8080/response-map", invalidPayload)
form.SendTestPost(":8080/response-flat", invalidPayload)
form.SendTestPost(":8080/response-custom", emptyPayload)
form.SendTestPost(":8080/response-custom-flat", emptyPayload)

Note

form.SendTestPost is a helper used only to make examples self-contained and easy to run locally. In real applications, requests usually come from frontend clients, API consumers, tests, or tools such as curl and Postman.

Complete Example

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("/response-map", "POST", func(w http.ResponseWriter, req *http.Request, ctx *router.Context) {
		var in ResponseExampleRequest

		if !httpform.BindAndValidate(w, req, &in, ResponseExampleSchema(), 1<<20) {
			fmt.Println("map response validation failed")
			return
		}

		w.Header().Set("Content-Type", "application/json")
		_ = json.NewEncoder(w).Encode(map[string]any{
			"message": "map response validation passed",
			"data":    in,
		})
	})

	r.HandleFunc("/response-flat", "POST", func(w http.ResponseWriter, req *http.Request, ctx *router.Context) {
		var in ResponseExampleRequest

		if !httpform.BindAndValidateFlat(w, req, &in, ResponseExampleSchema(), 1<<20) {
			fmt.Println("flat response validation failed")
			return
		}

		w.Header().Set("Content-Type", "application/json")
		_ = json.NewEncoder(w).Encode(map[string]any{
			"message": "flat response validation passed",
			"data":    in,
		})
	})

	r.HandleFunc("/response-custom", "POST", func(w http.ResponseWriter, req *http.Request, ctx *router.Context) {
		var in ResponseExampleRequest

		opts := httpform.ResponseOptions{
			ErrorFormat:        httpform.ErrorFormatMap,
			ValidationMessage:  "please check your input",
			InvalidJSONMessage: "request body is not valid JSON",
			UniqueCodes:        true,
		}

		if !httpform.BindAndValidateWithOptions(w, req, &in, ResponseExampleSchema(), 1<<20, opts) {
			fmt.Println("custom response validation failed")
			return
		}

		w.Header().Set("Content-Type", "application/json")
		_ = json.NewEncoder(w).Encode(map[string]any{
			"message": "custom response validation passed",
			"data":    in,
		})
	})

	r.HandleFunc("/response-custom-flat", "POST", func(w http.ResponseWriter, req *http.Request, ctx *router.Context) {
		var in ResponseExampleRequest

		opts := httpform.ResponseOptions{
			ErrorFormat:        httpform.ErrorFormatFlat,
			ValidationMessage:  "please check your input",
			InvalidJSONMessage: "request body is not valid JSON",
			UniqueCodes:        true,
		}

		if !httpform.BindAndValidateWithOptions(w, req, &in, ResponseExampleSchema(), 1<<20, opts) {
			fmt.Println("custom flat response validation failed")
			return
		}

		w.Header().Set("Content-Type", "application/json")
		_ = json.NewEncoder(w).Encode(map[string]any{
			"message": "custom flat response validation passed",
			"data":    in,
		})
	})

	validPayload := map[string]any{
		"email":    "john@example.com",
		"password": "secret123",
	}

	invalidPayload := map[string]any{
		"email":    "invalid-email",
		"password": "short",
	}

	emptyPayload := map[string]any{
		"email":    "",
		"password": "",
	}

	form.SendTestPost(":8080/response-map", validPayload)
	form.SendTestPost(":8080/response-map", invalidPayload)
	form.SendTestPost(":8080/response-flat", invalidPayload)
	form.SendTestPost(":8080/response-custom", emptyPayload)
	form.SendTestPost(":8080/response-custom-flat", emptyPayload)

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

Validation Helpers

The httpform package provides multiple validation helpers depending on the response format and customization level required by the application.

BindAndValidate

httpform.BindAndValidate(w, r, &in, PostSchema(), 1<<20)

Binds the incoming JSON payload into a Go structure, validates it against the provided schema, and returns validation errors as a field-based map response.

Typical response format:

{
  "message": "validation failed",
  "errors": {
    "email": [
      {
        "code": "email_invalid",
        "message": "must be a valid email"
      }
    ]
  }
}

This helper is typically used for:

  • frontend forms
  • field-level UI validation
  • structured API validation responses

BindAndValidateFlat

httpform.BindAndValidateFlat(w, r, &in, PostSchema(), 1<<20)

Behaves similarly to BindAndValidate, but returns validation errors as a flat list instead of grouping them by field.

Typical response format:

{
  "message": "validation failed",
  "errors": [
    {
      "field": "email",
      "code": "email_invalid",
      "message": "must be a valid email"
    }
  ]
}

This format is useful for:

  • logging systems
  • CLI tools
  • flat API contracts
  • validation aggregation pipelines

BindAndValidateWithOptions

httpform.BindAndValidateWithOptions(
	w,
	r,
	&in,
	PostSchema(),
	1<<20,
	opts,
)

Provides full control over validation response formatting and behavior.

This helper allows:

  • custom validation messages
  • custom invalid JSON messages
  • flat or map response formatting
  • unique validation error codes
  • response customization for API contracts

Example:

opts := httpform.ResponseOptions{
	ErrorFormat:        httpform.ErrorFormatMap,
	ValidationMessage:  "please check your input",
	InvalidJSONMessage: "request body is not valid JSON",
	UniqueCodes:        true,
}

Use this helper when the API requires standardized validation responses across services or frontend applications.

Validation Philosophy

Learn how NLG Form uses explicit, type-safe, reusable validation schemas instead of struct tags and reflection-heavy validation pipelines.

Response Customization

Learn how to customize validation responses in NLG Form using custom messages, unique error codes, and configurable HTTP validation response options.

On this page

Available HelpersResponse OptionsExample SchemaMap ResponseFlat ResponseCustom ResponseTest RequestsComplete ExampleValidation HelpersBindAndValidateBindAndValidateFlatBindAndValidateWithOptions