Basic Usage

Simple News Fetch

const fetch = require('node-fetch');

const getNews = async () => {
  const response = await fetch('https://api.byul.ai/api/v2/news?limit=10&minImportance=7', {
    headers: {
      'X-API-Key': process.env.BYUL_API_KEY
    }
  });
  
  if (!response.ok) {
    throw new Error(`HTTP error! status: ${response.status}`);
  }
  
  const data = await response.json();
  
  data.items.forEach(article => {
    console.log(`${article.title} - Importance: ${article.importanceScore}/10`);
  });
  
  return data;
};

getNews();

With Error Handling

const fetch = require('node-fetch');

const safeGetNews = async (filters = {}) => {
  try {
    const params = new URLSearchParams({
      limit: '20',
      minImportance: '6',
      ...filters
    });
    
    const response = await fetch(`https://api.byul.ai/api/v2/news?${params}`, {
      headers: {
        'X-API-Key': process.env.BYUL_API_KEY
      }
    });
    
    if (!response.ok) {
      const errorData = await response.json();
      console.error('API Error:', errorData.message);
      return null;
    }
    
    return await response.json();
  } catch (error) {
    console.error('Request failed:', error.message);
    return null;
  }
};

// Usage
safeGetNews({ symbol: 'AAPL' });

Express.js Integration

News API Proxy

const express = require('express');
const fetch = require('node-fetch');
const rateLimit = require('express-rate-limit');

const app = express();
const PORT = process.env.PORT || 3000;

// Rate limiting
const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100 // limit each IP to 100 requests per windowMs
});

app.use(limiter);
app.use(express.json());

// News endpoint
app.get('/api/news', async (req, res) => {
  try {
    const { limit = 10, minImportance = 1, symbol, q } = req.query;
    
    // Validate parameters
    if (limit < 1 || limit > 100) {
      return res.status(400).json({ error: 'limit must be between 1 and 100' });
    }
    
    const params = new URLSearchParams({ limit, minImportance });
    if (symbol) params.append('symbol', symbol);
    if (q) params.append('q', q);
    
    const response = await fetch(`https://api.byul.ai/api/v2/news?${params}`, {
      headers: { 'X-API-Key': process.env.BYUL_API_KEY }
    });
    
    if (!response.ok) {
      const errorData = await response.json();
      return res.status(response.status).json(errorData);
    }
    
    const data = await response.json();
    res.json(data);
    
  } catch (error) {
    console.error('News API error:', error);
    res.status(500).json({ error: 'Internal server error' });
  }
});


app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

Advanced Examples

News Aggregator Class

const fetch = require('node-fetch');

class ByulNewsClient {
  constructor(apiKey) {
    this.apiKey = apiKey;
    this.baseUrl = 'https://api.byul.ai/api/v2';
    this.cache = new Map();
  }
  
  async makeRequest(endpoint, params = {}) {
    const url = new URL(`${this.baseUrl}${endpoint}`);
    Object.entries(params).forEach(([key, value]) => {
      if (value !== undefined) {
        url.searchParams.append(key, value.toString());
      }
    });
    
    const response = await fetch(url, {
      headers: { 'X-API-Key': this.apiKey }
    });
    
    if (!response.ok) {
      const errorData = await response.json();
      throw new Error(`API Error: ${errorData.message}`);
    }
    
    return response.json();
  }
  
  async getNews(filters = {}) {
    return this.makeRequest('/news', filters);
  }
  
  
  async getAllNews(filters = {}) {
    let allNews = [];
    let cursor = null;
    let hasMore = true;
    
    while (hasMore) {
      const data = await this.getNews({ 
        ...filters, 
        cursor,
        limit: 100 
      });
      
      allNews.push(...data.items);
      cursor = data.nextCursor;
      hasMore = data.hasMore;
      
      // Prevent infinite loops
      if (allNews.length > 10000) break;
    }
    
    return allNews;
  }
  
  async getPortfolioNews(symbols, minImportance = 6) {
    const promises = symbols.map(symbol => 
      this.getNews({ symbol, minImportance, limit: 50 })
    );
    
    const results = await Promise.all(promises);
    
    // Combine and deduplicate
    const newsMap = new Map();
    results.forEach(data => {
      data.items.forEach(article => {
        newsMap.set(article._id, article);
      });
    });
    
    return Array.from(newsMap.values())
      .sort((a, b) => b.importanceScore - a.importanceScore);
  }
}

// Usage
const client = new ByulNewsClient(process.env.BYUL_API_KEY);

// Get breaking news
client.getNews({ minImportance: 9 })
  .then(data => console.log('Breaking news:', data.items.length))
  .catch(console.error);

// Get portfolio news
client.getPortfolioNews(['AAPL', 'GOOGL', 'MSFT'])
  .then(news => console.log('Portfolio news:', news.length))
  .catch(console.error);

Real-time News Monitor

const EventEmitter = require('events');

class NewsMonitor extends EventEmitter {
  constructor(apiKey, pollInterval = 30000) {
    super();
    this.apiKey = apiKey;
    this.pollInterval = pollInterval;
    this.lastArticleId = null;
    this.isRunning = false;
  }
  
  async checkForNewNews() {
    try {
      const params = { limit: 50, minImportance: 6 };
      if (this.lastArticleId) {
        params.sinceId = this.lastArticleId;
      }
      
      const response = await fetch(`https://api.byul.ai/api/v2/news?${new URLSearchParams(params)}`, {
        headers: { 'X-API-Key': this.apiKey }
      });
      
      if (!response.ok) {
        this.emit('error', new Error(`HTTP ${response.status}`));
        return;
      }
      
      const data = await response.json();
      
      if (data.items.length > 0) {
        this.lastArticleId = data.items[0]._id;
        this.emit('news', data.items);
        
        // Emit breaking news separately
        const breakingNews = data.items.filter(article => article.importanceScore >= 9);
        if (breakingNews.length > 0) {
          this.emit('breaking', breakingNews);
        }
      }
      
    } catch (error) {
      this.emit('error', error);
    }
  }
  
  start() {
    if (this.isRunning) return;
    
    this.isRunning = true;
    this.intervalId = setInterval(() => {
      this.checkForNewNews();
    }, this.pollInterval);
    
    // Check immediately
    this.checkForNewNews();
    
    this.emit('started');
  }
  
  stop() {
    if (this.intervalId) {
      clearInterval(this.intervalId);
      this.intervalId = null;
    }
    this.isRunning = false;
    this.emit('stopped');
  }
}

// Usage
const monitor = new NewsMonitor(process.env.BYUL_API_KEY, 30000);

monitor.on('started', () => {
  console.log('News monitoring started');
});

monitor.on('news', (articles) => {
  console.log(`📰 Found ${articles.length} new articles`);
  articles.forEach(article => {
    console.log(`  ${article.title} (${article.importanceScore}/10)`);
  });
});

monitor.on('breaking', (articles) => {
  console.log('BREAKING NEWS:');
  articles.forEach(article => {
    console.log(`  ${article.title}`);
  });
});

monitor.on('error', (error) => {
  console.error('Monitor error:', error.message);
});

monitor.start();

// Graceful shutdown
process.on('SIGINT', () => {
  console.log('\n🛑 Shutting down news monitor...');
  monitor.stop();
  process.exit(0);
});

Webhook Server

const express = require('express');
const crypto = require('crypto');

const app = express();
app.use(express.json());

// News data storage (in production, use a database)
let newsCache = [];

// Simulate webhook endpoint (Byul doesn't have webhooks yet, but this shows the pattern)
app.post('/webhook/news', (req, res) => {
  try {
    // In a real webhook, you'd verify the signature
    const { articles } = req.body;
    
    // Process new articles
    articles.forEach(article => {
      // Add to cache
      newsCache.unshift(article);
      
      // Keep only latest 1000 articles
      newsCache = newsCache.slice(0, 1000);
      
      // Log high-importance news
      if (article.importanceScore >= 8) {
        console.log(`High impact: ${article.title}`);
      }
    });
    
    res.json({ received: articles.length });
  } catch (error) {
    console.error('Webhook error:', error);
    res.status(500).json({ error: 'Webhook processing failed' });
  }
});

// Serve cached news
app.get('/api/cached-news', (req, res) => {
  const { limit = 20, minImportance } = req.query;
  
  let filtered = newsCache;
  
  if (minImportance) {
    filtered = filtered.filter(article => 
      article.importanceScore >= parseInt(minImportance)
    );
  }
  
  res.json({
    items: filtered.slice(0, parseInt(limit)),
    total: filtered.length,
    cached: true
  });
});

app.listen(3000, () => {
  console.log('Webhook server listening on port 3000');
});

Environment Setup

package.json

{
  "name": "byul-news-client",
  "version": "1.0.0",
  "description": "Byul AI News API client",
  "main": "index.js",
  "scripts": {
    "start": "node index.js",
    "dev": "nodemon index.js",
    "test": "jest"
  },
  "dependencies": {
    "node-fetch": "^2.6.7",
    "express": "^4.18.0",
    "express-rate-limit": "^6.7.0"
  },
  "devDependencies": {
    "nodemon": "^2.0.20",
    "jest": "^29.0.0"
  }
}

Environment Variables

Create .env file:
BYUL_API_KEY=byul_api_key
NODE_ENV=development
PORT=3000

Installation and Run

# Install dependencies
npm install

# Set environment variable
export BYUL_API_KEY=byul_api_key

# Run the application
npm start

Testing

Unit Tests

const { describe, it, expect, beforeEach, jest } = require('@jest/globals');
const ByulNewsClient = require('./byul-client');

// Mock fetch
global.fetch = jest.fn();

describe('ByulNewsClient', () => {
  let client;
  
  beforeEach(() => {
    client = new ByulNewsClient('test_api_key');
    fetch.mockClear();
  });
  
  it('should fetch news successfully', async () => {
    const mockResponse = {
      items: [
        {
          _id: '123',
          title: 'Test News',
          importanceScore: 8
        }
      ],
      hasMore: false
    };
    
    fetch.mockResolvedValueOnce({
      ok: true,
      json: async () => mockResponse
    });
    
    const result = await client.getNews({ limit: 10 });
    
    expect(result).toEqual(mockResponse);
    expect(fetch).toHaveBeenCalledWith(
      expect.stringContaining('/news?limit=10'),
      expect.objectContaining({
        headers: { 'X-API-Key': 'test_api_key' }
      })
    );
  });
  
  it('should handle API errors', async () => {
    fetch.mockResolvedValueOnce({
      ok: false,
      status: 401,
      json: async () => ({ message: 'Unauthorized' })
    });
    
    await expect(client.getNews()).rejects.toThrow('API Error: Unauthorized');
  });
});
Run tests:
npm test