After a few frustrating Google searches, I couldn’t find a direct answer for “How do I validate a JSON file against a known schema on git commit?” – so I decided to do it my own way. It’s a simple solution that I really thought would be out there already!
You can download the starter project files here.
For this project, I’m going to provide two files – data.json and schema.json. That’s it. All I’m looking to do is make a project where I’m able to update data.json and commit it with git, while guaranteeing that nothing that gets committed violates the schema. In a more complex NodeJS-based project, this same technique will still work. The schema was generated via a JSON Schema Tool.
We’re going to run two commands – the first to create a git repository (so we can actually commit something), and the second to create an NodeJS project (which we’ll use to run our precommit hooks).
git init
npm init
Now, we install two things:
We can install them with one easy command:
npm install --save-dev husky ajv-cli
We can now run a simple test of ajv to see if our data can really be validated against the schema. Below is both the command as well as the output.
npx ajv -d data.json -s schema.json
> data.json is valid
Woohoo! It works. And now as a test, change something to be invalid - I removed the line '"randomStringValue": "test"', which is a required attribute, and ran it again:
npx ajv -d data.json -s schema.json
data.json invalid
> [ { keyword: 'required',
dataPath: '',
schemaPath: '#/required',
params: { missingProperty: 'randomStringValue' },
message: 'should have required property \'randomStringValue\'' } ]
And there we can see that it lets us know that 'randomStringValue' is required, but missing. Now all that's left to do is get this to run every time we commit. To do that, all we need to do is create a new file (I named mine ajv.sh) and drop the npx line above into that file. Then, we need to open the package.json file (generated by the `npm init` command) and add the following lines:
{
"husky": {
"hooks": {
"pre-commit": "sh ajv.sh"
}
}
}
That code simply says "Before any commits (pre-commit), use the shell (sh) to run our script (ajv.sh). All of that is facilitated through the Husky tool. Now we can test our pre-commit hooks by adding our files to git staging and trying to commit them - remember that we still have an error in our data.json file!
git commit -m "Testing a known invalid data file"
> husky > pre-commit (node v10.15.3)
> data.json invalid
> [ { keyword: 'required',
dataPath: '',
schemaPath: '#/required',
params: { missingProperty: 'randomStringValue' },
message: 'should have required property \'randomStringValue\'' } ]
> husky > pre-commit hook failed (add --no-verify to bypass)
Makes sense! Now we just revert our data.json file to the original, unbroken version and run the commit one more time:
git commit -m "Testing a known valid data file"
> husky > pre-commit (node v10.15.3)
> data.json valid
> [master (root-commit) 4438c61] Testing a known valid data file
> 1 file changed, 1 insertion(+)
> create mode 100644 ajv.sh
And voila! We're done!
You can download the finished project files here.
This simple technique for JSON schema validation on commit of an NodeJS project can be extended beyond projects that are just two .json files - any NodeJS project (including a React or Vue project) could utilize this technique. By generating a schema, you guarantee that saved data files always have the required attributes, and are of the correct type.
You can include this kind of check while doing other forms of linting, and it doesn't just have to be pre-commit, it could be run pre-push, or against any of Husky's hooks.