Complete Node.js examples for REST API integration
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();
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' });
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}`);
});
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);
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);
});
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');
});
{
"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"
}
}
.env
file:
BYUL_API_KEY=byul_api_key
NODE_ENV=development
PORT=3000
# Install dependencies
npm install
# Set environment variable
export BYUL_API_KEY=byul_api_key
# Run the application
npm start
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');
});
});
npm test