nickel
Nickel Configuration Language
Nickel is a configuration language designed to automate generation of static configuration files (JSON, YAML, TOML, XML). It combines gradual typing with runtime contracts to provide both static checking for complex logic and flexible validation for configuration data.
When to Use This Skill
Activate this skill when:
- Defining typed configuration schemas with validation contracts
- Validating YAML/JSON/TOML files against Nickel contract definitions
- Merging multiple configuration files using Nickel's merge semantics
- Converting between configuration formats (JSON ↔ YAML ↔ TOML)
- Generating type-safe configurations from a single Nickel source
- Creating reusable configuration templates with metadata
- Building configuration pipelines with validation and transformation
Key Concepts
Gradual Typing
Nickel supports both static and dynamic typing. Mix typed and untyped code within the same configuration:
- Typed regions: Functions with static type checking for complex logic
- Dynamic regions: Flexible configuration values validated at runtime with contracts
- Types are optional—choose when to use them
Contracts
Contracts are runtime assertions that validate values satisfy specific properties. They act as schemas:
- Built-in contracts:
Number,String,Bool,Dyn,Array, record contracts - Custom contracts: Functions that check predicates and return values or errors
- Composition: Combine multiple contracts with boolean logic (
std.contract.one_of,std.contract.all_of) - Metadata: Contracts attach to fields using the
|operator
Merge System
The & operator merges records with symmetric semantics:
- Symmetric merging: Both sides contribute to the result
- Recursive merging: Nested records merge recursively
- Metadata composition: Documentation, defaults, and contracts merge together
- Priorities and defaults: Control override behavior with metadata
Installation
Using mise
Add to mise.toml:
[tools]
nickel = "latest"
[tasks.validate-config]
script = "nickel eval config.ncl"
[tasks.export-config]
script = "nickel export --format json config.ncl"
Using Cargo
cargo install nickel-lang-cli
CLI Usage
Evaluate Nickel Files
nickel eval config.ncl
nickel eval config.ncl --output result.json
Export to Different Formats
# Export to JSON
nickel export config.ncl --format json
# Export to YAML
nickel export config.ncl --format yaml
# Export to TOML
nickel export config.ncl --format toml
# Merge and export
nickel export base.json overrides.ncl --format yaml
Format Code
nickel format config.ncl
nickel format --check config.ncl # Check without modifying
Interactive REPL
nickel repl
Basic Syntax
Records (Objects)
{
field1 = "value",
field2 = 42,
nested = {
key = "data",
}
}
String Interpolation
let name = "app" in
let port = 8080 in
"Starting %{name} on port %{port}"
Arrays
let ports = [8000, 8001, 8002] in
ports
Functions
let add = fun x y => x + y in
let result = add 2 3 in
result
Let Bindings
let base_port = 8000 in
let debug = true in
{
port = base_port,
debug = debug,
}
Contracts and Validation
Applying Contracts with the Pipe Operator
Attach contracts to fields using |:
{
port | Number = 8080,
name | String = "my-service",
debug | Bool = false,
}
Built-in Contracts
Nickel provides contracts for basic types:
# Number contract
value | Number
# String contract
value | String
# Boolean contract
value | Bool
# Dyn (dynamic) contract - never fails
value | Dyn
# Array contract
items | Array Number # Array of numbers
# Record contract
config | {port: Number, host: String}
Custom Contracts
Define contracts as functions that validate and return values:
# Port number validation (1-65535)
let Port = std.contract.from_predicate (
fun x => x >= 1 && x <= 65535
) in
{
port | Port = 8080,
}
Contract with Error Messages
let LogLevel =
fun label value =>
if std.array.elem value ["debug", "info", "warn", "error"]
then value
else std.contract.blame label
in
{
log_level | LogLevel = "info",
}
Combining Contracts
Use boolean contract combinators from std.contract:
# One of multiple contracts must match
value | std.contract.one_of [Contract1, Contract2]
# All contracts must match
value | std.contract.all_of [Contract1, Contract2]
# Negation
value | std.contract.not SomeContract
Importing and Validating Formats
Import YAML
let config = import "config.yaml" in
config
Import JSON
let data = import "data.json" in
data
Import TOML
let settings = import "settings.toml" in
settings
Apply Validation Contract to Import
let AppConfig = {
port | Number,
name | String,
debug | Bool,
} in
(import "app.yaml") | AppConfig
Full Validation Workflow
# Define schema
let ConfigSchema = {
app_name | String,
port | Number,
log_level | String,
database = {
host | String,
port | Number,
}
} in
# Import and validate
let imported = import "config.yaml" in
let validated = imported | ConfigSchema in
# Export as JSON
validated
Merge System
Basic Merge
let base = {
app_name = "service",
port = 8080,
} in
let overrides = {
port = 443,
} in
# Result: port = 443 (overrides takes precedence)
base & overrides
Recursive Merge
let base = {
database = {
host = "localhost",
port = 5432,
}
} in
let env_overrides = {
database = {
host = "prod.example.com",
}
} in
# Nested database.host overridden, port preserved
base & env_overrides
Defaults and Optional Fields
let defaults = {
port | default = 8080,
log_level | default = "info",
} in
let user_config = {
port = 3000,
} in
defaults & user_config # port = 3000, log_level = "info"
Anti-Fabrication Notes
When using Nickel configurations:
- Verify contract behavior by running
nickel eval file.nclto confirm validation works as expected - Test import validation using
nickel evalwith actual data files to ensure format imports succeed - Validate merge results by examining output with
nickel export --format jsonto confirm merge semantics - Check CLI flags against current Nickel version documentation, as CLI may have changed since this skill was written
Ecosystem Tools
json-schema-to-nickel
Convert JSON Schema specifications to Nickel contracts:
# Generates Nickel contract from JSON Schema
json-schema-to-nickel schema.json > schema.ncl
Topiary Integration
Nickel is Topiary's configuration language. Topiary's language configuration uses languages.ncl:
# Topiary language configuration with Nickel
{
language = "rust",
formatting_rules = {
indent = 2,
}
}
mise Task Examples
Define validation and export tasks in mise.toml:
[tasks.config:validate]
description = "Validate configuration against schema"
script = "nickel eval config.ncl --output /dev/null && echo 'Valid'"
[tasks.config:export-json]
description = "Export configuration as JSON"
script = "nickel export config.ncl --format json --output config.json"
[tasks.config:export-all]
description = "Export in all formats"
script = """
nickel export config.ncl --format json --output config.json
nickel export config.ncl --format yaml --output config.yaml
nickel export config.ncl --format toml --output config.toml
"""
Additional Resources
More from vinnie357/claude-skills
material-design
Guide for implementing Material Design 3 (Material You). Use when designing Android apps, implementing dynamic theming, or following Material component patterns.
18elixir-testing
Guide for Elixir testing with ExUnit. Use when writing unit tests, implementing property-based tests, setting up mocks, or organizing test suites.
16elixir-anti-patterns
Identify and refactor Elixir anti-patterns. Use when reviewing Elixir code for smells, refactoring problematic patterns, or improving code quality.
15phoenix-framework
Guide for Phoenix web applications. Use when building Phoenix apps, implementing LiveView, designing contexts, setting up channels, or integrating Tidewave MCP dev tools.
14nushell
Guide for using Nushell for structured data pipelines and scripting. Use when writing shell scripts, processing structured data, or working with cross-platform automation.
14documentation-writing
Guide for writing technical documentation. Use when creating README files, API documentation, guides, or inline code documentation.
12