DNS resource record parser, validator, importer, and exporter for node.js & browsers
Quick Links: Getting Started | API Reference | Browser/CDN | Supported Records | Contributing
This module validates, imports, and exports DNS resource records (RRs) with rigorous RFC compliance and robust test coverage.
What you can do:
- Validate DNS records for RFC compliance and well-formedness
- Parse/Import records from BIND zone files, tinydns data files, or JS objects
- Export records to BIND, tinydns, MaraDNS, JSON, or wire format
- Manipulate validated records with type-safe setters
- Convert between formats seamlessly (e.g., tinydns → BIND -> wire)
Scope: This package handles individual Resource Records. For zone file operations, see dns-zone.
Quality: RFC compliance is a first-class concern. If you encounter a valid RR that fails to parse or an invalid RR that passes validation, please open an issue.
This module supports all current DNS RRs in active use on the internet.
| Usage | Resource Record Types |
|---|---|
| Common | A, AAAA, CNAME, HTTPS, MX, NS, PTR, SOA, SRV, TXT |
| Less Common | APL, CERT, DHCID, DNAME, HIP, KX, LOC, NAPTR, SVCB, TSIG, URI |
| Security | CAA, IPSECKEY, OPENPGPKEY, SMIMEA, SSHFP, TLSA |
| DNSSEC | DNSKEY, DS, NSEC, NSEC3, NSEC3PARAM, RRSIG |
| Obsolete | HINFO, KEY, NXT, RP, SIG, SPF, WKS |
npm install @nictool/dns-resource-recordValidate an A record:
import * as RR from '@nictool/dns-resource-record'
const validA = new RR.A({
owner: 'example.com.',
address: '192.0.2.1',
ttl: 3600,
})
console.log(validA.toBind())
// example.com 3600 IN A 192.0.2.1Invalid records throw immediately:
try {
new RR.A({ owner: 'example.com.', address: 'not-an-ip' })
} catch (err) {
console.error(err.message) // Error: A address must be IPv4
}This library enforces RFC-compliance during construction, catching errors early.
Every DNS record has these standard fields:
- owner — The domain name this record belongs to (always fully qualified, e.g.,
example.com.) - type — The record type (A, AAAA, MX, TXT, etc.)
- ttl — Time to live in seconds
- class — Almost always
IN(Internet); omitted in examples
Plus rdata fields specific to each record type (e.g., address for A records, preference/exchange for MX records).
When you have data in JavaScript object form:
new RR.SOA({
owner: 'example.com.',
mname: 'ns1.example.com.',
rname: 'hostmaster.example.com.',
serial: 2024042201,
refresh: 7200,
retry: 3600,
expire: 1209600,
minimum: 3600,
ttl: 3600,
})When to use: Building records programmatically, validating user input, working with API responses.
When you have a line from a BIND zone file:
const rr = RR.A.fromBind('www.example.com. 3600 IN A 192.0.2.1\n')
console.log(rr.get('address')) // '192.0.2.1'When to use: Processing BIND zone files, DNS configs, or standard zone file exports.
When you have a line from a tinydns data file:
const rr = RR.A.fromTinydns('+www.example.com:192.0.2.1:3600::\n')
console.log(rr.get('address')) // '192.0.2.1'When to use: Working with djbdns/tinydns configurations, or when parsing tinydns data files.
Convert any validated record to BIND zone file format:
new RR.MX({
owner: 'example.com.',
preference: 10,
exchange: 'mail.example.com.',
ttl: 3600,
}).toBind()
// example.com 3600 IN MX 10 mail.example.com.When to use: Generating zone files, displaying records for editing, exporting to BIND nameservers.
Convert to tinydns data file format:
new RR.A({
owner: 'example.com.',
address: '192.0.2.1',
ttl: 3600,
}).toTinydns()
// +example.com:192.0.2.1:3600::When to use: Generating tinydns data files, migrating to djbdns, or integrating with tinydns tooling.
Parse from one format, export to another:
// tinydns → BIND
const fromTiny = RR.CAA.fromTinydns(
':ns1.example.com:257:\\000\\005issue"http\\072\\057\\057letsencrypt.org":3600::\n',
)
console.log(fromTiny.toBind())
// ns1.example.com 3600 IN CAA 0 issue "http://letsencrypt.org"When to use: DNS migrations, format conversions, tool interoperability.
Use setter methods to modify validated records. Setter names follow the pattern set + FieldName (camelCased):
const a = new RR.A({
owner: 'example.com.',
address: '192.0.2.1',
ttl: 3600,
})
a.setAddress('192.0.2.2')
a.setTtl(7200)
console.log(a.toBind())
// example.com 7200 IN A 192.0.2.2Setters include validation. Invalid values throw with helpful error messages.
For a list of available setters, check getFields('rdata') for your record type:
new RR.SSHFP(null).getFields('rdata')
// ['algorithm', 'fptype', 'fingerprint']
// So use: setSshfp(), setFptype(), setFingerprint()Get information about record types:
Get field names for a record type. Pass 'rdata' to get only the custom fields:
new RR.A(null).getFields() // ['owner', 'ttl', 'class', 'type', 'address']
new RR.A(null).getFields('rdata') // ['address']
new RR.MX(null).getFields('rdata') // ['preference', 'exchange']Get RFC references for a record type:
new RR.A(null).getRFCs() // [1035]
new RR.MX(null).getRFCs() // [1035, 2181, 7505]
new RR.SRV(null).getRFCs() // [2782]Get a record with all fields populated with default/canonical values:
new RR.AAAA(null).getCanonical()
// {
// owner: 'host.example.com.',
// address: '2001:0db8:0020:000a:0000:0000:0000:0004',
// class: 'IN',
// ttl: 3600,
// type: 'AAAA'
// }Serialize a record to a plain object:
const a = new RR.A({ owner: 'example.com.', address: '192.0.2.1', ttl: 3600 })
JSON.stringify(a)
// {"owner":"example.com.","ttl":3600,"class":"IN","type":"A","address":"192.0.2.1"}The set() method bypasses validation (use carefully):
record.set('address', 'invalid-value')
// Works, but now the record is malformed — useful only for testing or low-level manipulationNote: Validation is there to protect data integrity. Use at your own risk.
Use this module directly in browsers via CDN:
<!DOCTYPE html>
<html>
<head>
<title>DNS Record Validator</title>
</head>
<body>
<h1>DNS Record Validator</h1>
<pre id="output"></pre>
<script type="module">
// Import from unpkg (or jsdelivr, esm.sh)
import * as RR from 'https://unpkg.com/@nictool/dns-resource-record@latest/dist/dns-rr.min.js'
// Use just like Node.js
const record = new RR.A({
owner: 'example.com.',
address: '192.0.2.1',
ttl: 3600,
})
document.getElementById('output').textContent = record.toBind()
</script>
</body>
</html>Available CDNs:
- unpkg:
https://unpkg.com/@nictool/dns-resource-record/dist/dns-rr.min.js - jsdelivr:
https://cdn.jsdelivr.net/npm/@nictool/dns-resource-record/dist/dns-rr.min.js - esm.sh:
https://esm.sh/@nictool/dns-resource-record
Live example: See nictool.github.io/builder/ for an interactive DNS record editor using this library.
Domain owner names are:
- Stored fully qualified (absolute), e.g.,
example.com.notexample.com - Normalized to lowercase because:
- DNS is case-insensitive (RFC 4343, RFC 1035)
- This library enforces automatic duplicate suppression
- DNSSEC canonicalization requires it (RFC 4034)
- Wire format for most RRs requires lowercase
Example:
new RR.A({ owner: 'EXAMPLE.COM', address: '192.0.2.1', ttl: 3600 })
// Automatically normalized to: 'example.com.'Master zone file expansions (relative domain names) are handled by dns-zone. This library works only with fully qualified names.
The toBind() and toMaraDNS() methods accept an options object to customize output:
record.toBind({ suppressTtl: true, suppressClass: true, relativeName: true })See dns-zone for full options documentation.
No external dependencies. Runs on node.js and modern browsers.
Key scripts:
npm test— Run all tests with node's built-in test runnernpm run watch— Run tests in watch mode during developmentnpm run lint— Check code with ESLintnpm run format— Auto-format with Prettier and fix linting issuesnpm run build— Regenerate browser bundle and READMEnpm run test:coverage— Generate test coverage report
Architecture:
- rr.js — Base class with shared DNS logic and validation
- rr/*.js — Individual RR type implementations
- test/*.js — Comprehensive test suite for each RR type
- DNS Record Types (Wikipedia)
- DNS Terminology (NicTool Dictionary)
- dns-zone — Zone file operations
- dns-nameserver — Nameserver management