Free & open source — no account required
Free & open source — no account required
This technical guide provides an in-depth analysis of the json to yaml engine, best practices for implementation, and data security standards.
YAML is a superset of JSON, but "idiomatic" YAML is more than JSON with brackets removed — it uses features JSON lacks: anchors and aliases (& / *) to reuse blocks without duplication, block scalars (| and >) for multi-line strings, and comments to document configuration intent. Real-world YAML targets — Kubernetes manifests, GitHub Actions workflows, Docker Compose, Helm values — each have style conventions worth following. This guide covers js-yaml's conversion options, the DRY anchor/alias pattern, multi-line scalar styles, and practical examples generating Kubernetes and CI/CD config from JSON data.
import yaml from 'js-yaml';
const config = {
name: "production-cluster",
version: "1.2.0",
database: {
host: "db.internal",
port: 5432,
name: "appdb",
ssl: true,
poolSize: 20,
},
servers: [
{ id: "node-1", role: "primary", region: "us-east-1" },
{ id: "node-2", role: "replica", region: "us-east-1" },
{ id: "node-3", role: "replica", region: "eu-west-1" },
],
features: {
logging: true,
monitoring: true,
debug: false,
},
};
// Default dump — works fine for most uses
console.log(yaml.dump(config));
// Production options:
console.log(yaml.dump(config, {
indent: 2, // indentation spaces (default: 2)
lineWidth: 120, // max line width before wrapping long strings (-1 = no limit)
noRefs: true, // don't use & anchors for repeated objects (default: false)
sortKeys: false, // preserve insertion order (true for deterministic output)
forceQuotes: false, // only quote strings when necessary
quotingType: "'", // use single quotes when quoting is needed
}));
// Output:
// name: production-cluster
// version: 1.2.0
// database:
// host: db.internal
// port: 5432
// name: appdb
// ssl: true
// poolSize: 20
// servers:
// - id: node-1
// role: primary
// region: us-east-1
// - id: node-2
// role: replica
// region: us-east-1
// - id: node-3
// role: replica
// region: eu-west-1
// features:
// logging: true
// monitoring: true
// debug: false
// YAML anchors (&name) define a reusable block
// YAML aliases (*name) reference the defined block
// Great for shared environment config, common CI steps, default values
# Without anchors (repetitive)
staging:
db_host: db-staging.internal
db_port: 5432
ssl: true
pool_size: 10
production:
db_host: db-prod.internal
db_port: 5432
ssl: true
pool_size: 50
---
# With anchors (DRY)
.db_defaults: &db_defaults
db_port: 5432
ssl: true
staging:
<<: *db_defaults # merge key — copies db_port and ssl
db_host: db-staging.internal
pool_size: 10
production:
<<: *db_defaults
db_host: db-prod.internal
pool_size: 50
// Generating YAML with anchors from JSON using js-yaml:
// js-yaml automatically uses & and * when the SAME object reference appears twice
const dbDefaults = { port: 5432, ssl: true };
const config = {
staging: { ...dbDefaults, host: "db-staging.internal", poolSize: 10 },
production: { ...dbDefaults, host: "db-prod.internal", poolSize: 50 },
};
// Spread creates NEW objects — no shared references → no anchors in output
// To force anchors, use the SAME reference:
const sharedPool = { port: 5432, ssl: true };
const configWithRefs = {
staging: { db: sharedPool, host: "db-staging.internal" },
production: { db: sharedPool, host: "db-prod.internal" },
};
console.log(yaml.dump(configWithRefs));
// staging:
// db: &ref_1
// port: 5432
// ssl: true
// host: db-staging.internal
// production:
// db: *ref_1 <-- alias references the shared object
// host: db-prod.internal
// Block scalar styles for long strings — critical for scripts in CI/CD config
// | (literal block) — preserves newlines exactly
// > (folded block) — folds newlines to spaces (single paragraph)
# Kubernetes job with inline script using literal block scalar
apiVersion: batch/v1
kind: Job
spec:
template:
spec:
containers:
- name: migrator
command: ["/bin/sh", "-c"]
args:
- |
set -e
echo "Running database migration..."
psql $DATABASE_URL -f /migrations/001_add_users.sql
psql $DATABASE_URL -f /migrations/002_add_indexes.sql
echo "Migration complete"
# Long description using folded block scalar (newlines become spaces)
description: >
This job runs database migrations on deployment.
It connects to the primary database and applies
all pending migration files in order.
// Generating block scalars with js-yaml from multiline strings:
const manifest = {
command: ["/bin/sh", "-c"],
script: "set -e\necho 'Starting...'\nnpm run migrate\necho 'Done'",
description: "Runs database migrations.\nConnects to primary DB.",
};
// js-yaml automatically uses block style for strings containing newlines
console.log(yaml.dump(manifest, { lineWidth: -1 }));
// command:
// - /bin/sh
// - -c
// script: | <-- literal block, preserves \n
// set -e
// echo 'Starting...'
// npm run migrate
// echo 'Done'
// description: |
// Runs database migrations.
// Connects to primary DB.
// Generate a Kubernetes ConfigMap manifest from application config JSON
import yaml from 'js-yaml';
interface AppConfig {
[key: string]: string | number | boolean | object;
}
function toConfigMap(name: string, namespace: string, config: AppConfig): string {
const configData: Record<string, string> = {};
for (const [key, value] of Object.entries(config)) {
// Kubernetes ConfigMap values are always strings
configData[key] = typeof value === 'object'
? JSON.stringify(value)
: String(value);
}
const manifest = {
apiVersion: 'v1',
kind: 'ConfigMap',
metadata: {
name,
namespace,
labels: { 'managed-by': 'typemorph' },
},
data: configData,
};
return yaml.dump(manifest, { lineWidth: 120, noRefs: true });
}
const appConfig = {
LOG_LEVEL: 'info',
MAX_RETRIES: 3,
FEATURE_FLAG: true,
DB_POOL_SIZE: 20,
CORS_ORIGINS: '["https://app.example.com", "https://api.example.com"]',
};
console.log(toConfigMap('app-config', 'production', appConfig));
// apiVersion: v1
// kind: ConfigMap
// metadata:
// name: app-config
// namespace: production
// labels:
// managed-by: typemorph
// data:
// LOG_LEVEL: info
// MAX_RETRIES: '3'
// FEATURE_FLAG: 'true'
// DB_POOL_SIZE: '20'
// CORS_ORIGINS: '["https://app.example.com", "https://api.example.com"]'
// Generate GitHub Actions workflow YAML from a JSON pipeline definition
const pipeline = {
name: "CI",
on: { push: { branches: ["main", "develop"] }, pull_request: null },
jobs: {
test: {
"runs-on": "ubuntu-latest",
steps: [
{ uses: "actions/checkout@v4" },
{ uses: "actions/setup-node@v4", with: { "node-version": "20", cache: "npm" } },
{ run: "npm ci" },
{ run: "npm run lint" },
{ run: "npm test", env: { CI: "true", DATABASE_URL: "${{ secrets.TEST_DB_URL }}" } },
],
},
build: {
"runs-on": "ubuntu-latest",
needs: "test",
steps: [
{ uses: "actions/checkout@v4" },
{ uses: "actions/setup-node@v4", with: { "node-version": "20", cache: "npm" } },
{ run: "npm ci" },
{ run: "npm run build" },
{ uses: "actions/upload-artifact@v4", with: { name: "build", path: "dist/" } },
],
},
},
};
const workflow = yaml.dump(pipeline, {
lineWidth: -1, // don't wrap — GitHub Actions is whitespace-sensitive
noRefs: true,
sortKeys: false,
});
console.log(workflow);
// Write to .github/workflows/ci.yml
lineWidth: -1 for configuration files that will be parsed by other tools: Automatic line wrapping in long strings can break YAML tools that don't handle continuation correctly. GitHub Actions and Kubernetes are sensitive to this — a line-wrapped command string produces two separate arguments instead of one.yamllint catches invisible whitespace errors, tab characters (forbidden in YAML), and anchor/alias cycles. Run it in CI on all generated YAML files: yamllint .github/workflows/*.yml k8s/*.yaml.sortKeys: false to preserve semantic ordering: Kubernetes manifests expect apiVersion before kind before metadata before spec. Alphabetic sorting breaks this convention and makes diffs harder to read. Only use sortKeys: true when generating config files where key order is truly irrelevant.yes, no, on, off as booleans in older spec versions (YAML 1.1). Docker Compose and some Kubernetes tools use YAML 1.1. Quote version strings like "1.2" to prevent them being parsed as floats, and quote anything that could be mistaken for a boolean.Q: What's the difference between YAML 1.1 and YAML 1.2, and which one should I use?
A: YAML 1.1 (used by most legacy tools including older PyYAML and Ruby's Psych) treats yes/no/on/off as booleans and parses unquoted floats like 1_000 with underscores. YAML 1.2 (stricter, JSON-compatible) only recognizes true/false as booleans. js-yaml defaults to YAML 1.2 behavior for output, which is safer. Always quote values that could be misinterpreted by a YAML 1.1 parser.
Q: How do I add comments to YAML generated from JSON?
A: JSON has no comment syntax, so comment information isn't in the source. Add comments by post-processing the YAML string or by using a template approach where you maintain a YAML template with comments and merge JSON values into it. The yaml package (not js-yaml) supports round-trip parsing that preserves comments if you start from a commented YAML file.
Q: Can YAML handle binary data like images?
A: Yes, YAML supports binary data with the !!binary tag, encoded as Base64. This is rarely used in practice — for configuration files, reference binary assets by path or URL rather than embedding them inline. Kubernetes Secrets use Base64-encoded values in YAML, but those are just string values, not YAML !!binary nodes.
Is the processing local-only?
Absolutely. TypeMorph operates entirely within your browser's sandbox. We use Web Workers for high-performance computation without ever transmitting your JSON, SQL, or API data to a remote server.
Can I use this for enterprise projects?
Yes. The tool is designed for professional software engineers who require GDPR compliance and data privacy. It is trusted by developers at top-tier startups and financial institutions.