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

@pendulum-baas/sdk

v1.0.1

Published

SDK Client library for Pendulum BaaS

Downloads

4

Readme

Pendulum SDK

A TypeScript SDK for connecting frontend applications to the Pendulum Backend-as-a-Service platform. Provides seamless integration for database operations, authentication, and real-time updates.

Features

  • Database Operations - Full CRUD operations with TypeScript generics
  • Authentication - User registration, login, and logout
  • Real-time Updates - Subscribe to live data changes via Server-Sent Events
  • TypeScript First - Built with TypeScript for excellent developer experience
  • Error Handling - Comprehensive error handling with detailed error messages
  • Configurable - Easy configuration for different environments

Installation

npm install @pendulum/sdk

Quick Start

import { PendulumClient } from "@pendulum/sdk";

// Initialize the client
const client = new PendulumClient({
  apiUrl: "https://your-app-server.com",
  eventsUrl: "https://your-event-server.com/events"
});

// Database operations
const users = await client.db.getAll("users");
if (users.success) {
  console.log(users.data);
}

// Authentication
const loginResult = await client.auth.login("username", "password");
if (loginResult.success) {
  console.log("Logged in:", loginResult.userId);
}

// Real-time subscriptions
client.realtime.subscribe("users", (event) => {
  console.log("User data changed:", event);
});

Configuration

The SDK accepts the following configuration options:

interface PendulumConfig {
  apiUrl?: string;      // Default: "http://localhost:3000"
  eventsUrl?: string;   // Default: "http://localhost:8080/events"
}

Environment-specific Setup

Development:

const client = new PendulumClient(); // Uses defaults

Production:

const client = new PendulumClient({
  apiUrl: process.env.REACT_APP_API_URL,
  eventsUrl: process.env.REACT_APP_EVENTS_URL
});

API Reference

Database Operations

All database methods return a DatabaseResult<T>, where T defaults to any to allow for optional typing, with the following structure:

interface DatabaseResult<T = any> {
  success: boolean;
  data?: T;
  error?: string;
}

Read Operations

// Get a single record
const user = await client.db.getOne<User>("users", "userId");

// Get multiple records with pagination
const users = await client.db.getSome<User[]>("users", 10, 0, "createdAt");

// Get all records
const allUsers = await client.db.getAll<User[]>("users");

Write Operations

// Insert new records
const result = await client.db.insert("users", [
  { name: "John", email: "[email protected]" }
]);

// Update a single record
const updated = await client.db.updateOne("users", "userId", {
  name: "John Doe"
});

// Update multiple records
const bulkUpdate = await client.db.updateSome("users",
  { status: "inactive" },
  { status: "archived" }
);

// Update all records
const updateAll = await client.db.updateAll("users", { lastSeen: new Date() });

// Replace a record completely
const replaced = await client.db.replace("users", "userId", {
  name: "Jane Doe",
  email: "[email protected]"
});

Delete Operations

// Delete a single record
const deleted = await client.db.removeOne("users", "userId");

// Delete multiple records
const bulkDelete = await client.db.removeSome("users", { status: "archived" });

// Delete all records
const deleteAll = await client.db.removeAll("users");

Authentication

Authentication methods return AuthResult or LoginResult:

interface AuthResult {
  success: boolean;
  error?: string;
}

interface LoginResult extends AuthResult {
  userId?: string;
}
// Register a new user
const registerResult = await client.auth.register("username", "[email protected]", "password");

// Login
const loginResult = await client.auth.login("username", "password");
if (loginResult.success) {
  console.log("User ID:", loginResult.userId);
}

// Logout
const logoutResult = await client.auth.logout();

Real-time Updates

Subscribe to live data changes using Server-Sent Events from the Pendulum Event Server:

// Subscribe to collection changes
const handleUserUpdate = (event: DatabaseEvent) => {
  console.log('User data changed:', event);
};

client.realtime.subscribe("users", handleUserUpdate);

// Unsubscribe from updates
client.realtime.unsubscribe("users", handleUserUpdate);

// Disconnect from all real-time updates
client.realtime.disconnect();

⚠️ Important: Subscription Management

The subscription system uses referential equality to track callback functions. Always use named functions to avoid duplicate subscriptions:

// ❌ Don't do this - creates duplicate subscriptions
client.realtime.subscribe("users", (event) => console.log(event));
client.realtime.subscribe("users", (event) => console.log(event));

// ✅ Do this instead - uses function reference
const handleUserUpdate = (event) => console.log(event);
client.realtime.subscribe("users", handleUserUpdate);
client.realtime.subscribe("users", handleUserUpdate); // Second subscription is de-duplicated

DatabaseEvent Structure

interface DatabaseEvent {
  collection: string;
  action: "insert" | "update" | "delete";
  eventData: {
    affected?: any[];           // Records that were affected
    filter?: any;               // Filter used for update/delete operations
    updateOperation?: any;      // Changes made in update operations
    count?: number;             // Number of affected records
    ids?: string[];             // IDs of affected records
  };
}

Framework Integration

React Integration

The Pendulum SDK works seamlessly with React applications and allows you to choose exactly which pieces of frontend state should update in real-time to mirror backend application state.

import React, { useState, useEffect } from "react";
import { PendulumClient, type DatabaseEvent } from "@pendulum/sdk";

const client = new PendulumClient({
	apiUrl: process.env.REACT_APP_API_URL,
	eventsUrl: process.env.REACT_APP_EVENTS_URL,
});

function UsersList() {
	const [users, setUsers] = useState([]);

	// callback function for realtime subscription
	const addUsers = (event: DatabaseEvent) => {
			const { action, eventData } = event;
			if (action === "insert") {
				const newUsers = eventData.affected;
				setUsers(prev => [...prev, ...newUsers].sort());
			}
	};

	React.useEffect(() => {
			// initial fetch on mount
			const fetchUsers = async () => {
				try {
					const response = await client.db.getAll("users");
					if (response.success) {
						const fetchedUsers = response.data;
						setUsers(fetchedUsers);
					} else {
						throw new Error(response.error);
					}
				} catch (error) {
					/* error handling */
				}
			};

			fetchUsers();

			// Subscribe to real-time event updates
			client.realtime.subscribe("users", addUsers);

			// Cleanup subscription on unmount
			return () => client.realtime.unsubscribe("users", addUsers);
	}, []);

	return (
		<div>
			{users.map(user => (
				<div key={user.id}>{user.name}</div>
			))}
		</div>
	);
}

TypeScript Support

The SDK is built with TypeScript and provides type safety out of the box:

interface User {
  id: string;
  name: string;
  email: string;
  createdAt: Date;
}

// Type-safe database operations
const user = await client.db.getOne<User>("users", "userId");
if (user.success) {
  console.log(user.data.name); // TypeScript knows this is a string
}

// Type-safe real-time subscriptions
client.realtime.subscribe("users", (event: DatabaseEvent) => {
  // TypeScript compiler knows that event has `collection`, `action`, and `eventData` properties
});

Error Handling

All SDK methods use consistent error handling. Network errors, HTTP errors (4xx, 5xx), and parsing errors are automatically caught and returned in a structured format:

const result = await client.db.getOne("users", "invalid-id");
if (!result.success) {
  console.error("Error:", result.error);
  // Handle the error appropriately
} else {
  console.log("Data:", result.data);
}

Real-time Connection Management

The SDK automatically handles connection management for real-time updates:

  • Automatic reconnection with exponential backoff
  • Connection state management
  • Error recovery for network issues
  • Clean disconnection when needed

Examples

User Management with Real-time Updates

import { PendulumClient, type DatabaseEvent, type User } from "@pendulum/sdk";

const client = new PendulumClient({
  apiUrl: "https://api.myapp.com",
  eventsUrl: "https://events.myapp.com/events"
});

// Subscribe to user changes
const handleUpdateUsers = (event: DatabaseEvent) => {
	if (event.action === "insert") {
    console.log("New user added:", event.eventData.affected);
  } else if (event.action === "update") {
    console.log("User updated:", event.eventData.affected);
  } else if (event.action === "delete") {
    console.log("User deleted:", event.eventData.ids);
  }
}
client.realtime.subscribe("users", handleUpdateUsers);

// Create a new user
const newUser = await client.db.insert("users", [{
  name: "Alice Smith",
  email: "[email protected]"
}]);

if (newUser.success) {
  console.log("User created successfully");
  // DatabaseEvent with new user will be pushed to all active clients
}

// Disconnect from real-time updates and close connection to event server
client.realtime.disconnect()

Authentication Flow

async function handleLogin(username: string, password: string) {
  const result = await client.auth.login(username, password);

  if (result.success) {
    console.log("Login successful!", result.userId);
    // Redirect to dashboard or update UI state
    return { success: true, userId: result.userId };
  } else {
    console.error("Login failed:", result.error);
    // Show error message to user
    return { success: false, error: result.error };
  }
}

Contributing

This SDK is part of the Pendulum Backend-as-a-Service platform. For issues, feature requests, or contributions, please refer to the main Pendulum project repository.