Skip to content

Commit a8b630d

Browse files
committed
Initial commit
0 parents  commit a8b630d

28 files changed

Lines changed: 6403 additions & 0 deletions

.github/workflows/ci.yml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
concurrency:
10+
group: ${{ github.workflow }}-${{ github.ref }}
11+
cancel-in-progress: true
12+
13+
jobs:
14+
test:
15+
name: Node ${{ matrix.node-version }}
16+
runs-on: ubuntu-latest
17+
strategy:
18+
fail-fast: false
19+
matrix:
20+
node-version: [20, 22, 24]
21+
22+
steps:
23+
- uses: actions/checkout@v6
24+
25+
- uses: actions/setup-node@v4
26+
with:
27+
node-version: ${{ matrix.node-version }}
28+
cache: npm
29+
30+
- run: npm ci
31+
32+
- run: npm run lint
33+
- run: npm run format:check
34+
- run: npm run typecheck
35+
36+
- run: npm test
37+
38+
- run: npm run build
39+
- run: npm run attw

.github/workflows/publish.yml

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
name: Publish to npm
2+
3+
on:
4+
release:
5+
types: [published]
6+
7+
concurrency:
8+
group: ${{ github.workflow }}
9+
cancel-in-progress: false
10+
11+
permissions:
12+
contents: read
13+
id-token: write
14+
15+
jobs:
16+
test:
17+
name: Test (Node ${{ matrix.node-version }})
18+
runs-on: ubuntu-latest
19+
strategy:
20+
fail-fast: true
21+
matrix:
22+
node-version: [20, 22, 24]
23+
24+
steps:
25+
- uses: actions/checkout@v6
26+
27+
- uses: actions/setup-node@v4
28+
with:
29+
node-version: ${{ matrix.node-version }}
30+
cache: npm
31+
32+
- run: npm ci
33+
- run: npm run lint
34+
- run: npm run format:check
35+
- run: npm run typecheck
36+
- run: npm test
37+
- run: npm run build
38+
- run: npm run attw
39+
40+
publish:
41+
name: Publish
42+
needs: test
43+
runs-on: ubuntu-latest
44+
environment: release
45+
46+
steps:
47+
- uses: actions/checkout@v6
48+
49+
- uses: actions/setup-node@v4
50+
with:
51+
node-version: 24
52+
cache: npm
53+
registry-url: https://registry.npmjs.org
54+
55+
- run: npm ci
56+
- run: npm run build
57+
58+
- run: npm publish --provenance --access public
59+
env:
60+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
node_modules/
2+
dist/
3+
coverage/
4+
*.tsbuildinfo
5+
.DS_Store

.npmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
engine-strict=true

.nvmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
24

.prettierrc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"semi": true,
3+
"singleQuote": false,
4+
"trailingComma": "all",
5+
"printWidth": 100,
6+
"tabWidth": 2
7+
}

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026 RDAP API
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
# RDAP API Node.js SDK
2+
3+
[![npm version](https://img.shields.io/npm/v/rdapapi.svg)](https://www.npmjs.com/package/rdapapi)
4+
[![Node.js](https://img.shields.io/node/v/rdapapi.svg)](https://www.npmjs.com/package/rdapapi)
5+
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
6+
7+
Official Node.js SDK for the [RDAP API](https://rdapapi.io) — look up domains, IP addresses, ASNs, nameservers, and entities via the RDAP protocol.
8+
9+
## Installation
10+
11+
```bash
12+
npm install rdapapi
13+
```
14+
15+
## Quick start
16+
17+
```typescript
18+
import { RdapClient } from "rdapapi";
19+
20+
const client = new RdapClient("your-api-key");
21+
22+
// Domain lookup
23+
const domain = await client.domain("google.com");
24+
console.log(domain.registrar.name); // "MarkMonitor Inc."
25+
console.log(domain.dates.expires); // "2028-09-14T04:00:00Z"
26+
console.log(domain.nameservers); // ["ns1.google.com", ...]
27+
28+
// IP address lookup
29+
const ip = await client.ip("8.8.8.8");
30+
console.log(ip.name); // "GOGL"
31+
console.log(ip.cidr); // ["8.8.8.0/24"]
32+
33+
// ASN lookup
34+
const asn = await client.asn(15169);
35+
console.log(asn.name); // "GOOGLE"
36+
37+
// Nameserver lookup
38+
const ns = await client.nameserver("ns1.google.com");
39+
console.log(ns.ipAddresses.v4); // ["216.239.32.10"]
40+
41+
// Entity lookup
42+
const entity = await client.entity("GOGL");
43+
console.log(entity.name); // "Google LLC"
44+
console.log(entity.autnums[0].handle); // "AS15169"
45+
46+
client.close();
47+
```
48+
49+
## Bulk domain lookups
50+
51+
Look up multiple domains in a single request (Pro and Business plans). Up to 10 domains per call, with concurrent upstream fetches:
52+
53+
```typescript
54+
const result = await client.bulkDomains(["google.com", "github.com", "invalid..com"], {
55+
follow: true,
56+
});
57+
58+
console.log(result.summary); // { total: 3, successful: 2, failed: 1 }
59+
60+
for (const r of result.results) {
61+
if (r.status === "success" && r.data) {
62+
console.log(`${r.data.domain}: ${r.data.registrar.name}`);
63+
} else {
64+
console.log(`${r.domain}: ${r.error}`);
65+
}
66+
}
67+
```
68+
69+
Each domain counts as one request toward your monthly quota. Starter plans receive a `SubscriptionRequiredError` (403).
70+
71+
## Registrar follow-through
72+
73+
For thin registries like `.com` and `.net`, the registry only returns basic registrar info. Use `follow: true` to follow the registrar's RDAP link and get richer contact data:
74+
75+
```typescript
76+
const domain = await client.domain("google.com", { follow: true });
77+
console.log(domain.entities.registrant?.organization); // "Google LLC"
78+
console.log(domain.entities.registrant?.email); // "registrant@google.com"
79+
```
80+
81+
## Error handling
82+
83+
```typescript
84+
import { RdapClient, NotFoundError, RateLimitError, AuthenticationError } from "rdapapi";
85+
86+
const client = new RdapClient("your-api-key");
87+
88+
try {
89+
const domain = await client.domain("nonexistent.example");
90+
} catch (err) {
91+
if (err instanceof NotFoundError) {
92+
console.log("Domain not found");
93+
} else if (err instanceof RateLimitError) {
94+
console.log(`Rate limited. Retry after ${err.retryAfter}s`);
95+
} else if (err instanceof AuthenticationError) {
96+
console.log("Invalid API key");
97+
}
98+
}
99+
```
100+
101+
All exceptions inherit from `RdapApiError` and include `statusCode`, `error`, and `message` properties.
102+
103+
| Exception | HTTP Status | When |
104+
| --------------------------- | ----------- | ---------------------------- |
105+
| `ValidationError` | 400 | Invalid input format |
106+
| `AuthenticationError` | 401 | Missing or invalid API key |
107+
| `SubscriptionRequiredError` | 403 | No active subscription |
108+
| `NotFoundError` | 404 | No RDAP data found |
109+
| `RateLimitError` | 429 | Rate limit or quota exceeded |
110+
| `UpstreamError` | 502 | Upstream RDAP server error |
111+
112+
## Configuration
113+
114+
```typescript
115+
const client = new RdapClient("your-api-key", {
116+
baseUrl: "https://rdapapi.io/api/v1", // default
117+
timeout: 30_000, // milliseconds, default
118+
});
119+
```
120+
121+
## TypeScript
122+
123+
The SDK is written in TypeScript with full type definitions. All response types are exported:
124+
125+
```typescript
126+
import type { DomainResponse, IpResponse, AsnResponse } from "rdapapi";
127+
```
128+
129+
## Requirements
130+
131+
- Node.js 20 or later
132+
- An API key from [rdapapi.io](https://rdapapi.io/register)
133+
134+
## Links
135+
136+
- [API Documentation](https://rdapapi.io/docs)
137+
- [Get an API Key](https://rdapapi.io/register)
138+
- [OpenAPI Spec](https://rdapapi.io/openapi.yaml)
139+
- [Pricing](https://rdapapi.io/pricing)
140+
141+
## License
142+
143+
MIT

eslint.config.mjs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import eslint from "@eslint/js";
2+
import tseslint from "typescript-eslint";
3+
import eslintConfigPrettier from "eslint-config-prettier";
4+
5+
export default tseslint.config(
6+
eslint.configs.recommended,
7+
...tseslint.configs.strict,
8+
...tseslint.configs.stylistic,
9+
eslintConfigPrettier,
10+
{
11+
files: ["**/*.ts"],
12+
languageOptions: {
13+
parserOptions: {
14+
projectService: true,
15+
tsconfigRootDir: import.meta.dirname,
16+
},
17+
},
18+
},
19+
{
20+
ignores: ["dist/", "node_modules/", "*.config.*", "examples/"],
21+
},
22+
);

examples/bulk-lookup.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/**
2+
* Bulk domain lookup example (Pro and Business plans).
3+
*
4+
* Usage: npx tsx examples/bulk-lookup.ts
5+
*/
6+
7+
import { RdapClient } from "rdapapi";
8+
9+
const client = new RdapClient("your-api-key");
10+
11+
const result = await client.bulkDomains(["google.com", "github.com", "invalid..com"], {
12+
follow: true,
13+
});
14+
15+
console.log(`Total: ${result.summary.total}`);
16+
console.log(`OK: ${result.summary.successful}`);
17+
console.log(`Failed: ${result.summary.failed}`);
18+
19+
for (const r of result.results) {
20+
if (r.status === "success" && r.data) {
21+
console.log(
22+
` ${r.data.domain}: registrar=${r.data.registrar.name}, expires=${r.data.dates.expires}`,
23+
);
24+
} else {
25+
console.log(` ${r.domain}: ${r.error}${r.message}`);
26+
}
27+
}
28+
29+
client.close();

0 commit comments

Comments
 (0)