Need help with your JSON?
Try our JSON Formatter tool to automatically identify and fix syntax errors in your JSON. JSON Formatter tool
Integrating JSON Validation with Git Pre-Commit Hooks
Configuration files, translations, API response examples – JSON is everywhere in modern development. Keeping these files consistent and valid is crucial for project stability and developer sanity. An invalid JSON file can lead to runtime errors, deployment failures, or subtle bugs that are hard to track down.
Catching these errors as early as possible in the development lifecycle is key. One of the most effective ways to do this is by integrating JSON validation directly into your Git workflow using pre-commit hooks.
Why Git Pre-Commit Hooks?
Git hooks are scripts that Git executes before or after events like commit, push, and receive. Pre-commit hooks run *before* the commit process finishes. If a pre-commit script exits with a non-zero status, Git aborts the commit. This provides a perfect opportunity to run automated checks on your code (or data files) and prevent commits that fail those checks.
Using a pre-commit hook for JSON validation means:
- Proactive Error Prevention: You catch validation errors before they even make it into your commit history or reach shared branches.
- Consistent Codebase: Ensures that all JSON files across the project adhere to defined standards and syntax rules.
- Faster Feedback Loop: Developers get immediate feedback if their JSON changes are invalid, without needing to wait for CI/CD pipelines.
Why Validate JSON? Syntax vs. Schema
JSON validation typically falls into two categories:
- Syntax Validation: This is the most basic check. It ensures that the file content follows the fundamental rules of the JSON format (correct use of braces
{}
, brackets[
], commas,
, colons:
, string quoting, etc.). An invalid JSON file is fundamentally unreadable by JSON parsers. - Schema Validation: This goes beyond basic syntax. It checks if the JSON data conforms to a predefined structure or "schema". A schema specifies things like:
- Which keys are required?
- What data types should values have (string, number, boolean, array, object)?
- What are the valid values for a specific key (e.g., from an enum list)?
- How should arrays or objects be structured?
Tools for the Job
You'll need a few tools:
- JSON Validator: A command-line tool to check JSON syntax. Examples include
jsonlint
,jq
(can be used for basic validation), or built-in options in some environments (like Node.jsJSON.parse
). - JSON Schema Validator (Optional but recommended): A tool that validates a JSON file against a JSON Schema. Examples include
ajv-cli
(for JavaScript/Node.js),json-schema-validator
(Python), etc. - Git Hook Manager (Recommended): While you can write shell scripts directly in the
.git/hooks/pre-commit
file, managing hooks across a team is easier with tools likehusky
(for Node.js projects),pre-commit
(Python-based, general purpose), orlefthook
. These tools make hooks version-controlled and shareable. - Staged File Filter (with Hook Manager): Tools like
lint-staged
(often used with Husky) allow you to run commands only on the files that are currently staged for commit, which is much more efficient than checking every file in the repository.
Method 1: Simple Syntax Validation Hook
Let's start with a basic hook script that checks only the syntax of staged JSON files. We'll use jsonlint
as an example validator.
Installation
Install jsonlint
globally or as a dev dependency in your project:
npm install -g jsonlint
or
npm install --save-dev jsonlint
Manual Hook Script (.git/hooks/pre-commit)
Navigate to your project's .git/hooks/
directory. Create a file named pre-commit
(if it doesn't exist) and make it executable (chmod +x .git/hooks/pre-commit
). Add the following script:
#!/bin/sh # Get a list of staged JSON files JSON_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep '\\.json') # Check if any JSON files are staged if [ -z "$JSON_FILES" ]; then echo "No JSON files staged. Skipping JSON syntax validation." exit 0 # Exit successfully if no JSON files fi echo "Running JSON syntax validation on staged files..." # Loop through each staged JSON file and validate for file in $JSON_FILES; do if jsonlint "$file"; then echo " ${file}: PASSED" else echo " ${file}: FAILED syntax validation!" echo " Commit aborted." exit 1 # Exit with error if validation fails fi done echo "All staged JSON files passed syntax validation." exit 0 # Exit successfully if all files pass
Now, whenever you try to commit, this script will run, check staged .json
files with jsonlint
, and block the commit if any file has a syntax error.
Method 2: Adding JSON Schema Validation
To enforce structure and data types, you need schema validation. JSON Schema is a powerful standard for this. Let's use ajv-cli
as an example.
Installation
Install ajv-cli
and ajv
(as a peer dependency):
npm install --save-dev ajv ajv-cli
Define Your Schema
Create a JSON Schema file (e.g., config.schema.json
) that describes the structure of your data:
{ "$schema": "http://json-schema.org/draft-07/schema#", "title": "Configuration Settings", "description": "Schema for application configuration file", "type": "object", "properties": { "apiEndpoint": { "type": "string", "format": "url" }, "timeoutSeconds": { "type": "integer", "minimum": 1, "maximum": 300 }, "features": { "type": "array", "items": { "type": "string", "enum": ["featureA", "featureB", "featureC"] }, "uniqueItems": true }, "enableCaching": { "type": "boolean" } }, "required": [ "apiEndpoint", "timeoutSeconds" ], "additionalProperties": false }
Update the Hook Script
Modify the .git/hooks/pre-commit
script to also run schema validation after syntax check. You'll need to map which JSON files should be validated against which schema.
#!/bin/sh # Mapping of JSON files to their schema files # IMPORTANT: Define your specific file-to-schema mappings here! declare -A SCHEMA_MAP SCHEMA_MAP["path/to/your/config.json"]="path/to/your/config.schema.json" # Add other mappings as needed: # SCHEMA_MAP["path/to/another/data.json"]="path/to/another/data.schema.json" # Get a list of staged JSON files that are in the schema map JSON_FILES_TO_VALIDATE="" for file in $(git diff --cached --name-only --diff-filter=ACM | grep '\\.json'); do if [[ -n "${SCHEMA_MAP["$file"]}" ]]; then JSON_FILES_TO_VALIDATE="$JSON_FILES_TO_VALIDATE $file" fi done # Check if any relevant JSON files are staged if [ -z "$JSON_FILES_TO_VALIDATE" ]; then echo "No relevant JSON files staged for schema validation. Skipping validation." exit 0 # Exit successfully if no relevant JSON files fi echo "Running JSON schema validation on staged files..." # Loop through each relevant staged JSON file and validate against its schema VALIDATION_FAILED=0 for file in $JSON_FILES_TO_VALIDATE; do schema_file="${SCHEMA_MAP["$file"]}" if [ ! -f "$schema_file" ]; then echo "ERROR: Schema file not found for ${file}: ${schema_file}" VALIDATION_FAILED=1 continue # Continue to check other files fi # Run ajv-cli validation # Assumes ajv-cli is available in your PATH (e.g., installed globally or in node_modules/.bin) if npx ajv-cli validate -s "$schema_file" -d "$file"; then echo " ${file}: PASSED schema validation." else echo " ${file}: FAILED schema validation!" VALIDATION_FAILED=1 # Mark failure but continue to check other files fi done if [ "$VALIDATION_FAILED" -eq 1 ]; then echo "JSON schema validation failed for one or more files." echo "Commit aborted." exit 1 else echo "All relevant staged JSON files passed schema validation." exit 0 fi
Note: This script assumes ajv-cli
is available. Using npx
is a common way to run tools installed as dev dependencies in Node.js projects. You must update the SCHEMA_MAP
variable to list the JSON files in your project and their corresponding schema files.
Method 3: Using Hook Managers (Husky + Lint-Staged)
Directly modifying .git/hooks/
is problematic: hooks are local to your repository clone, not version-controlled by default, and hard to keep consistent across a team. Hook managers solve this. husky
and lint-staged
are a popular combination for Node.js projects.
Installation
Install husky
and lint-staged
as dev dependencies:
npm install --save-dev husky lint-staged
Initialize Husky (this sets up the Git hooks pointing to Husky):
npx husky init
This creates a .husky
directory and a pre-commit
file inside it, managed by Husky.
Configuration (package.json)
Add a lint-staged
configuration to your package.json
. This specifies commands to run on staged files matching certain glob patterns.
{ "name": "your-project", "version": "1.0.0", "devDependencies": { "husky": "^8.0.0", "lint-staged": "^13.0.0", "jsonlint": "^1.6.0", "ajv": "^8.0.0", "ajv-cli": "^5.0.0" }, "scripts": { "prepare": "husky install" }, "husky": { "hooks": { "pre-commit": "lint-staged" } }, "lint-staged": { "*.json": [ "jsonlint --compact", // Basic syntax check // Add schema validation for specific files // NOTE: Schema validation with lint-staged requires careful scripting // as ajv-cli needs the schema path and the file path. // You might need a small helper script or map here. // Example (conceptual - requires specific implementation): // "node scripts/validate-json-schema.js --schema-map config.json:config.schema.json" ], "path/to/your/config.json": [ "npx ajv-cli validate -s path/to/your/config.schema.json -d" // Validate this specific file against its schema ] // Add more specific schema validation rules for other files // "path/to/another/*.json": [ // "node scripts/validate-dynamic-schema.js --schema-dir schemas/" // Example using a helper script to find schemas // ] } }
In this setup:
"prepare": "husky install"
ensures Husky is installed when dependencies are installed."husky": {"hooks": {"pre-commit": "lint-staged"}}
tells Husky to runlint-staged
on pre-commit."lint-staged"
config maps file patterns (e.g.,"*.json"
or"path/to/your/config.json"
) to commands (e.g.,"jsonlint --compact"
or"npx ajv-cli..."
).lint-staged
passes the list of staged files matching the pattern to the command.
Integrating schema validation for multiple files with lint-staged
and ajv-cli
can be slightly complex if you have many files each requiring a different schema. You might need a small Node.js or shell script that lint-staged
calls, which then iterates through the provided file list and runs ajv-cli
with the correct schema for each file based on your mapping logic. The example shows validating a specific file directly.
Benefits of Using a Hook Manager
- Version Controlled: The hook configuration lives in your
package.json
(or dedicated config files), which is tracked by Git. - Shareable: All team members get the same hooks automatically when they clone the repository and install dependencies.
- Targeted Checks:
lint-staged
ensures validation only runs on the files you've modified and staged, making it fast. - Easy Integration: Works well with other pre-commit checks like linters, formatters, and tests.
Tips and Considerations
- Performance: Validation should be fast. Avoid complex or long-running checks in pre-commit hooks, as they can frustrate developers. Validating only staged files (via
lint-staged
or equivalent) is key for performance. - Error Messages: Ensure your validation tools provide clear error messages indicating *what* is wrong and *where*. This helps developers fix issues quickly.
- Schema Management: Keep your JSON Schemas well-organized and version-controlled alongside your data files.
- Multiple JSON Files: If your project has many different types of JSON files, each requiring a different schema, careful mapping in your hook script or
lint-staged
config is needed. A dedicated helper script can simplify this. - Skipping Hooks: Developers can bypass hooks using
git commit --no-verify
. While sometimes necessary, this should be discouraged for regular commits, especially on shared branches.
Conclusion
Integrating JSON validation into your Git pre-commit hooks is a straightforward yet powerful way to improve the quality and consistency of your codebase. Whether you opt for a simple syntax check with a bash script or a more robust setup with a hook manager like Husky and schema validation using tools like AJV, the principle is the same: catch errors early, automate checks, and ensure that only valid JSON makes it into your repository. This practice saves time, reduces bugs, and makes collaboration smoother.
Need help with your JSON?
Try our JSON Formatter tool to automatically identify and fix syntax errors in your JSON. JSON Formatter tool