Managing Configuration Files with JSON

ยท 8 min read

JSON config files are everywhere. Your package.json, tsconfig.json, prettier config, VS Code settings, and probably half the tools in your project use them. Here's how to work with them effectively.

Common JSON Config Files

You'll run into these constantly in JavaScript projects:

  • package.json - Node.js project metadata and dependencies
  • tsconfig.json - TypeScript compiler options
  • jsconfig.json - JavaScript project settings for editors
  • .prettierrc - Prettier code formatter config
  • .eslintrc.json - ESLint rules and settings
  • babel.config.json - Babel transpiler config
  • .vscode/settings.json - VS Code workspace settings
  • vercel.json, netlify.json - Deployment settings

Understanding package.json

This is the most important config file in any Node.js project. Here's what the fields mean:

{
  "name": "my-app",
  "version": "1.0.0",
  "description": "A cool application",
  "main": "index.js",
  "scripts": {
    "start": "node index.js",
    "dev": "nodemon index.js",
    "test": "jest",
    "build": "tsc"
  },
  "dependencies": {
    "express": "^4.18.2"
  },
  "devDependencies": {
    "jest": "^29.5.0",
    "nodemon": "^3.0.0"
  },
  "engines": {
    "node": ">=18.0.0"
  }
}

Key fields explained

  • name - Package name, used for npm publishing
  • version - Semantic version (major.minor.patch)
  • main - Entry point when your package is imported
  • scripts - Commands you run with npm run [name]
  • dependencies - Packages needed in production
  • devDependencies - Packages only needed during development
  • engines - Required Node.js version

Version ranges in dependencies

  • "express": "4.18.2" - Exact version only
  • "express": "^4.18.2" - Compatible with 4.x.x
  • "express": "~4.18.2" - Compatible with 4.18.x
  • "express": "*" - Any version (risky)

Environment Specific Configs

A common pattern is having different configs for development, staging, and production.

The layered approach

config/
  config.default.json   # Base settings
  config.development.json
  config.staging.json
  config.production.json

Merging configs in code

const fs = require('fs');

function loadConfig() {
  const env = process.env.NODE_ENV || 'development';

  const defaultConfig = JSON.parse(
    fs.readFileSync('./config/config.default.json')
  );

  const envConfig = JSON.parse(
    fs.readFileSync(`./config/config.${env}.json`)
  );

  // Environment config overrides defaults
  return { ...defaultConfig, ...envConfig };
}

const config = loadConfig();

Keep secrets out of JSON

Never put passwords, API keys, or other secrets in JSON config files. Use environment variables instead:

{
  "database": {
    "host": "localhost",
    "port": 5432,
    "name": "myapp"
  }
}
// Load password from environment
const dbPassword = process.env.DB_PASSWORD;

const dbConfig = {
  ...config.database,
  password: dbPassword
};

Best Practices

1. Keep configs small

If your config file is hundreds of lines, something's wrong. Split it up or use a different approach.

2. Document non-obvious settings

JSON doesn't support comments, but you can use a README or add "_comment" fields:

{
  "_comment": "Timeout is in milliseconds",
  "timeout": 30000
}

Some tools also support JSONC (JSON with Comments). Check if yours does.

3. Use schema validation

Many config formats have JSON schemas. VS Code will give you autocomplete and validation if you specify the schema:

{
  "$schema": "https://json.schemastore.org/tsconfig",
  "compilerOptions": {
    "strict": true
  }
}

4. Version control your configs

Check in your config files (except secrets). They're part of your project. Use .gitignore for local overrides.

5. Provide sensible defaults

Your app should work with minimal config. Require only what's absolutely necessary.

When Not to Use JSON

JSON is great, but sometimes other formats are better:

Use YAML when:

  • Humans edit the file frequently (more readable)
  • You need comments in the config
  • Working with DevOps tools (Kubernetes, Docker Compose)

Use TOML when:

  • You want comments but find YAML too complex
  • Working with Rust projects (Cargo.toml)

Use environment variables when:

  • Storing secrets
  • Values change between deployments
  • Running in containers

Use JavaScript/TypeScript config when:

  • You need computed values or functions
  • Type safety is important
  • Complex conditional logic
// config.js - Can use logic and environment vars
module.exports = {
  port: process.env.PORT || 3000,
  debug: process.env.NODE_ENV !== 'production',
  features: {
    newDashboard: process.env.ENABLE_NEW_DASHBOARD === 'true'
  }
};

Useful Tooling

VS Code extensions

  • JSON Schema Store - Auto-detects schemas for common configs
  • Prettier - Formats JSON files on save

Command line

  • jq - Query and transform JSON from the terminal
  • npx json5 - Convert JSON5 (with comments) to JSON

Online tools

Need to clean up messy config files? Our JSON tool can remove empty values and format your configs.