← Back to Documentation

Documentation

TypeScript API SDK Guide

A comprehensive guide to using the AdGuard DNS TypeScript API SDK with Deno 2.0+.

Overview

The TypeScript API SDK provides a complete, type-safe interface to the AdGuard DNS API v1.11 with feature parity to the .NET SDK. Built for Deno 2.0+, it offers high-level repositories, automatic retry policies, pagination support, and seamless integration with the rules compiler.

Features

  • Full API Coverage: Complete implementation of AdGuard DNS API v1.11
  • Type-Safe: Comprehensive TypeScript types generated from OpenAPI spec
  • Repository Pattern: High-level abstractions for common operations
  • Automatic Retry: Built-in exponential backoff for transient failures
  • Pagination Support: Easy-to-use pagination helpers for list operations
  • Error Handling: Robust error handling with custom exception types
  • DateTime Helpers: Utilities for Unix millisecond timestamp conversion
  • Rules Integration: Seamless sync with rules compiler
  • Deno Native: Leverages Deno 2.0+ features and security model

Prerequisites

Requirement Version Installation
Deno 2.0+ deno.land
AdGuard DNS Account - adguard-dns.io
API Key - Generate from account settings

Installation

# Clone the repository
git clone https://github.com/jaypatrick/ad-blocking.git
cd ad-blocking/src/adguard-api-typescript

# Cache dependencies
deno cache src/index.ts

Quick Start

Basic Setup

import { AdGuardDnsClient } from './src/index.ts';

// Create client with API key
const client = AdGuardDnsClient.withApiKey('your-api-key-here');

// Or from environment variable
const client = AdGuardDnsClient.fromEnv('ADGUARD_API_KEY');

// Use the client
const devices = await client.devices.listDevices();
console.log(`You have ${devices.length} devices`);

Using the Builder Pattern

import { AdGuardDnsClientBuilder } from './src/lib/index.ts';

const client = AdGuardDnsClientBuilder.create()
  .withApiKey('your-api-key')
  .withTimeout(60000)  // 60 seconds
  .withConsoleLogging()
  .build();

API Reference

Account API

Manage account information and limits.

// Get account limits
const limits = await client.account.getAccountLimits();
console.log(`Devices: ${limits.devices.used}/${limits.devices.limit}`);
console.log(`DNS Servers: ${limits.dnsServers.used}/${limits.dnsServers.limit}`);
console.log(`User Rules: ${limits.userRules.used}/${limits.userRules.limit}`);

Devices API

Manage devices connected to your DNS profiles.

// List all devices
const devices = await client.devices.listDevices();

// Create a new device
const newDevice = await client.devices.createDevice({
  name: 'My iPhone',
  deviceType: 'IOS',
  dnsServerId: 'dns-server-id'
});

// Get device details
const device = await client.devices.getDevice('device-id');

// Update device
await client.devices.updateDevice('device-id', {
  name: 'My New iPhone 15'
});

// Delete device
await client.devices.deleteDevice('device-id');

DNS Servers API

Manage DNS server profiles (filtering configurations).

// List all DNS servers
const servers = await client.dnsServers.listServers();

// Create DNS server
const newServer = await client.dnsServers.createServer({
  name: 'Family Profile',
  settings: {
    protectionEnabled: true,
    blockAdultWebsitesEnabled: true,
    safeSearchEnabled: true
  }
});

// Update DNS server
await client.dnsServers.updateServer('server-id', {
  settings: {
    protectionEnabled: false
  }
});

// Delete DNS server
await client.dnsServers.deleteServer('server-id');

Query Log API

View and manage DNS query logs.

import { daysAgo, toUnixMilliseconds } from './src/helpers/datetime.ts';

// Get query log for last 24 hours
const dayAgo = daysAgo(1);
const now = toUnixMilliseconds(new Date());

const queryLog = await client.queryLog.getQueryLog({
  timeFromMillis: dayAgo,
  timeToMillis: now,
  limit: 100
});

// Process results
for (const item of queryLog.items) {
  console.log(`${item.domain} - ${item.filteringStatus}`);
}

// Clear query log
await client.queryLog.clearQueryLog();

Statistics API

Retrieve DNS query statistics.

import { daysAgo, toUnixMilliseconds } from './src/helpers/datetime.ts';

// Get time-based statistics for last 7 days
const weekAgo = daysAgo(7);
const now = toUnixMilliseconds(new Date());

const timeStats = await client.statistics.getTimeQueriesStats({
  timeFromMillis: weekAgo,
  timeToMillis: now
});

// Calculate totals
let totalQueries = 0;
let totalBlocked = 0;

for (const stat of timeStats.stats) {
  totalQueries += stat.value.queries;
  totalBlocked += stat.value.blocked;
}

console.log(`Total Queries: ${totalQueries}`);
console.log(`Total Blocked: ${totalBlocked}`);
console.log(`Block Rate: ${(totalBlocked / totalQueries * 100).toFixed(2)}%`);

// Get device statistics
const deviceStats = await client.statistics.getDevicesQueriesStats({
  timeFromMillis: weekAgo,
  timeToMillis: now
});

// Get company statistics
const companyStats = await client.statistics.getCompaniesStats({
  timeFromMillis: weekAgo,
  timeToMillis: now
});

Filter Lists API

Get available filter lists.

// List all available filter lists
const filterLists = await client.filterLists.listFilterLists();

for (const filter of filterLists) {
  console.log(`${filter.name}: ${filter.description}`);
}

Web Services API

Manage blockable web services.

// List all web services
const webServices = await client.webServices.listWebServices();

for (const service of webServices) {
  console.log(`${service.name} (${service.id})`);
}

Dedicated IP API

Manage dedicated IPv4 addresses.

// List dedicated IPs
const ips = await client.dedicatedIps.listDedicatedIPv4Addresses();

// Allocate a new dedicated IP
const newIp = await client.dedicatedIps.allocateDedicatedIPv4Address();
console.log(`Allocated: ${newIp.ip}`);

// Link dedicated IP to device
await client.dedicatedIps.linkDedicatedIPv4ToDevice('ip-id', {
  deviceId: 'device-id'
});

Authentication API

Generate OAuth tokens (alternative to API key authentication).

// Get access token
const tokenResponse = await client.auth.getAccessToken({
  username: 'your-email@example.com',
  password: 'your-password',
  mfaToken: '123456'  // Optional, if 2FA enabled
});

// Use the token
const clientWithToken = AdGuardDnsClient.withBearerToken(tokenResponse.accessToken);

// Refresh token
const newToken = await client.auth.getAccessToken({
  refreshToken: tokenResponse.refreshToken
});

High-Level Repositories

Repositories provide convenient, high-level operations built on top of the base API.

Device Repository

// Get device by ID
const device = await client.deviceRepository.getById('device-id');

// Get all devices
const allDevices = await client.deviceRepository.getAll();

// Find devices by name
const iphones = allDevices.filter(d => d.name.includes('iPhone'));

// Find devices by type
const iosDevices = await client.deviceRepository.findByType('IOS');

// Get devices for a specific DNS server
const serverDevices = await client.deviceRepository.getByServerId('server-id');

DNS Server Repository

// Get default server
const defaultServer = await client.dnsServerRepository.getDefault();

// Find server by name
const familyServer = await client.dnsServerRepository.findByName('Family Profile');

// Get all servers with device counts
const servers = await client.dnsServerRepository.getAllWithDevices();

Query Log Repository

// Get queries for last N hours
const recentQueries = await client.queryLogRepository.getRecentQueries(24);

// Get blocked queries only
const blocked = await client.queryLogRepository.getBlockedQueries({
  hoursAgo: 24
});

// Get queries for specific domain
const domainQueries = await client.queryLogRepository.getQueriesForDomain(
  'example.com',
  { hoursAgo: 7 * 24 }  // Last 7 days
);

Statistics Repository

// Get summary statistics
const summary = await client.statisticsRepository.getSummary({
  hoursAgo: 24
});

console.log(`Queries: ${summary.totalQueries}`);
console.log(`Blocked: ${summary.totalBlocked}`);
console.log(`Block Rate: ${summary.blockRate}%`);

// Get statistics for specific time range
const stats = await client.statisticsRepository.getStatisticsForRange({
  startDate: new Date('2024-01-01'),
  endDate: new Date('2024-01-31')
});

User Rules Repository

// Sync rules from file
const result = await client.userRulesRepository.syncFromFile(
  'dns-server-id',
  'data/output/adguard_user_filter.txt'
);

console.log(`Added: ${result.added}`);
console.log(`Removed: ${result.removed}`);
console.log(`Unchanged: ${result.unchanged}`);

// Get current rules
const rules = await client.userRulesRepository.getRules('dns-server-id');

// Clear all rules
await client.userRulesRepository.clearRules('dns-server-id');

Pagination

The SDK provides powerful pagination utilities.

Using createPagedList

import { createPagedList } from './src/lib/index.ts';

// Get all devices as paged list
const pagedDevices = createPagedList(() => client.deviceRepository.getAll());

// Filter and take first 10
const activeDevices = await pagedDevices
  .filter(d => d.settings?.protectionEnabled)
  .take(10)
  .toArray();

// Map to simple list
const deviceNames = await pagedDevices
  .map(d => d.name)
  .toArray();

Manual Pagination

// Create paginated result
const devices = await client.devices.listDevices();
const pageResult = createPageResult(devices, { pageSize: 10 });

console.log(`Page 1 of ${pageResult.totalPages}`);
console.log(`Items: ${pageResult.items.length}`);
console.log(`Has More: ${pageResult.hasMore}`);

DateTime Helpers

Work with Unix millisecond timestamps easily.

import {
  toUnixMilliseconds,
  fromUnixMilliseconds,
  daysAgo,
  hoursAgo,
  minutesAgo,
  startOfToday,
  endOfToday,
  formatRelative
} from './src/helpers/datetime.ts';

// Convert to Unix milliseconds
const now = toUnixMilliseconds(new Date());
const specificDate = toUnixMilliseconds(new Date('2024-01-01'));

// Convert from Unix milliseconds
const date = fromUnixMilliseconds(1704067200000);

// Relative times
const yesterday = daysAgo(1);
const sixHoursAgo = hoursAgo(6);
const thirtyMinsAgo = minutesAgo(30);

// Today's range
const todayStart = startOfToday();
const todayEnd = endOfToday();

// Format for display
const relative = formatRelative(hoursAgo(2));  // "2 hours ago"

Retry Policies

Automatic retry with exponential backoff for transient failures.

Using Built-in Retry

import { executeWithRetry } from './src/helpers/retry.ts';

// Execute with automatic retry
const devices = await executeWithRetry(
  () => client.devices.listDevices(),
  { maxRetries: 3 }
);

Custom Retry Policy

import { RetryPolicy, createRateLimitRetryPolicy } from './src/helpers/retry.ts';

// Create custom retry policy
const customPolicy = new RetryPolicy({
  maxRetries: 5,
  baseDelay: 2000,  // 2 seconds
  maxDelay: 30000,  // 30 seconds
  retryableStatusCodes: [408, 429, 500, 502, 503, 504]
});

// Use with any operation
const result = await customPolicy.execute(() => 
  client.devices.createDevice(newDevice)
);

// Rate limit specific policy
const rateLimitPolicy = createRateLimitRetryPolicy({
  maxRetries: 5,
  baseDelay: 5000  // 5 seconds
});

Error Handling

The SDK provides typed error classes.

import { ApiError, RateLimitError, AuthenticationError } from './src/errors/index.ts';

try {
  const devices = await client.devices.listDevices();
} catch (error) {
  if (error instanceof AuthenticationError) {
    console.error('Invalid API key');
  } else if (error instanceof RateLimitError) {
    console.error(`Rate limited. Retry after: ${error.retryAfter}s`);
  } else if (error instanceof ApiError) {
    console.error(`API Error (${error.statusCode}): ${error.message}`);
  } else {
    console.error('Unknown error:', error);
  }
}

Rules Compiler Integration

Seamlessly integrate with the rules compiler.

import { RulesCompilerIntegration } from './src/rules-compiler-integration.ts';

const integration = new RulesCompilerIntegration(client);

// Sync rules from compiled output
const result = await integration.syncRulesToServer('dns-server-id', {
  rulesFile: 'data/output/adguard_user_filter.txt',
  clearExisting: true,
  dryRun: false
});

console.log(`Sync Complete:`);
console.log(`- Added: ${result.added}`);
console.log(`- Removed: ${result.removed}`);
console.log(`- Unchanged: ${result.unchanged}`);
console.log(`- Errors: ${result.errors.length}`);

// Compile and sync in one step
const compileResult = await integration.compileAndSync('dns-server-id', {
  configFile: 'compiler-config.yaml',
  clearExisting: true
});

Configuration

Configuration Builder

import { ConfigurationHelper } from './src/helpers/configuration.ts';

// Create with API key
const config = ConfigurationHelper.createWithApiKey('your-api-key');

// Create with bearer token
const config = ConfigurationHelper.createWithBearerToken('your-token');

// Custom configuration
const config = ConfigurationHelper.createCustom()
  .withApiKey('your-api-key')
  .withBaseUrl('https://api.adguard-dns.io')
  .withTimeout(60000)
  .withUserAgent('MyApp/1.0');

Environment Variables

# Set API key
export ADGUARD_API_KEY="your-api-key-here"

# Set base URL (optional)
export ADGUARD_API_URL="https://api.adguard-dns.io"

Testing

Run Tests

# Run all tests
deno task test

# Run specific test file
deno test tests/client.test.ts

# Run with coverage
deno task test:coverage

# Watch mode
deno task dev

Writing Tests

import { assertEquals } from 'https://deno.land/std@0.203.0/assert/mod.ts';
import { AdGuardDnsClient } from './src/index.ts';

Deno.test('should list devices', async () => {
  const client = AdGuardDnsClient.withApiKey('test-key');
  
  // Your test logic
  const devices = await client.devices.listDevices();
  
  assertEquals(Array.isArray(devices), true);
});

Best Practices

1. Use Environment Variables for API Keys

// Bad: Hardcoded key
const client = AdGuardDnsClient.withApiKey('sk_test_123456');

// Good: From environment
const client = AdGuardDnsClient.fromEnv('ADGUARD_API_KEY');

2. Use Repositories for Complex Operations

// Bad: Multiple low-level API calls
const devices = await client.devices.listDevices();
const myDevice = devices.find(d => d.id === 'device-id');

// Good: Use repository
const device = await client.deviceRepository.getById('device-id');

3. Always Handle Errors

// Bad: No error handling
const devices = await client.devices.listDevices();

// Good: With error handling
try {
  const devices = await client.devices.listDevices();
} catch (error) {
  if (error instanceof RateLimitError) {
    // Wait and retry
  } else {
    // Handle other errors
  }
}

4. Use DateTime Helpers

// Bad: Manual timestamp calculation
const now = Date.now();
const yesterday = now - (24 * 60 * 60 * 1000);

// Good: Use helpers
const yesterday = daysAgo(1);
const now = toUnixMilliseconds(new Date());

5. Leverage Pagination

// Bad: Loading all data at once
const devices = await client.devices.listDevices();
const first10 = devices.slice(0, 10);

// Good: Using pagination
const devices = await createPagedList(() => client.deviceRepository.getAll())
  .take(10)
  .toArray();

Interactive CLI

The SDK includes an interactive CLI for testing and exploration.

# Start interactive mode
deno task start

# With API key argument
deno task start -- --api-key your-key

Features:

  • Account information
  • Device management
  • DNS server management
  • Query log viewing
  • Statistics reports
  • User rules management

Performance Tips

  1. Use Repositories: Higher-level abstractions with optimizations
  2. Enable Retry Policies: Automatic handling of transient failures
  3. Batch Operations: Group multiple API calls when possible
  4. Cache Results: Store frequently-accessed data locally
  5. Use Pagination: Don't load all data at once for large datasets

Troubleshooting

API Key Issues

# Test API key
export ADGUARD_API_KEY="your-key"
deno task start

# Or test directly
deno run --allow-net --allow-env tests/client.test.ts

Rate Limiting

If you're being rate limited, implement retry delays:

const policy = createRateLimitRetryPolicy({ maxRetries: 5, baseDelay: 5000 });
const result = await policy.execute(() => client.devices.listDevices());

Network Errors

Ensure network access is allowed:

# Grant network permission
deno run --allow-net src/index.ts

Examples

See complete examples in:

Related Documentation

License

GPLv3 - See LICENSE for details.