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

@memberstack/admin

v1.6.0

Published

Official Memberstack Admin SDK for Node.js — server-side member, plan, and Data Table management with your secret key.

Readme

@memberstack/admin

The official Memberstack Admin SDK for Node.js. Manage members, plans, and Data Tables from your server with your secret key — securely, away from the browser.

⚠️ Server-side only. This package uses your secret key, which grants full administrative access to your app. Never expose it in client-side code, public repositories, or browser environments. For client-side auth, use @memberstack/dom.

Installation

npm install @memberstack/admin
# or
yarn add @memberstack/admin

Quick Start

import memberstackAdmin from '@memberstack/admin';

// Initialize with your secret key (find it in your Memberstack dashboard)
const memberstack = memberstackAdmin.init('sk_...'); // sk_sb_... for sandbox

// List members
const { data: members } = await memberstack.members.list({ limit: 10 });

// Create a member
const { data: member } = await memberstack.members.create({
  email: '[email protected]',
  password: 'a-strong-password'
});

Store your secret key in an environment variable, never in code:

const memberstack = memberstackAdmin.init(process.env.MEMBERSTACK_SECRET_KEY);

Secret Keys

  • Sandbox keys (sk_sb_...) — development and testing. Data is isolated from production.
  • Live keys (sk_...) — production. Real member data and Stripe transactions.

The key determines the environment automatically; sandbox and live data are never mixed.

API Reference

All methods return a promise. Read methods resolve to a { data } envelope, and failures reject with a { code, message } object (see Error Handling).

Members

// List members (paginated)
const { data, totalCount, endCursor, hasNextPage } = await memberstack.members.list({
  limit: 50,        // 1–100 (default 50)
  after: endCursor, // cursor from a previous page
  order: 'DESC'     // 'ASC' | 'DESC'
});

// Retrieve a member by id or email
const { data: member } = await memberstack.members.retrieve({ id: 'mem_...' });
const { data: byEmail } = await memberstack.members.retrieve({ email: '[email protected]' });

// Create a member
const { data: created } = await memberstack.members.create({
  email: '[email protected]',
  password: 'a-strong-password',
  plans: [{ planId: 'pln_...' }], // optional, free plans only
  customFields: { 'first-name': 'Jane' },
  metaData: { source: 'admin-api' },
  json: { preferences: { newsletter: true } },
  loginRedirect: '/dashboard'
});

// Update a member (only the fields you pass change)
const { data: updated } = await memberstack.members.update({
  id: 'mem_...',
  data: { verified: true, customFields: { 'first-name': 'Janet' } }
});

// Delete a member
const { data } = await memberstack.members.delete({ id: 'mem_...' }); // -> { id }

// Add / remove a free plan
await memberstack.members.addFreePlan({ id: 'mem_...', data: { planId: 'pln_...' } });
await memberstack.members.removeFreePlan({ id: 'mem_...', data: { planId: 'pln_...' } });

Notes: customFields and metaData are shallow-merged on update; json is replaced. Updating an email does not send a verification email. addFreePlan/removeFreePlan accept free plans only — use the @memberstack/dom checkout flow for paid plans.

Data Tables

Read, create, query, update, and delete records in your app's Data Tables. Create your tables and fields in the dashboard first — the SDK works with records.

// List tables (with field definitions)
const { data } = await memberstack.dataTables.list();

// Get one table by key
const { data: table } = await memberstack.dataTables.get({ table: 'contacts' });

// Create a record
const { data: record } = await memberstack.dataTables.createRecord({
  table: 'contacts',
  data: { name: 'Ada Lovelace', score: 9.5, active: true },
  memberId: 'mem_...' // optional, associate with a member
});

// Get one record by id
const { data: one } = await memberstack.dataTables.getRecord({
  table: 'contacts',
  recordId: 'rec_...'
}); // -> { record }

// Query records (Prisma-like findMany / findUnique)
const { data: page } = await memberstack.dataTables.queryRecords({
  table: 'contacts',
  query: {
    findMany: {
      where: { active: { equals: true }, score: { gte: 5 } },
      orderBy: { score: 'desc' },
      take: 10
    }
  }
}); // -> { records, pagination }

// Update / delete a record
await memberstack.dataTables.updateRecord({ table: 'contacts', recordId: 'rec_...', data: { score: 8 } });
await memberstack.dataTables.deleteRecord({ table: 'contacts', recordId: 'rec_...' });

Note: DECIMAL field values come back as strings from createRecord/updateRecord/deleteRecord, and as numbers from getRecord/queryRecords.

Verification

// Verify a member's JWT (from the client). Returns the decoded payload.
const payload = await memberstack.verifyToken({
  token,
  audience: process.env.MEMBERSTACK_APP_ID // optional but recommended
});
// payload.id -> the member's id

// Verify a webhook signature (svix). Returns true, or throws if invalid.
// The SDK looks up the svix headers by UPPERCASE key, but Node delivers
// incoming headers in lowercase — so map them, don't pass req.headers directly.
memberstack.verifyWebhookSignature({
  headers: {
    'SVIX-ID': req.headers['svix-id'],
    'SVIX-TIMESTAMP': req.headers['svix-timestamp'],
    'SVIX-SIGNATURE': req.headers['svix-signature']
  },
  secret: process.env.MEMBERSTACK_WEBHOOK_SECRET,
  payload: req.body
});

Common Patterns

Express auth middleware

import express from 'express';
import memberstackAdmin from '@memberstack/admin';

const memberstack = memberstackAdmin.init(process.env.MEMBERSTACK_SECRET_KEY);

async function requireAuth(req, res, next) {
  try {
    const token = (req.headers.authorization || '').replace('Bearer ', '');
    const member = await memberstack.verifyToken({ token });
    req.memberId = member.id;
    next();
  } catch {
    res.status(401).json({ error: 'Unauthorized' });
  }
}

app.get('/api/me', requireAuth, (req, res) => res.json({ memberId: req.memberId }));

Webhook handler

app.post('/webhooks/memberstack', express.json(), (req, res) => {
  try {
    memberstack.verifyWebhookSignature({
      // Map the lowercase incoming headers to the UPPERCASE keys the SDK reads.
      headers: {
        'SVIX-ID': req.headers['svix-id'],
        'SVIX-TIMESTAMP': req.headers['svix-timestamp'],
        'SVIX-SIGNATURE': req.headers['svix-signature']
      },
      secret: process.env.MEMBERSTACK_WEBHOOK_SECRET,
      payload: req.body
    });
  } catch {
    return res.status(400).send('Invalid signature');
  }

  // handle req.body.event ...
  res.sendStatus(200);
});

TypeScript Support

The package ships with full type declarations — no @types install needed. Params and responses are typed (e.g. members.list() resolves to a PaginatedPayload<Member>).

Error Handling

API calls reject with a plain object containing a code and message:

try {
  await memberstack.members.update({ id: 'mem_does_not_exist', data: { verified: true } });
} catch (error) {
  console.error(error.code, error.message);
  // e.g. "generic-message", "There is no member with this identifier."
}

members.retrieve() is the exception — a missing member resolves to { data: null } rather than throwing.

Rate limit: 25 requests/second per IP. Cache responses or batch work if you approach it.

Resources

License

MIT © Memberstack. See LICENSE.