New:Introducing Socket Scanning for VS Code Marketplace Extensions.Learn more →
Get Started

convex-youtube-cache

Package Overview
Dependencies
Maintainers
1
Versions
2
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

convex-youtube-cache

Convex component for YouTube video metadata caching with built-in rate limiting, TTL, reservation system, and batch lookups

latest
Source
npmnpm
Version
1.0.1
Version published
Weekly downloads
0
Maintainers
1
Weekly downloads
 
Created
Source

convex-youtube-metadata

npm license TypeScript

A Convex component for YouTube video metadata caching with built-in rate limiting, TTL management, a reservation system, and batch lookups.

Built for apps that need fast, reliable access to YouTube video data without exceeding API quotas.

Convex Components Challenge -- YouTube Metadata Cache with Rate Limiting

Features

  • Smart Caching -- Cache video metadata (title, description, thumbnail, duration, channel info) with configurable TTL
  • Rate Limiting -- Built-in rate limiter to stay within YouTube API quotas (default: 10,000 units/day)
  • Reservation System -- Queue requests when at capacity, wait up to configurable timeout (default 10s) before failing
  • Stale-While-Revalidate -- Return cached data immediately when available, fetch fresh data when stale or missing
  • Batch Lookups -- Fetch multiple video IDs in a single API call for efficient quota usage
  • Cache Statistics -- Monitor hit rate, quota usage, and active reservations
  • Retry with Backoff -- Configurable retry logic with exponential backoff on transient failures
  • Zero Environment Variables -- YouTube API key passed as configuration (components cannot use env vars)

Installation

npm install convex-youtube-metadata

Setup

1. Register the component

// convex/convex.config.ts
import { defineApp } from "convex/server";
import youtubeMetadata from "convex-youtube-metadata/convex.config";

const app = defineApp();
app.use(youtubeMetadata);

export default app;

2. Initialize the client

// convex/youtube.ts
import { YouTubeMetadataCache } from "convex-youtube-metadata";
import { components } from "./_generated/api.js";

const ytCache = new YouTubeMetadataCache(components.youtubeMetadata, {
  apiKey: "YOUR_YOUTUBE_DATA_API_V3_KEY",
  defaultTtlMs: 3_600_000, // 1 hour (optional)
  quotaLimit: 10_000,       // daily quota (optional)
  reservationTimeoutMs: 10_000, // 10s wait (optional)
  maxRetries: 3,            // retry attempts (optional)
  baseRetryDelayMs: 1_000,  // backoff base (optional)
});

Tip: Store your YouTube API key in the Convex dashboard as an environment variable, then pass it to the constructor:

const ytCache = new YouTubeMetadataCache(components.youtubeMetadata, {
  apiKey: process.env.YOUTUBE_API_KEY!,
});

Usage

Get a single video

import { action } from "./_generated/server.js";
import { v } from "convex/values";

export const getVideo = action({
  args: { videoId: v.string() },
  handler: async (ctx, args) => {
    const video = await ytCache.getVideo(ctx, {
      videoId: args.videoId,
    });

    if (!video) throw new Error("Video not found");

    return {
      title: video.title,
      channel: video.channelTitle,
      duration: video.duration,
      views: video.viewCount,
    };
  },
});

Batch lookup

export const getPlaylist = action({
  args: { videoIds: v.array(v.string()) },
  handler: async (ctx, args) => {
    // Single API call for up to 50 videos
    const videos = await ytCache.getVideos(ctx, {
      videoIds: args.videoIds,
      ttl: 2 * 60 * 60 * 1000, // 2 hour TTL
    });

    return videos.filter((v) => v !== null);
  },
});

Check cache stats

import { query } from "./_generated/server.js";

export const stats = query({
  handler: async (ctx) => {
    const stats = await ytCache.getCacheStats(ctx);

    return {
      hitRate: `${(stats.hitRate * 100).toFixed(1)}%`,
      quotaUsed: `${stats.quotaUsed}/${stats.quotaLimit}`,
      activeReservations: stats.activeReservations,
    };
  },
});

Clear cache

import { mutation } from "./_generated/server.js";

export const resetCache = mutation({
  handler: async (ctx) => {
    const deleted = await ytCache.clearCache(ctx);
    console.log(`Cleared ${deleted} cached videos`);
  },
});

Adjust quota limit

export const updateQuota = mutation({
  handler: async (ctx) => {
    // If you have a higher quota from Google
    await ytCache.setQuotaLimit(ctx, { quotaLimit: 50_000 });
  },
});

Schedule stale cleanup

// convex/crons.ts
import { cronJobs } from "convex/server";
import { internal } from "./_generated/api.js";

const crons = cronJobs();
crons.hourly("cleanup stale cache", { minuteUTC: 0 }, internal.youtube.cleanup);
export default crons;

// convex/youtube.ts
export const cleanup = internalMutation({
  handler: async (ctx) => {
    const deleted = await ytCache.cleanupStale(ctx);
    console.log(`Cleaned up ${deleted} stale entries`);
  },
});

How It Works

  • Cache Check -- On every request, the component checks the local Convex database for a cached entry.
  • Fresh Hit -- If the entry exists and is within its TTL, it's returned immediately (cache hit).
  • Stale/Miss -- If the entry is missing or stale, the component checks the rate limiter.
  • Quota Available -- If quota is available, it fetches fresh data from the YouTube API.
  • Quota Exhausted -- If quota is exhausted, a reservation is created. The request waits (up to reservationTimeoutMs) for quota to free up.
  • Graceful Degradation -- If the wait times out, stale data is returned when available. Otherwise, null is returned.
  • Retry Logic -- Transient HTTP errors trigger exponential backoff retries (up to maxRetries attempts).

API Reference

YouTubeMetadataCache class

MethodContextDescription
getVideo(ctx, { videoId, ttl? })actionGet metadata for a single video
getVideos(ctx, { videoIds, ttl? })actionBatch lookup for multiple videos
getCacheStats(ctx)queryGet hit rate, quota usage, reservations
clearCache(ctx)mutationClear all cached entries and reset stats
setQuotaLimit(ctx, { quotaLimit })mutationUpdate the daily quota limit
cleanupStale(ctx)mutationRemove entries past their TTL
resetQuota(ctx)mutationReset quota counter and start fresh window

VideoMetadata type

FieldTypeDescription
videoIdstringYouTube video ID
titlestringVideo title
descriptionstringVideo description
thumbnailUrlstringHighest quality thumbnail URL
durationstringISO 8601 duration (e.g. PT4M13S)
channelIdstringChannel ID
channelTitlestringChannel name
publishedAtstringISO 8601 publish date
viewCountstringView count
likeCountstringLike count
cachedAtnumberWhen this entry was cached (ms since epoch)
ttlnumberTTL in milliseconds
freshbooleanWhether the entry is within its TTL

Constructor options

OptionTypeDefaultDescription
apiKeystringrequiredYouTube Data API v3 key
defaultTtlMsnumber3,600,000Default cache TTL (1 hour)
quotaLimitnumber10,000Max quota units per day
reservationTimeoutMsnumber10,000Max wait time for quota (10s)
maxRetriesnumber3Retry attempts on transient failures
baseRetryDelayMsnumber1,000Base delay for exponential backoff

Live Demo

Check out the live demo at youtube-metadata-demo.vercel.app.

Author

Built and maintained by TimpiaAI.

License

MIT

Keywords

convex

FAQs

Package last updated on 11 Mar 2026

Related posts