npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, πŸ‘‹, I’m Ryan HefnerΒ  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you πŸ™

Β© 2026 – Pkg Stats / Ryan Hefner

@podx/api

v2.0.2

Published

🌐 REST API server for PODx - Twitter/X scraping and crypto analysis platform

Readme

@podx/api

Version License TypeScript Hono Bun

The API package provides a high-performance REST API server for the PODx ecosystem, built with Hono and optimized for Bun runtime. It offers endpoints for scraping data, signal analysis, token information, and system health monitoring.

πŸ“¦ Installation

# Install from workspace
bun add @podx/api@workspace:*

# Or install from npm (when published)
bun add @podx/api

πŸ—οΈ Architecture

The API package is built with modern web standards and optimized for performance:

packages/api/src/
β”œβ”€β”€ server/           # Server implementation
β”‚   └── index.ts         # Main server setup with Hono
β”œβ”€β”€ routes/           # API route handlers
β”‚   β”œβ”€β”€ index.ts         # Route setup and configuration
β”‚   β”œβ”€β”€ health.ts        # Health check endpoints
β”‚   β”œβ”€β”€ signals.ts       # Signal analysis endpoints
β”‚   β”œβ”€β”€ tokens.ts        # Token information endpoints
β”‚   └── performance.ts   # Performance monitoring
β”œβ”€β”€ auth/             # Authentication system
β”‚   β”œβ”€β”€ index.ts         # JWT token utilities
β”‚   β”œβ”€β”€ jwt.ts          # JWT implementation
β”‚   └── middleware.ts   # Auth middleware
β”œβ”€β”€ middleware/       # Custom middleware
β”œβ”€β”€ validation/       # Request validation schemas
β”œβ”€β”€ types/            # TypeScript type definitions
└── index.ts          # Main exports

πŸš€ Quick Start

import { PodxAPIServer } from '@podx/api';

// Create and start the server
const server = new PodxAPIServer();
const instance = server.start(3000);

console.log('πŸš€ API server running on http://localhost:3000');

// Generate community tokens
const token = await server.generateCommunityToken(
  'user-123',
  'premium',
  '7d'
);

console.log('Generated token:', token);

// Stop the server
server.stop();

πŸ” Authentication

The API uses JWT-based authentication with tiered access control:

JWT Token Generation

import { generateToken, verifyToken } from '@podx/api/auth';

// Generate a token for a user
const token = generateToken({
  sub: 'user-123',
  tier: 'premium'
}, '7d');

console.log('JWT Token:', token);

// Verify a token
const payload = verifyToken(token);
if (payload) {
  console.log('User ID:', payload.sub);
  console.log('Tier:', payload.tier);
}

Tier-Based Access Control

// Free tier: Limited access
const freeToken = await server.generateCommunityToken('user-123', 'free', '1d');

// Premium tier: Enhanced features
const premiumToken = await server.generateCommunityToken('user-456', 'premium', '7d');

// Admin tier: Full access
const adminToken = await server.generateCommunityToken('admin-789', 'admin', '30d');

Rate Limiting

The API implements tier-based rate limiting:

import { rateLimits } from '@podx/api/auth';

// Rate limit configurations
console.log('Free tier:', rateLimits.free);       // 100 requests/hour
console.log('Premium tier:', rateLimits.premium); // 1000 requests/hour
console.log('Admin tier:', rateLimits.admin);     // 10000 requests/hour

πŸ“‘ API Endpoints

Health & Status

GET /api/v1/health

Get server health status.

Response:

{
  "data": {
    "status": "healthy",
    "timestamp": "2024-01-01T12:00:00.000Z",
    "uptime": 3600,
    "version": "2.0.0"
  },
  "meta": {
    "requestId": "req-123",
    "timestamp": "2024-01-01T12:00:00.000Z"
  }
}

GET /api/v1/ready

Check if the server is ready to handle requests.

Response:

{
  "data": {
    "ready": true
  },
  "meta": {
    "requestId": "req-124",
    "timestamp": "2024-01-01T12:00:00.000Z"
  }
}

Signal Analysis

GET /api/v1/signals/top

Get top trading signals (requires authentication).

Headers:

Authorization: Bearer <jwt-token>

Query Parameters:

  • limit (optional): Number of signals to return (default: 5 for free, 20 for premium)
  • offset (optional): Pagination offset (default: 0)

Response:

{
  "data": {
    "signals": [
      {
        "token": "BTC",
        "type": "BUY",
        "strength": 8,
        "confidence": 85,
        "reason": "Strong bullish sentiment detected",
        "timeframe": "4h",
        "timestamp": "2024-01-01T12:00:00.000Z",
        "source": "PODX AI",
        "riskLevel": "low"
      }
    ],
    "tier": "premium",
    "pagination": {
      "limit": 20,
      "offset": 0,
      "total": 150,
      "hasMore": true
    }
  },
  "meta": {
    "requestId": "req-125",
    "timestamp": "2024-01-01T12:00:00.000Z"
  }
}

GET /api/v1/signals

Get paginated list of signals with filtering.

Query Parameters:

  • limit: Number of signals (1-100, default: 10)
  • offset: Pagination offset (default: 0)
  • token: Filter by token symbol
  • type: Filter by signal type (BUY, SELL, HOLD)
  • minConfidence: Minimum confidence level (0-100)

Token Information

GET /api/v1/tokens/trending

Get trending cryptocurrency tokens.

Query Parameters:

  • limit: Number of tokens (default: 10)
  • timeframe: Time period (1h, 24h, 7d, default: 24h)

Response:

{
  "data": {
    "tokens": [
      {
        "symbol": "BTC",
        "name": "Bitcoin",
        "price": 45000,
        "change24h": 2.5,
        "volume24h": 28000000000,
        "marketCap": 880000000000,
        "mentions": 1250,
        "sentiment": "bullish",
        "lastUpdated": "2024-01-01T12:00:00.000Z"
      }
    ],
    "timeframe": "24h"
  },
  "meta": {
    "requestId": "req-126",
    "timestamp": "2024-01-01T12:00:00.000Z"
  }
}

GET /api/v1/tokens/{symbol}

Get detailed information about a specific token.

Response:

{
  "data": {
    "token": {
      "symbol": "BTC",
      "name": "Bitcoin",
      "contractAddress": "0x...",
      "blockchain": "bitcoin",
      "price": 45000,
      "change24h": 2.5,
      "volume24h": 28000000000,
      "marketCap": 880000000000,
      "supply": {
        "circulating": 19000000,
        "total": 21000000,
        "max": 21000000
      },
      "socialMetrics": {
        "twitterMentions": 1250,
        "redditMentions": 450,
        "sentiment": "bullish"
      },
      "lastUpdated": "2024-01-01T12:00:00.000Z"
    }
  }
}

Performance Monitoring

GET /api/v1/performance/metrics

Get system performance metrics.

Response:

{
  "data": {
    "cpu": {
      "usage": 45.2,
      "cores": 8
    },
    "memory": {
      "used": 1024,
      "total": 8192,
      "percentage": 12.5
    },
    "requests": {
      "total": 15420,
      "perSecond": 12.5,
      "averageResponseTime": 245
    },
    "scraping": {
      "activeJobs": 3,
      "completedToday": 1250,
      "averageProcessingTime": 890
    }
  }
}

πŸ”§ Advanced Usage

Custom Server Configuration

import { createServer } from '@podx/api/server';
import { cors } from 'hono/cors';
import { logger } from 'hono/logger';

// Create custom server with additional middleware
const app = createServer();

// Add custom middleware
app.use('*', cors({
  origin: ['https://podx.dev', 'https://app.podx.dev'],
  allowMethods: ['GET', 'POST', 'PUT', 'DELETE'],
  allowHeaders: ['Content-Type', 'Authorization', 'X-API-Key']
}));

// Add custom routes
app.get('/api/v1/custom', (c) => {
  return c.json({
    message: 'Custom endpoint',
    timestamp: new Date().toISOString()
  });
});

// Start the server
const server = Bun.serve({
  port: 3000,
  fetch: app.fetch
});

Request Validation

import { z } from 'zod';
import { createServer } from '@podx/api/server';

// Custom validation schema
const CreateSignalSchema = z.object({
  token: z.string().min(2).max(10),
  type: z.enum(['BUY', 'SELL', 'HOLD']),
  confidence: z.number().min(0).max(100),
  reason: z.string().min(10).max(500)
});

const app = createServer();

// Route with validation
app.post('/api/v1/signals', async (c) => {
  try {
    const body = await c.req.json();
    const validatedData = CreateSignalSchema.parse(body);

    // Process the validated data
    return c.json({
      success: true,
      signal: {
        id: crypto.randomUUID(),
        ...validatedData,
        createdAt: new Date().toISOString()
      }
    });
  } catch (error) {
    if (error instanceof z.ZodError) {
      return c.json({
        error: 'Validation failed',
        details: error.errors
      }, 400);
    }

    return c.json({
      error: 'Internal server error'
    }, 500);
  }
});

Authentication Middleware

import { verifyToken, extractBearerToken } from '@podx/api/auth';
import { createServer } from '@podx/api/server';

// Create server with authentication
const app = createServer();

// Protected route middleware
const authenticate = async (c: any, next: any) => {
  const authHeader = c.req.header('Authorization');
  const token = extractBearerToken(authHeader);

  if (!token) {
    return c.json({ error: 'No token provided' }, 401);
  }

  const payload = verifyToken(token);
  if (!payload) {
    return c.json({ error: 'Invalid token' }, 401);
  }

  // Add user to context
  c.set('user', payload);
  await next();
};

// Protected route
app.get('/api/v1/user/profile', authenticate, (c) => {
  const user = c.get('user');

  return c.json({
    profile: {
      id: user.sub,
      tier: user.tier,
      // ... other profile data
    }
  });
});

Error Handling

import { createServer } from '@podx/api/server';

// Custom error handler
const app = createServer();

// Override default error handler
app.onError((err, c) => {
  console.error('API Error:', err);

  // Custom error response based on error type
  if (err instanceof ValidationError) {
    return c.json({
      error: {
        type: 'validation_error',
        message: err.message,
        fields: err.fields
      }
    }, 400);
  }

  if (err instanceof AuthenticationError) {
    return c.json({
      error: {
        type: 'authentication_error',
        message: 'Authentication required'
      }
    }, 401);
  }

  // Default error response
  return c.json({
    error: {
      type: 'internal_error',
      message: 'An unexpected error occurred'
    }
  }, 500);
});

// Custom not found handler
app.notFound((c) => {
  return c.json({
    error: {
      type: 'not_found',
      message: 'Endpoint not found'
    }
  }, 404);
});

πŸ”Œ Integration with Scraper

import { ScraperService } from '@podx/scraper';
import { createServer } from '@podx/api/server';

const scraper = new ScraperService();
const app = createServer();

// API endpoint that uses scraper
app.post('/api/v1/scrape', async (c) => {
  try {
    const { username, maxTweets } = await c.req.json();

    const tweets = await scraper.scrapeAccount({
      targetUsername: username,
      maxTweets: maxTweets || 100
    });

    return c.json({
      success: true,
      data: {
        username,
        tweetCount: tweets.length,
        tweets: tweets.slice(0, 10) // Return first 10
      }
    });
  } catch (error) {
    console.error('Scraping error:', error);
    return c.json({
      error: 'Failed to scrape tweets'
    }, 500);
  }
});

// Real-time scraping endpoint
app.get('/api/v1/scrape/stream', (c) => {
  return new Response(
    new ReadableStream({
      start(controller) {
        scraper.scrapeAccount({
          targetUsername: 'cryptowhale',
          maxTweets: 100,
          progressCallback: (progress) => {
            const data = `data: ${JSON.stringify(progress)}\n\n`;
            controller.enqueue(new TextEncoder().encode(data));
          }
        }).then(() => {
          controller.close();
        });
      }
    }),
    {
      headers: {
        'Content-Type': 'text/event-stream',
        'Cache-Control': 'no-cache'
      }
    }
  );
});

πŸ“Š Monitoring and Analytics

Request Logging

import { createServer } from '@podx/api/server';
import { logger } from '@podx/core';

const app = createServer();

// Custom logging middleware
app.use('*', async (c, next) => {
  const start = Date.now();
  const method = c.req.method;
  const url = c.req.url;

  logger.info('Request started', {
    method,
    url,
    userAgent: c.req.header('User-Agent'),
    ip: c.req.header('X-Forwarded-For')
  });

  await next();

  const duration = Date.now() - start;
  const status = c.res.status;

  logger.info('Request completed', {
    method,
    url,
    status,
    duration,
    size: c.res.headers.get('Content-Length')
  });
});

Performance Monitoring

import { createServer } from '@podx/api/server';

const app = createServer();

// Performance monitoring middleware
app.use('*', async (c, next) => {
  const start = performance.now();

  await next();

  const duration = performance.now() - start;

  // Log slow requests
  if (duration > 1000) {
    console.warn('Slow request detected', {
      method: c.req.method,
      url: c.req.url,
      duration: `${duration.toFixed(2)}ms`
    });
  }

  // Add performance header
  c.header('X-Response-Time', `${duration.toFixed(2)}ms`);
});

πŸ§ͺ Testing

Unit Tests

import { describe, test, expect, mock } from 'bun:test';
import { createServer } from '@podx/api/server';

describe('API Server', () => {
  test('should respond to health check', async () => {
    const app = createServer();

    const response = await app.request('/api/v1/health');
    const data = await response.json();

    expect(response.status).toBe(200);
    expect(data.data.status).toBe('healthy');
    expect(data.data).toHaveProperty('timestamp');
  });

  test('should handle authentication', async () => {
    const app = createServer();

    // Mock authentication middleware
    app.use('/api/v1/protected/*', async (c, next) => {
      const auth = c.req.header('Authorization');
      if (!auth?.startsWith('Bearer ')) {
        return c.json({ error: 'Unauthorized' }, 401);
      }
      await next();
    });

    app.get('/api/v1/protected/data', (c) => {
      return c.json({ data: 'secret' });
    });

    // Test without auth
    const response1 = await app.request('/api/v1/protected/data');
    expect(response1.status).toBe(401);

    // Test with auth
    const response2 = await app.request('/api/v1/protected/data', {
      headers: { Authorization: 'Bearer fake-token' }
    });
    expect(response2.status).toBe(200);
  });
});

Integration Tests

import { describe, test, expect } from 'bun:test';
import { PodxAPIServer } from '@podx/api';

describe('API Integration', () => {
  let server: PodxAPIServer;
  let port: number;

  beforeAll(() => {
    server = new PodxAPIServer();
    port = 3001;
    server.start(port);
  });

  afterAll(() => {
    server.stop();
  });

  test('should handle full request flow', async () => {
    // Generate token
    const token = await server.generateCommunityToken('test-user', 'premium', '1h');

    // Make authenticated request
    const response = await fetch(`http://localhost:${port}/api/v1/signals/top`, {
      headers: {
        Authorization: `Bearer ${token}`
      }
    });

    expect(response.ok).toBe(true);
    const data = await response.json();
    expect(data).toHaveProperty('data');
    expect(data).toHaveProperty('meta');
  });
});

πŸ“‹ API Response Format

All API responses follow a consistent format:

Success Response

{
  "data": {
    // Response data
  },
  "meta": {
    "requestId": "req-123",
    "timestamp": "2024-01-01T12:00:00.000Z"
  }
}

Error Response

{
  "error": {
    "type": "validation_error",
    "title": "Invalid Request Parameters",
    "status": 400,
    "detail": "The provided parameters are invalid",
    "instance": "req-123"
  },
  "meta": {
    "requestId": "req-123",
    "timestamp": "2024-01-01T12:00:00.000Z"
  }
}

πŸ”§ Configuration

Environment Variables

# Server Configuration
PORT=3000
HOST=0.0.0.0

# JWT Configuration
JWT_SECRET=your-secret-key-here

# Database Configuration
DATABASE_URL=postgresql://user:pass@localhost:5432/podx

# Scraper Configuration
XSERVE_USERNAME=your_twitter_username
XSERVE_PASSWORD=your_twitter_password
[email protected]

Server Options

import { PodxAPIServer } from '@podx/api';

// Advanced server configuration
const server = new PodxAPIServer();

// Custom server options
const instance = server.start(3000, {
  development: true,
  cors: {
    origin: ['https://podx.dev'],
    credentials: true
  },
  rateLimit: {
    windowMs: 15 * 60 * 1000, // 15 minutes
    max: 100 // limit each IP to 100 requests per windowMs
  }
});

🀝 Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for new functionality
  4. Ensure all tests pass
  5. Submit a pull request

πŸ“ License

This package is licensed under the ISC License. See the LICENSE file for details.

πŸ”— Related Packages

πŸ“ž Support

For support and questions: