Programming / JavaScript
Script

JavaScript Script to Parse and Validate JSON Payloads

This guide shows how to build a practical JavaScript script that safely parses and validates JSON payloads, handles bad input, and produces predictable output for automation and security workflows.

JavaScript Script to Parse and Validate JSON Payloads

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:

  1. it accepts input from a file or stdin
  2. it validates the arguments before processing
  3. it parses JSON inside a try/catch block
  4. it checks for a top-level object rather than an array or primitive
  5. it enforces required fields and basic type rules
  6. it optionally rejects unexpected keys in strict mode
  7. it prints structured JSON on success and clear errors on failure
  8. 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

  • id must be a non-empty string
  • type must be a non-empty string
  • timestamp must be a string that Date.parse() can interpret
  • meta is optional, but if present it must be an object
  • in --strict mode, 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.

Continue learning

Related content