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

live-leaderboard

v1.0.4

Published

A plug-and-play real-time leaderboard SDK using Redis and Socket.IO with Express router integration for easy backend setup.

Readme

🏆 Live Leaderboard SDK

A real‑time leaderboard library powered by Redis, Postgres, Socket.IO, and TypeScript.

  • Postgres → Persistent storage
  • Redis → Ultra-fast reads & live score updates
  • Socket.IO → Instant leaderboard broadcasts
  • Express Router → Drop-in REST API

Perfect for multiplayer games, coding contests, and online quizzes.


📦 Installation

npm install live-leaderboard redis pg express socket.io dotenv
# or:
pnpm add live-leaderboard redis pg express socket.io dotenv

⚙️ Database Setup (Postgres)

1) Create schema

Save this as playground/schema.sql in your project:

CREATE TABLE IF NOT EXISTS leaderboard_scores (
  id BIGSERIAL PRIMARY KEY,
  game_id TEXT NOT NULL,
  user_id TEXT NOT NULL,
  score INTEGER NOT NULL,
  updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  CONSTRAINT leaderboard_scores_game_user_uk UNIQUE (game_id, user_id)
);

CREATE INDEX IF NOT EXISTS idx_leaderboard_scores_game_score_desc
  ON leaderboard_scores (game_id, score DESC);

Run it once:

psql -d <YOUR_DB_NAME> -f playground/schema.sql

If you customize table/column names, update the LeaderboardConfig accordingly (see below).


🚀 Backend Setup

.env

REDIS_URL=redis://localhost:6379
POSTGRES_URL=postgres://user:pass@localhost:5432/dbname
PORT=3000

Run Redis & Postgres (Docker)

docker run -p 6379:6379 redis

docker run -p 5432:5432 \
  -e POSTGRES_PASSWORD=pass \
  -e POSTGRES_USER=user \
  -e POSTGRES_DB=dbname \
  postgres

server.ts

import express from "express";
import http from "http";
import { Server as SocketIOServer } from "socket.io";
import { createClient } from "redis";
import { Pool } from "pg";
import dotenv from "dotenv";

import {
  Leaderboard,
  RedisService,
  PostgresService,
  createLeaderboardRouter,
  type LeaderboardConfig
} from "live-leaderboard";

dotenv.config();

async function main() {
  const app = express();
  const server = http.createServer(app);
  const io = new SocketIOServer(server, { cors: { origin: "*" } });
  app.use(express.json());

  const redis = createClient({ url: process.env.REDIS_URL });
  await redis.connect();

  const pg = new Pool({ connectionString: process.env.POSTGRES_URL });

  const config: LeaderboardConfig = {
    redisPrefix: "leaderboard",
    tableName: "leaderboard_scores",
    columns: {
      gameId: "game_id",
      userId: "user_id",
      score: "score",
    },
  };

  const leaderboard = new Leaderboard(
    new RedisService(redis, config.redisPrefix),
    new PostgresService(pg, config)
  );

  app.use("/leaderboard", createLeaderboardRouter(leaderboard, io));

  io.on("connection", (socket) => {
    console.log("Client connected:", socket.id);
    socket.on("join-game", (gameId: string) => socket.join(gameId));
  });

  server.listen(process.env.PORT || 3000, () =>
    console.log(`Server running at http://localhost:${process.env.PORT || 3000}`)
  );
}

main().catch(console.error);

🎮 Frontend Example (Vanilla)

<!DOCTYPE html>
<html>
<head>
  <title>Live Leaderboard</title>
  <script src="https://cdn.socket.io/4.7.2/socket.io.min.js"></script>
</head>
<body>
  <h1>Leaderboard</h1>
  <ul id="leaderboard"></ul>

  <script>
    const socket = io("http://localhost:3000");
    socket.emit("join-game", "demo123");

    socket.on("leaderboard:update", (players) => {
      const list = document.getElementById("leaderboard");
      list.innerHTML = "";
      players.forEach((p, i) => {
        const li = document.createElement("li");
        li.textContent = `#${i + 1} ${p.userId} - ${p.score}`;
        list.appendChild(li);
      });
    });
  </script>
</body>
</html>

📡 REST API

POST /leaderboard/score

Submit or update a score.

Body

{
  "gameId": "demo123",
  "userId": "alice",
  "score": 150
}

Response

{ "success": true }

GET /leaderboard/:gameId/top?limit=10

Get top N players.

Response

[
  { "userId": "dave", "score": 490 },
  { "userId": "alice", "score": 237 },
  { "userId": "bob", "score": 39 }
]

GET /leaderboard/:gameId/rank/:userId

Get a user’s rank.

Response

{ "rank": 2 }

⚡ SDK Usage (Direct)

import { Leaderboard, RedisService, PostgresService, type LeaderboardConfig } from "live-leaderboard";
import { createClient } from "redis";
import { Pool } from "pg";

const redis = createClient({ url: process.env.REDIS_URL });
await redis.connect();

const pg = new Pool({ connectionString: process.env.POSTGRES_URL });

const config: LeaderboardConfig = {
  redisPrefix: "lb",
  tableName: "leaderboard_scores",
  columns: { gameId: "game_id", userId: "user_id", score: "score" },
};

const leaderboard = new Leaderboard(
  new RedisService(redis, config.redisPrefix),
  new PostgresService(pg, config)
);

await leaderboard.submitScore("game1", "bob", 1000);
console.log(await leaderboard.getTopPlayers("game1", 5));
console.log(await leaderboard.getUserRank("game1", "bob"));

🔧 Custom Schema Support

Use any table/column names by changing the config:

const config: LeaderboardConfig = {
  redisPrefix: "myapp:lb",
  tableName: "game_scores_custom",
  columns: {
    gameId: "gid",
    userId: "uid",
    score: "points",
  },
};

Matching SQL:

CREATE TABLE IF NOT EXISTS game_scores_custom (
  id BIGSERIAL PRIMARY KEY,
  gid TEXT NOT NULL,
  uid TEXT NOT NULL,
  points INTEGER NOT NULL,
  updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  CONSTRAINT game_scores_custom_gid_uid_uk UNIQUE (gid, uid)
);

📁 Suggested Project Structure

live-leaderboard/
├── src/
│   ├── config/           # DB & service configs
│   ├── server/           # Express + Socket.IO setup
│   ├── sdk/              # Leaderboard classes/services
│   └── playground/
│       ├── server.ts     # Example server
│       └── schema.sql    # Postgres schema
├── package.json
└── README.md

📝 License

MIT © abhi-garg