Code Examples
Practical examples showing how to use the DDG Web Search package in different scenarios.
Basic Usage
Simple example showing how to search the web and fetch web content.
basic-usage.ts
import { WebSearcher, WebContentFetcher } from "@lucid-spark/ddg-web-search";
async function main() {
const searcher = new WebSearcher();
const fetcher = new WebContentFetcher();
try {
// Search the web using DuckDuckGo with browser automation
console.log("š Searching for TypeScript tutorials...");
const results = await searcher.search("TypeScript tutorials");
console.log(`ā
Found ${results.length} results:`);
results.forEach((result, index) => {
console.log(`${index + 1}. ${result.title}`);
console.log(` URL: ${result.url}`);
console.log(` Snippet: ${result.snippet}`);
if (result.icon) {
console.log(` Icon: ${result.icon}`);
}
console.log();
});
// Fetch content from the first result
if (results.length > 0) {
console.log("š Fetching content from first result...");
const fetchResult = await fetcher.fetch(results[0].url);
if (fetchResult.success && fetchResult.data?.content) {
console.log(`ā
Content fetched successfully!`);
console.log(`š Content length: ${fetchResult.data.content.length} characters`);
console.log(`š Title: ${fetchResult.data.metadata?.title || 'N/A'}`);
console.log(`š Preview: ${fetchResult.data.content.substring(0, 200)}...`);
} else {
console.log(`ā Failed to fetch content: ${fetchResult.error}`);
}
}
} catch (error) {
console.error("ā Error:", error);
} finally {
// Important: cleanup browser resources
await searcher.close();
}
}
main();
Command Line Interface
Examples of using the built-in CLI for quick searches and content fetching.
Terminal Commands
# Install globally for CLI access
npm install -g @lucid-spark/ddg-web-search
# Search the web with browser automation
ddg-web-search search "Node.js best practices"
# Fetch content from a URL with intelligent parsing
ddg-web-search fetch https://nodejs.org/en/docs/
# Start interactive mode
ddg-web-search interactive
# Start MCP server (stdio transport)
ddg-web-search mcp
# Start MCP server (HTTP transport)
ddg-web-search mcp-http
# In interactive mode:
š¦ ddg> search "React hooks tutorial"
š¦ ddg> fetch https://reactjs.org/docs/hooks-intro.html
š¦ ddg> mcp
š¦ ddg> help
š¦ ddg> exit
MCP Server Integration
Setting up the Model Context Protocol server for AI assistant integration.
mcp-setup.json
{
"mcpServers": {
"ddg-web-search": {
"command": "@lucid-spark/ddg-web-search-mcp",
"args": [],
"env": {}
}
}
}
Terminal - MCP Server
# Start MCP server with stdio transport (default)
ddg-web-search-mcp
# Start with HTTP transport
node dist/mcp.js --transport http --port 3001
# Start with custom host and port
node dist/mcp.js --transport http --host 0.0.0.0 --port 8080
# Test HTTP transport with curl
curl http://localhost:3001/
# Available MCP tools:
# - search: Search the web for results
# - fetch_web_content: Fetch content from a web URL
Advanced Configuration
Advanced usage with custom rate limiting and error handling.
advanced-usage.ts
import {
WebSearcher,
WebContentFetcher,
RateLimiter,
HttpClient
} from "@lucid-spark/ddg-web-search";
async function advancedExample() {
// Custom rate limiting: 2 requests per 3 seconds
const customFetcher = new WebContentFetcher(2, 3000);
const searcher = new WebSearcher();
try {
// Batch search multiple queries
const queries = [
"TypeScript best practices",
"Node.js performance optimization",
"React testing strategies"
];
console.log("š Performing batch searches...");
for (const query of queries) {
try {
console.log(`\nš Searching: "${query}"`);
const results = await searcher.search(query);
console.log(` ā
Found ${results.length} results`);
// Fetch content from first result if available
if (results.length > 0) {
const fetchResult = await customFetcher.fetch(results[0].url);
if (fetchResult.success && fetchResult.data?.content) {
console.log(` š Content length: ${fetchResult.data.content.length} chars`);
console.log(` š Title: ${fetchResult.data.metadata?.title || 'N/A'}`);
} else {
console.log(` ā Fetch failed: ${fetchResult.error}`);
}
}
} catch (error) {
console.error(` ā Error searching "${query}":`, error);
}
}
} finally {
// Essential cleanup
await searcher.close();
}
}
// Error handling example
async function errorHandlingExample() {
const fetcher = new WebContentFetcher();
const urls = [
"https://httpbin.org/status/200", // Should succeed
"https://httpbin.org/status/404", // Should fail
"invalid-url", // Invalid URL
"https://httpbin.org/delay/5" // Might timeout
];
for (const url of urls) {
try {
console.log(`\nš Fetching: ${url}`);
const result = await fetcher.fetch(url);
if (result.success && result.data?.content) {
console.log(` ā
Success: ${result.data.content.length} characters`);
} else {
console.log(` ā Failed: ${result.error}`);
}
} catch (error) {
console.log(` š„ Exception: ${error}`);
}
}
}
// Run examples
advancedExample().then(() => {
console.log("\n" + "=".repeat(50));
return errorHandlingExample();
}).catch(console.error);
Express.js API Integration
Building a REST API with Express.js using the DDG Web Search package.
express-api.js
const express = require('express');
const { WebSearcher, WebContentFetcher } = require('@lucid-spark/ddg-web-search');
const app = express();
const port = 3000;
// Initialize services
const searcher = new WebSearcher();
const fetcher = new WebContentFetcher();
app.use(express.json());
// Search endpoint
app.get('/api/search', async (req, res) => {
try {
const { q } = req.query;
if (!q) {
return res.status(400).json({
error: 'Query parameter "q" is required'
});
}
const results = await searcher.search(q);
res.json({
query: q,
results: results,
count: results.length,
timestamp: new Date().toISOString()
});
} catch (error) {
res.status(500).json({
error: 'Search failed',
message: error.message
});
}
});
// Fetch content endpoint
app.post('/api/fetch', async (req, res) => {
try {
const { url } = req.body;
if (!url) {
return res.status(400).json({
error: 'URL is required in request body'
});
}
const result = await fetcher.fetch(url);
if (result.success && result.data?.content) {
res.json({
url: url,
content: result.data.content,
length: result.data.content.length,
metadata: result.data.metadata,
timestamp: new Date().toISOString()
});
} else {
res.status(400).json({
url: url,
error: result.error,
timestamp: new Date().toISOString()
});
}
} catch (error) {
res.status(500).json({
error: 'Fetch failed',
message: error.message
});
}
});
// Health check
app.get('/health', (req, res) => {
res.json({
status: 'healthy',
timestamp: new Date().toISOString()
});
});
app.listen(port, () => {
console.log(`š DDG Web Search API listening at http://localhost:${port}`);
console.log(`š Endpoints:`);
console.log(` GET /api/search?q=`);
console.log(` POST /api/fetch (body: {"url": "..."})`);
console.log(` GET /health`);
});
TypeScript Types & Interfaces
Working with TypeScript types for better development experience.
types-example.ts
import {
SearchResult,
FetchResult,
WebContent,
WebSearcher,
WebContentFetcher
} from "@lucid-spark/ddg-web-search";
// Type-safe search function
async function searchWithTypes(query: string): Promise {
const searcher = new WebSearcher();
return await searcher.search(query);
}
// Type-safe fetch function
async function fetchWithTypes(url: string): Promise {
const fetcher = new WebContentFetcher();
return await fetcher.fetch(url);
}
// Custom interface extending the base types
interface SearchWithMetadata extends SearchResult {
searchTimestamp: Date;
relevanceScore?: number;
}
// Custom service class with proper typing
class SearchService {
private searcher: WebSearcher;
private fetcher: WebContentFetcher;
constructor() {
this.searcher = new WebSearcher();
this.fetcher = new WebContentFetcher(1, 2000); // 1 req per 2 seconds
}
async searchWithMetadata(query: string): Promise {
const results = await this.searcher.search(query);
return results.map((result, index) => ({
...result,
searchTimestamp: new Date(),
relevanceScore: Math.max(0, 1 - (index * 0.1)) // Simple relevance scoring
}));
}
async fetchContent(url: string): Promise {
const result = await this.fetcher.fetch(url);
if (result.success && result.data?.content) {
return {
content: result.data.content,
metadata: {
title: this.extractTitle(result.data.content),
description: this.extractDescription(result.data.content),
url: url
}
};
}
return null;
}
private extractTitle(content: string): string {
const titleMatch = content.match(/]*>([^<]+)<\/title>/i);
return titleMatch ? titleMatch[1].trim() : 'No title found';
}
private extractDescription(content: string): string {
const descMatch = content.match(/]*name="description"[^>]*content="([^"]+)"/i);
return descMatch ? descMatch[1].trim() : content.substring(0, 160) + '...';
}
}
// Usage example
async function main() {
const service = new SearchService();
const results: SearchWithMetadata[] = await service.searchWithMetadata("TypeScript");
for (const result of results.slice(0, 3)) {
console.log(`š ${result.title}`);
console.log(`š ${result.url}`);
console.log(`ā Relevance: ${result.relevanceScore?.toFixed(2)}`);
console.log(`š Searched: ${result.searchTimestamp.toISOString()}`);
const content = await service.fetchContent(result.url);
if (content) {
console.log(`š ${content.metadata?.description}`);
}
console.log('---');
}
}
main().catch(console.error);
Testing
Unit testing examples using Jest with mocked dependencies.
search.test.ts
import { WebSearcher } from '@lucid-spark/ddg-web-search';
// Note: This is a mock example - the actual WebSearcher uses Puppeteer for browser automation
describe('WebSearcher', () => {
let searcher: WebSearcher;
beforeEach(() => {
searcher = new WebSearcher();
});
afterEach(async () => {
// Important: cleanup browser resources
await searcher.close();
});
it('should return search results for valid query', async () => {
const results = await searcher.search('TypeScript');
expect(Array.isArray(results)).toBe(true);
expect(results.length).toBeGreaterThan(0);
if (results.length > 0) {
expect(results[0]).toHaveProperty('title');
expect(results[0]).toHaveProperty('url');
expect(results[0]).toHaveProperty('snippet');
expect(typeof results[0].title).toBe('string');
expect(typeof results[0].url).toBe('string');
expect(typeof results[0].snippet).toBe('string');
}
});
it('should return empty array for empty query', async () => {
const results = await searcher.search('');
expect(results).toEqual([]);
});
it('should handle search errors gracefully', async () => {
// Test with very long query that might cause issues
const veryLongQuery = 'a'.repeat(1000);
const results = await searcher.search(veryLongQuery);
expect(Array.isArray(results)).toBe(true);
});
});