How to validate a payload schema


Problem
Incoming request payloads in JSON
format often requires validation.
Validators might get complex and messy.
We don’t want our codebase to be messy - at least we try not to.
Solution
JSON schema
The best way is to convert our JSON
representation of the incoming request into the JSON
schema with the help of one of the multiple converters online. I was using the Free Online JSON to JSON Schema Converter.
From the input JSON
payload:
{"id":"022eb326-6fa2-4932-8a5f-058a908616a4"}
We’re getting the JSON
shema that looks like this for the mentioned input:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"id": {
"type": "string"
}
},
"required": [
"id"
]
}
Schema validation
Now let’s get to the code.
The validation function:
// ValidateSchema validates payload against JSON schema located in the schema file.
func ValidateSchema(payload map[string]any, schema string) error {
// Let's read the schema file
file, err := os.ReadFile(schema)
if err != nil {
return fmt.Errorf("os.ReadFile: %w", err)
}
// Prepare validators for schema...
schemaLoader := gojsonschema.NewStringLoader(string(file))
// ... and payload.
payloadLoader := gojsonschema.NewGoLoader(payload)
// Validate schema against payload:
result, err := gojsonschema.Validate(schemaLoader, payloadLoader)
if err != nil {
return fmt.Errorf("gojsonschema.Validate: %w", err)
}
// If there was something wrong, communicate the errors:
if !result.Valid() {
errMsg := "JSON validation failed:\n"
for _, desc := range result.Errors() {
errMsg += fmt.Sprintf("- %s\n", desc)
}
return fmt.Errorf("%w: %s", ErrInvalidPayload, errMsg)
}
return nil
}
Working application
Let’s put all the piecen together (file main.go
):
package main
import (
"encoding/json"
"errors"
"fmt"
"log"
"os"
"github.com/google/uuid"
"github.com/xeipuuv/gojsonschema"
)
var ErrInvalidPayload = errors.New("invalid payload")
type RequestPayload struct {
ID uuid.UUID `json:"id"`
}
func main() {
// Request payload.
p, err := payload()
if err != nil {
log.Fatal(err)
}
// Validate incoming payload.
if err = ValidateSchema(p, "schema/payload.json"); err != nil {
log.Fatal(err)
}
// We're good!
fmt.Println("Schema validated")
}
// ValidateSchema validates payload against JSON schema located in the schema file.
func ValidateSchema(payload map[string]any, schema string) error {
// Let's read the schema file
file, err := os.ReadFile(schema)
if err != nil {
return fmt.Errorf("os.ReadFile: %w", err)
}
// Prepare validators for schema...
schemaLoader := gojsonschema.NewStringLoader(string(file))
// ... and payload.
payloadLoader := gojsonschema.NewGoLoader(payload)
// Validate schema against payload:
result, err := gojsonschema.Validate(schemaLoader, payloadLoader)
if err != nil {
return fmt.Errorf("gojsonschema.Validate: %w", err)
}
// If there was something wrong, communicate the errors:
if !result.Valid() {
errMsg := "JSON validation failed:\n"
for _, desc := range result.Errors() {
errMsg += fmt.Sprintf("- %s\n", desc)
}
return fmt.Errorf("%w: %s", ErrInvalidPayload, errMsg)
}
return nil
}
// payload returns sample request payload in map[string]any format.
func payload() (map[string]any, error) {
r := &RequestPayload{ID: uuid.New()}
payload, err := json.Marshal(r)
if err != nil {
return nil, fmt.Errorf("json.Marshal: %w", err)
}
var data map[string]any
if err := json.Unmarshal(payload, &data); err != nil {
return nil, fmt.Errorf("json.Unmarshal: %w", err)
}
return data, nil
}
➜ payloadschema git:(main) ✗ go run main.go
Schema validated
Sources
Subscribe to my newsletter
Read articles from Marek Skopowski directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Marek Skopowski
Marek Skopowski
Software Engineer x Data Engineer - I make the world a better place to live with software that enables data-driven decision-making