Why this script matters
JSON is the default interchange format for APIs, queues, logs, and webhook payloads, but a payload that looks valid can still break downstream automation if required fields are missing, types are wrong, or the structure changes unexpectedly. In operational environments, that means failed jobs, noisy alerts, rejected tickets, and avoidable incidents.
This guide gives you a practical JavaScript script that parses a JSON payload, validates its shape, and returns deterministic results you can use in shell automation, CI checks, or lightweight ingestion workflows. After reading it, you will be able to decide when this approach is appropriate, run the script safely, adapt the validation rules, and verify what to check before production use.
What the script does and does not do
This script is intentionally narrow. It is designed to:
- read JSON from a file or standard input
- parse the payload safely
- validate the top-level structure and required fields
- enforce simple type and format checks
- emit machine-readable output and non-zero exit codes on failure
- avoid mutating input or calling external services by default
It does not:
- validate against a full JSON Schema document
- authenticate requests or verify signatures
- normalize arbitrary payload formats into a canonical model
- perform network calls, write to databases, or send alerts
- guarantee business-level correctness beyond the rules you define
If you need schema-heavy validation or contract enforcement across multiple services, consider whether a schema validator or an API gateway policy is a better fit. For production readiness checks around JavaScript utilities, a JavaScript Checklist for Production Readiness is useful for confirming error handling, testing, and safe deployment behavior.
Assumptions and requirements
Assumptions
The script assumes:
- the payload is JSON text, not form-encoded data or newline-delimited JSON
- top-level validation is enough for the use case
- missing or malformed input should fail closed
- the script should be safe to run in automation without side effects
Requirements
You need:
- Node.js 18 or later recommended
- access to a file path or piped standard input
- no third-party npm modules for the base version
- a shell environment capable of checking exit codes
Permissions and access
The script only needs read access to the input source. It should not require write permissions unless you later extend it to save logs, quarantine bad files, or post results to another system. If you add API calls, ticket creation, or alerting, use explicit credentials, rate limiting, and a dry-run mode until behavior is verified.
Script: parse and validate JSON payloads in JavaScript
The example below uses plain Node.js and no dependencies. It reads from a file or stdin, validates a simple event payload, and exits with a useful status code.
#!/usr/bin/env node
'use strict';
/**
* Parse and validate a JSON payload from a file or stdin.
*
* Usage:
* node validate-payload.js --file payload.json
* cat payload.json | node validate-payload.js
*
* Exit codes:
* 0 = valid
* 1 = invalid input or validation failure
* 2 = usage or runtime error
*/
const fs = require('fs');
function usage() {
console.error(
'Usage: node validate-payload.js [--file path] [--strict] [--dry-run]'
);
}
function parseArgs(argv) {
const args = { file: null, strict: false, dryRun: true };
for (let i = 2; i < argv.length; i++) {
const current = argv[i];
const next = argv[i + 1];
if (current === '--file' && next) {
args.file = next;
i++;
} else if (current === '--strict') {
args.strict = true;
} else if (current === '--dry-run') {
args.dryRun = true;
} else if (current === '--no-dry-run') {
args.dryRun = false;
} else {
throw new Error(`Unknown or incomplete argument: ${current}`);
}
}
return args;
}
function readInput(filePath) {
if (filePath) {
return fs.readFileSync(filePath, 'utf8');
}
if (process.stdin.isTTY) {
throw new Error('No input provided. Use --file or pipe JSON via stdin.');
}
return fs.readFileSync(0, 'utf8');
}
function safeParseJson(raw) {
try {
return { ok: true, value: JSON.parse(raw) };
} catch (err) {
return {
ok: false,
error: `Invalid JSON: ${err.message}`,
};
}
}
function isNonEmptyString(value) {
return typeof value === 'string' && value.trim().length > 0;
}
function validatePayload(payload, strict) {
const errors = [];
if (payload === null || Array.isArray(payload) || typeof payload !== 'object') {
errors.push('Payload must be a JSON object.');
return errors;
}
if (!isNonEmptyString(payload.id)) {
errors.push('Missing or invalid required field: id (non-empty string).');
}
if (!isNonEmptyString(payload.type)) {
errors.push('Missing or invalid required field: type (non-empty string).');
}
if (typeof payload.timestamp !== 'string' || Number.isNaN(Date.parse(payload.timestamp))) {
errors.push('Missing or invalid required field: timestamp (ISO 8601 string).');
}
if (payload.meta !== undefined) {
if (payload.meta === null || Array.isArray(payload.meta) || typeof payload.meta !== 'object') {
errors.push('Field meta must be an object when present.');
}
}
if (strict) {
const allowedKeys = new Set(['id', 'type', 'timestamp', 'meta']);
const extraKeys = Object.keys(payload).filter((key) => !allowedKeys.has(key));
if (extraKeys.length > 0) {
errors.push(`Unknown field(s) not allowed in strict mode: ${extraKeys.join(', ')}`);
}
}
return errors;
}
function main() {
let args;
try {
args = parseArgs(process.argv);
} catch (err) {
usage();
console.error(`Error: ${err.message}`);
process.exit(2);
}
if (!args.dryRun) {
console.error('Warning: this script is designed for validation only. No external actions are performed.');
}
let raw;
try {
raw = readInput(args.file);
} catch (err) {
console.error(`Error: ${err.message}`);
process.exit(2);
}
const parsed = safeParseJson(raw);
if (!parsed.ok) {
console.error(parsed.error);
process.exit(1);
}
const validationErrors = validatePayload(parsed.value, args.strict);
if (validationErrors.length > 0) {
console.error('Validation failed:');
for (const error of validationErrors) {
console.error(`- ${error}`);
}
process.exit(1);
}
const result = {
valid: true,
mode: args.dryRun ? 'dry-run' : 'validation-only',
fields: {
id: parsed.value.id,
type: parsed.value.type,
timestamp: parsed.value.timestamp,
metaPresent: parsed.value.meta !== undefined,
},
};
console.log(JSON.stringify(result, null, 2));
}
main();
How the script works
The script follows a defensive workflow:
- it accepts input from a file or stdin
- it validates the arguments before processing
- it parses JSON inside a try/catch block
- it checks for a top-level object rather than an array or primitive
- it enforces required fields and basic type rules
- it optionally rejects unexpected keys in strict mode
- it prints structured JSON on success and clear errors on failure
- it exits with predictable status codes for automation
This makes it suitable for use in pipelines where a scheduler, CI job, or ingestion wrapper needs a simple pass/fail decision.
Expected input
The default validation model expects a payload like this:
{
"id": "evt_12345",
"type": "user.created",
"timestamp": "2026-07-04T12:34:56Z",
"meta": {
"source": "api"
}
}
Validation rules used in the script
idmust be a non-empty stringtypemust be a non-empty stringtimestampmust be a string thatDate.parse()can interpretmetais optional, but if present it must be an object- in
--strictmode, no unknown top-level keys are allowed
These checks are intentionally simple and visible. If your payload has nested arrays, union types, or field-level constraints that are more complex, you should extend the validation function or move to a schema-based approach.
Example commands and expected output
Validate a file
node validate-payload.js --file payload.json
Expected output on success:
{
"valid": true,
"mode": "dry-run",
"fields": {
"id": "evt_12345",
"type": "user.created",
"timestamp": "2026-07-04T12:34:56Z",
"metaPresent": true
}
}
Validate piped input in a safe default mode
cat payload.json | node validate-payload.js
Enable strict mode
node validate-payload.js --file payload.json --strict
If the payload contains an unexpected field such as debug, the script exits with code 1 and prints a validation failure message.
Example failure output
Validation failed:
- Missing or invalid required field: timestamp (ISO 8601 string).
- Unknown field(s) not allowed in strict mode: debug
What to change before production use
The script is intentionally conservative, but you should still review it before production use.
Tighten the field model
Adjust the required fields, accepted formats, and type checks to match the contract you actually expect. If type must come from a known allowlist, enforce that explicitly.
Decide whether strict mode should be default
Strict mode is useful when you want to reject drift early. It can be too rigid if producers add optional fields frequently. Choose the default based on how stable the payload contract is.
Add schema validation if the payload is complex
If you need nested object validation, arrays with element constraints, or reusable definitions, a JSON Schema validator may be more maintainable than hand-written checks.
Add structured logging only if needed
If you extend the script to log failures to a file or forward them to another system, be careful not to expose secrets or personal data from the raw payload. Redact sensitive fields before logging.
Preserve safe defaults
Do not remove the dry-run default if you later add side effects such as ticket creation, database writes, or alerting. Safe mode should remain the default behavior until you intentionally opt in.
Security and privacy notes
JSON validation scripts often touch untrusted data, so the main risk is not code execution from JSON.parse() itself but unsafe handling after parsing.
Keep these controls in mind:
- do not use
eval()or dynamic execution on payload values - treat the payload as untrusted until validation passes
- avoid logging full payloads if they may contain secrets, tokens, or personal data
- limit file permissions and process access to only what the script needs
- reject overly large inputs if the script will be exposed to untrusted sources
If this script becomes part of a security-sensitive workflow, verify how it handles malformed input, very large payloads, and unexpected nesting depth. For teams that operationalize JavaScript utilities, a production checklist such as the JavaScript Checklist for Production Readiness helps confirm testing, error handling, and deployment safeguards.
Validation and testing steps
Before production use, test the script with representative and adversarial inputs.
Functional tests
Check that the script:
- accepts a valid payload
- rejects invalid JSON syntax
- rejects missing required fields
- rejects invalid types
- rejects extra keys when strict mode is enabled
- returns the expected exit code on each path
Negative test examples
{
"id": "",
"type": "user.created",
"timestamp": "not-a-date"
}
[
{ "id": "evt_1" }
]
{"id":"evt_1","type":"user.created",}
Operational checks
Validate how the script behaves when:
- stdin is empty
- the file path does not exist
- the payload is larger than expected
- the script is run in CI and stdout/stderr are captured separately
If your environment uses wrapper scripts or automation jobs, verify that exit codes are propagated correctly and that errors are not hidden by command substitution or shell pipelines.
Common mistakes
| Mistake | Why it matters | Better approach |
|---|---|---|
| Parsing without validating required fields | Downstream code may assume fields exist and fail later | Validate each field explicitly before using it |
| Treating any object as valid JSON payload | The JSON may parse but still be structurally unusable | Check top-level type, required keys, and value formats |
| Printing raw payloads on error | Sensitive data can leak into logs | Log concise error messages and redact content when necessary |
| Skipping exit codes | Automation cannot reliably detect success or failure | Exit 0 on success and non-zero on failure |
| Making network calls during validation | Validation becomes slow, brittle, and harder to test | Keep validation pure and side-effect free |
| Using permissive defaults in production | Bad payloads may slip through unnoticed | Keep dry-run or validation-only behavior as the default |
| Ignoring unexpected fields | Payload drift can go unnoticed until it breaks a consumer | Enable strict mode where contract stability matters |
Cleanup and rollback guidance
Because the base script has no side effects, cleanup is usually straightforward: remove temporary input files, delete test fixtures containing sensitive data, and clear any shell history that may include secrets.
If you later extend the script to write files, create tickets, or send alerts, define rollback before rollout. That means knowing how to:
- disable the script in the scheduler or pipeline
- revert to the previous script version
- delete any quarantined files or test records it created
- confirm that partially processed data was not duplicated
Keep the first rollout limited to dry-run or validation-only mode, then enable any write actions only after you have verified output, logging, and error handling under realistic inputs.
Final takeaway
A JavaScript JSON validation script is most useful when you need a small, explicit, low-risk way to parse trusted or semi-trusted payloads and fail fast on structural problems; keep it side-effect free, validate inputs conservatively, test failure paths as carefully as success, and verify the exact payload contract before you rely on it in production.
Use this guidance together with learning maturity to connect the workflow with related operational context already available on the site.
Use this guidance together with optimize Dijkstra's algorithm to connect the workflow with related operational context already available on the site.