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 🙏

© 2025 – Pkg Stats / Ryan Hefner

erlc-api

v3.1.0

Published

An ER:LC API wrapper for JS/TS

Readme

🚔 ER:LC API Wrapper

npm version License: MIT

A comprehensive, lightweight, and fully-typed API wrapper for Emergency Response: Liberty County (ER:LC) with 100% API coverage, robust error handling, and TypeScript support.

✨ Features

  • 🎯 100% API Coverage - All ER:LC API endpoints supported
  • 🛡️ Robust Error Handling - Comprehensive error catching and meaningful error messages
  • 📝 Full TypeScript Support - Complete type definitions for all methods and responses
  • Optimized Performance - Efficient request handling with timeout management
  • 🔒 Secure - Built-in validation and secure token handling
  • 📚 Well Documented - Extensive documentation and examples
  • 🚀 Easy to Use - Simple, intuitive API design

📦 Installation

npm install erlc-api
bun add erlc-api

🚀 Quick Start

Basic Setup

const erlc = require("erlc-api");

// Initialize the client with your global token
const client = new erlc.Client({
  globalToken: "your-global-token-here", // Get this from ER:LC developers
});

// Register your client
client.config();

TypeScript Setup

import erlc from "erlc-api";

const client = new erlc.Client({
  globalToken: "your-global-token-here",
});

client.config();

📖 API Methods

🖥️ Server Information

Get Server Details

const getServerInfo = async () => {
  try {
    const serverToken = "your-server-api-key"; // From Server Settings
    const server = await erlc.getServer(serverToken);

    console.log(server);
    /*
    Expected Response:
    {
      Name: "Your Server Name",
      OwnerUsername: "ServerOwner",
      CoOwnerUsernames: ["CoOwner1", "CoOwner2"],
      CurrentPlayers: 25,
      MaxPlayers: 40,
      JoinKey: "ABC123",
      AccVerifiedReq: "Disabled", // "Email" | "Phone/ID"
      TeamBalance: true,
      VanityURL: "https://policeroleplay.community/join?code=ABC123"
    }
    */
  } catch (error) {
    console.error("Error fetching server info:", error.message);
  }
};

Get Current Players

const getCurrentPlayers = async () => {
  try {
    const players = await erlc.getPlayers(serverToken);

    console.log(players);
    /*
    Expected Response:
    [
      {
        Player: "PlayerName:123456789",
        Permission: "Server Owner", // "Member" | "Moderator" | "Server Administrator"
        Team: "Police" // "Civilian" | "Fire" | "Sheriff"
      }
    ]
    */
  } catch (error) {
    console.error("Error fetching players:", error.message);
  }
};

Get Server Queue

const getServerQueue = async () => {
  try {
    const queue = await erlc.getQueue(serverToken);
    console.log(`Players in queue: ${queue.length}`);
  } catch (error) {
    console.error("Error fetching queue:", error.message);
  }
};

👥 Staff Management

Get Staff Information

const getStaffInfo = async () => {
  try {
    const staff = await erlc.getStaff(serverToken);

    console.log(staff);
    /*
    Expected Response:
    {
      CoOwners: [123456789, 987654321],
      Admins: { "123456789": "AdminName" },
      Mods: { "987654321": "ModName" }
    }
    */
  } catch (error) {
    console.error("Error fetching staff:", error.message);
  }
};

📊 Server Logs

Get Join/Leave Logs

const getJoinLogs = async () => {
  try {
    const logs = await erlc.getJoinLogs(serverToken);

    logs.forEach((log) => {
      const action = log.Join ? "joined" : "left";
      console.log(
        `${log.Player} ${action} at ${new Date(log.Timestamp * 1000)}`
      );
    });
  } catch (error) {
    console.error("Error fetching join logs:", error.message);
  }
};

Get Kill Logs

const getKillLogs = async () => {
  try {
    const kills = await erlc.getKillLogs(serverToken);

    kills.forEach((kill) => {
      console.log(
        `${kill.Killer} killed ${kill.Killed} at ${new Date(
          kill.Timestamp * 1000
        )}`
      );
    });
  } catch (error) {
    console.error("Error fetching kill logs:", error.message);
  }
};

Get Command Logs

const getCommandLogs = async () => {
  try {
    const commands = await erlc.getCommandLogs(serverToken);

    commands.forEach((cmd) => {
      console.log(`${cmd.Player} executed: ${cmd.Command}`);
    });
  } catch (error) {
    console.error("Error fetching command logs:", error.message);
  }
};

Get Moderator Call Logs

const getModCalls = async () => {
  try {
    const modcalls = await erlc.getModcallLogs(serverToken);

    modcalls.forEach((call) => {
      const status = call.Moderator
        ? `answered by ${call.Moderator}`
        : "unanswered";
      console.log(`${call.Caller} made a modcall - ${status}`);
    });
  } catch (error) {
    console.error("Error fetching modcall logs:", error.message);
  }
};

🚗 Vehicle Management

Get Server Vehicles

const getVehicles = async () => {
  try {
    const vehicles = await erlc.getVehicles(serverToken);

    vehicles.forEach((vehicle) => {
      console.log(
        `${vehicle.Name} owned by ${vehicle.Owner} - Texture: ${
          vehicle.Texture || "Default"
        }`
      );
    });
  } catch (error) {
    console.error("Error fetching vehicles:", error.message);
  }
};

🔨 Server Management

Execute Server Commands

const executeCommand = async () => {
  try {
    const success = await erlc.runCommand(
      serverToken,
      ":h Welcome to our server!"
    );

    if (success) {
      console.log("Command executed successfully!");
    }
  } catch (error) {
    console.error("Error executing command:", error.message);
  }
};

Get Server Bans

const getBannedPlayers = async () => {
  try {
    const bans = await erlc.getBans(serverToken);

    Object.entries(bans).forEach(([playerId, playerName]) => {
      console.log(`${playerName} (${playerId}) is banned`);
    });
  } catch (error) {
    console.error("Error fetching bans:", error.message);
  }
};

🛠️ Advanced Usage

Error Handling Best Practices

const handleApiCall = async () => {
  try {
    const result = await erlc.getServer(serverToken);
    return result;
  } catch (error) {
    // The error is now an ErlcError with detailed information
    console.error(`Error ${error.code}: ${error.message}`);
    console.error(`Category: ${error.category}, Severity: ${error.severity}`);

    // Handle specific ERLC error codes
    switch (error.code) {
      case 2002:
        console.error(
          "Invalid server key - get a new one from server settings"
        );
        break;
      case 4001:
        console.error("Rate limited - reduce request frequency");
        break;
      case 3002:
        console.error("Server offline - wait for players to join");
        break;
      case 9999:
        console.error("Server module outdated - restart server");
        break;
    }

    // Show suggested actions
    if (error.suggestions) {
      console.error("Suggested actions:");
      error.suggestions.forEach((action) => console.error(`- ${action}`));
    }

    // Check if error is retryable
    if (error.retryable) {
      console.error("This error might be resolved by retrying");
    }

    throw error; // Re-throw if needed
  }
};

Batch Operations

const getServerOverview = async (serverToken) => {
  try {
    // Execute multiple API calls concurrently
    const [serverInfo, players, staff, vehicles] = await Promise.all([
      erlc.getServer(serverToken),
      erlc.getPlayers(serverToken),
      erlc.getStaff(serverToken),
      erlc.getVehicles(serverToken),
    ]);

    return {
      server: serverInfo,
      playerCount: players.length,
      staffCount:
        Object.keys(staff.Admins).length + Object.keys(staff.Mods).length,
      vehicleCount: vehicles.length,
    };
  } catch (error) {
    console.error("Error getting server overview:", error.message);
    throw error;
  }
};

🔑 Authentication

Getting Your Tokens

  1. Global Token: Contact ER:LC developers through their Discord to request increased API limits
  2. Server Token: Found in your server settings within ER:LC

Token Security

// ❌ Don't hardcode tokens
const client = new erlc.Client({
  globalToken: "your-token-here",
});

// ✅ Use environment variables
const client = new erlc.Client({
  globalToken: process.env.ERLC_GLOBAL_TOKEN,
});

📝 TypeScript Support

The package includes comprehensive TypeScript definitions:

import erlc, { ServerStatus, ServerPlayer, JoinLog } from "erlc-api";

const client = new erlc.Client({
  globalToken: process.env.ERLC_GLOBAL_TOKEN!,
});

client.config();

// Fully typed responses
const server: ServerStatus = await erlc.getServer(serverToken);
const players: ServerPlayer[] = await erlc.getPlayers(serverToken);
const joinLogs: JoinLog[] = await erlc.getJoinLogs(serverToken);

⚡ Performance Tips

  1. Use Promise.all() for concurrent requests when fetching multiple endpoints
  2. Implement caching for frequently accessed data that doesn't change often
  3. Handle rate limits by implementing retry logic with exponential backoff
  4. Use timeouts - all methods have built-in 10-15 second timeouts

🐛 Error Types

The wrapper provides comprehensive error handling with specific ERLC error codes:

ERLC Error Codes

| Code | Category | Description | | ---- | -------------------- | -------------------------------------- | | 0 | System Error | Unknown error occurred | | 1001 | Communication Error | Error communicating with Roblox server | | 1002 | System Error | Internal system error | | 2000 | Authentication Error | Missing server key | | 2001 | Authentication Error | Invalid server key format | | 2002 | Authentication Error | Invalid or expired server key | | 2003 | Authentication Error | Invalid global API key | | 2004 | Authentication Error | Server key banned | | 3001 | Request Error | Invalid command provided | | 3002 | Request Error | Server offline (no players) | | 4001 | Rate Limit Error | Rate limited | | 4002 | Permission Error | Restricted command | | 4003 | Content Error | Prohibited message | | 9998 | Access Error | Restricted resource | | 9999 | Version Error | Outdated server module |

Error Properties

All errors are instances of ErlcError with these properties:

  • code: ERLC error code or HTTP status
  • message: Human-readable error message
  • category: Error category (e.g., "AUTHENTICATION_ERROR")
  • severity: Error severity ("LOW", "MEDIUM", "HIGH", "CRITICAL")
  • suggestions: Array of suggested actions to resolve the error
  • retryable: Boolean indicating if the error might be resolved by retrying
  • timestamp: ISO timestamp when the error occurred

Retry Logic Example

async function withRetry(apiCall, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await apiCall();
    } catch (error) {
      if (!error.retryable || attempt === maxRetries) {
        throw error;
      }

      // Wait before retrying with exponential backoff
      const delay = 1000 * Math.pow(2, attempt - 1);
      await new Promise((resolve) => setTimeout(resolve, delay));
    }
  }
}

// Usage
const players = await withRetry(() => erlc.getPlayers(serverToken));

🤝 Contributing

We welcome contributions! Please see our Contributing Guide for details.

📄 License

This project is licensed under the MIT License - see the LICENSE file for details.

🔗 Links

👨‍💻 Credits