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

@praxium/sdk

v0.5.91

Published

Official TypeScript SDK for the Praxium platform API

Downloads

1,268

Readme

@praxium/sdk

Official TypeScript SDK for the Praxium platform API. Build tenant websites that display practice data (team, services, FAQ, opening hours) with full locale support.

Quick Start

npm install @praxium/sdk

Locale-Specific Mode

Pass a locale to get pre-resolved labels and values in that language:

import { createPraxiumClient } from '@praxium/sdk'

const client = createPraxiumClient({
  baseUrl: process.env.PRAXIUM_API_URL!,
  apiKey: process.env.PRAXIUM_API_KEY!,
  locale: 'nl',  // 'nl' | 'en' | '*' (see Multilingual Mode)
})

const hours = await client.getOpeningHours()
const team = await client.getTeamMembers()
const faq = await client.getFaq()

All methods return data directly or throw a typed PraxiumError on failure (see Error Handling).

Multilingual Mode

Pass locale: '*' to receive all translations as objects. Resolve them at render time with localizeText():

import { createPraxiumClient, localizeText } from '@praxium/sdk'

const client = createPraxiumClient({
  baseUrl: process.env.PRAXIUM_API_URL!,
  apiKey: process.env.PRAXIUM_API_KEY!,
  locale: '*',
})

const team = await client.getTeamMembers()
// Returns MultilingualTeamMember[] — labels and values are multilingual objects:
// {
//   name: "Dr. Jan de Vries",
//   role: "Fysiotherapeut",
//   customFields: [
//     { label: { nl: "Specialisatie", en: "Specialization" }, value: { nl: "Sport", en: "Sports" } }
//   ]
// }

const label = localizeText(team[0].customFields[0].label, 'nl')  // → "Specialisatie"
const value = localizeText(team[0].customFields[0].value, 'nl')  // → "Sport"

Contact Form

Submit a contact form on behalf of a website visitor:

const result = await client.submitContactForm({
  name: 'Jan de Vries',
  email: '[email protected]',
  phone: '+31612345678',
  subject: 'Appointment request',
  message: 'I would like to book an appointment.',
  acceptTerms: true,  // required — must reflect the visitor's explicit consent
})
// → { success: true, emailStatus: 'sent' }

Configuration

Your website needs two environment variables to connect to the Praxium platform, plus an optional third for webhook-based cache revalidation:

Required:

| Variable | Purpose | Example | |----------|---------|---------| | PRAXIUM_API_URL | Your tenant's admin portal URL. The SDK sends API requests to this host. | https://mypractice.admin.praxium.nl | | PRAXIUM_API_KEY | HMAC API key for authentication. Generated in the admin portal under API Profiles. The tenant slug AND the API profile slug are both embedded in the key — no need to configure them separately. | praxium_v1_mypractice_public-team_17..._abc... |

Optional (only if using ISR revalidation webhooks):

| Variable | Purpose | Example | |----------|---------|---------| | PRAXIUM_WEBHOOK_SECRET | Shared secret for webhook signature verification. Only needed if you want the platform to notify your site when data changes (team, FAQ, opening hours). See Webhooks. | (32+ character random string) |

# .env.local
PRAXIUM_API_URL="https://mypractice.admin.praxium.nl"
PRAXIUM_API_KEY="praxium_v1_mypractice_public-team_1234567890_abcdef..."
PRAXIUM_WEBHOOK_SECRET="your-webhook-secret-from-admin-portal"  # optional

How tenant routing works: Each tenant has its own admin subdomain ({slug}.admin.praxium.nl). The platform identifies your tenant from both the hostname AND the API key's embedded slug, cross-validating them for security. You don't need to configure the tenant slug separately — it's derived from your API key automatically.

Available Methods

| Method | Description | |--------|-------------| | getOpeningHours() | Weekly opening schedule | | getTeamMembers() | Staff members with photos and custom fields | | getContactDetails() | Practice contact information | | getLocation() | Address and map coordinates | | getBusinessName() | Practice display name | | getSocialLinks() | Social media URLs | | getFaq() | FAQ grouped by category | | getServiceVariants() | Service pricing and variants | | getInsuranceInfo() | Accepted insurance providers | | getFeatures() | Practice features and amenities | | getPaymentMethods() | Accepted payment methods | | getPolicyInfo() | Practice policies | | getBookableServices() | Services available for online booking | | submitContactForm(body) | Submit a contact form |

Error Handling

All methods throw typed errors that you can catch individually:

import { PraxiumNotFoundError, PraxiumAuthError } from '@praxium/sdk'

try {
  const team = await client.getTeamMembers()
} catch (error) {
  if (error instanceof PraxiumNotFoundError) {
    // 404 — resource not found
  } else if (error instanceof PraxiumAuthError) {
    // 401 — invalid or expired API key
  }
}

| Error Class | HTTP Status | When | |-------------|-------------|------| | PraxiumAuthError | 401 | Invalid API key | | PraxiumForbiddenError | 403 | Key doesn't match tenant | | PraxiumNotFoundError | 404 | Resource not found | | PraxiumValidationError | 400 | Invalid request data | | PraxiumRateLimitError | 429 | Too many requests | | PraxiumError | Other | Base class for all errors |

Webhooks

When an admin updates data (team, FAQ, opening hours), the platform sends an HMAC-signed webhook to your registered endpoint. You can use this to react to entity changes in any way your application needs.

Common use cases:

| Use Case | What you do on entity change | |----------|------------------------------| | ISR revalidation | Call revalidatePath() to refresh cached pages | | Search index | Re-index changed entities in your search engine | | CDN cache | Purge cached API responses or assets |

Prerequisites:

  1. Register your webhook endpoint URL in the admin portal (Settings → Webhooks)
  2. Set PRAXIUM_WEBHOOK_SECRET to the shared secret from the admin portal

All webhook handlers include HMAC-SHA256 signature verification, timestamp-based replay protection (5-minute window), and timing-safe comparison.

Next.js ISR Revalidation

The SDK provides a ready-made handler for Next.js ISR revalidation. It verifies the webhook signature and calls revalidatePath() for the affected pages.

The pathMap is fully customizable — you define which pages to revalidate for each entity based on your website's routing structure:

// app/api/revalidate/route.ts
import { createRevalidationHandler } from '@praxium/sdk/webhooks'

export const POST = createRevalidationHandler({
  secret: process.env.PRAXIUM_WEBHOOK_SECRET!,
  // Map platform entities to YOUR website's page paths.
  // Adjust the paths to match your project's routing structure.
  pathMap: {
    'team': ['/nl/team', '/en/team'],
    'rates': ['/nl/tarieven', '/en/rates'],
    'faq': ['/nl/faq', '/en/faq'],
    'opening-hours': ['/nl', '/en'],    // homepage shows opening hours
    'all': ['/nl', '/en'],              // full-site revalidation
  },
})

Requires next as a peer dependency.

Custom Webhook Handler

For non-Next.js use cases or custom logic, use processWebhook() to verify the signature and extract the changed entity:

// Example: invalidate a Redis cache on entity change
import { processWebhook, WEBHOOK_SIGNATURE_HEADER } from '@praxium/sdk/webhooks'

export async function POST(request: Request) {
  const body = await request.text()

  const result = await processWebhook({
    body,
    signature: request.headers.get(WEBHOOK_SIGNATURE_HEADER)!,
    secret: process.env.PRAXIUM_WEBHOOK_SECRET!,
  })

  if (!result.valid) {
    return new Response(result.error, { status: 401 })
  }

  // result.entity = 'team' | 'faq' | 'rates' | ...
  await redis.del(`cache:${result.entity}`)

  return new Response('OK')
}

Contributing

npm run generate   # Regenerate client from OpenAPI spec
npm run build      # Build dist/
npm run test       # Run tests
npm run typecheck  # Type-check without emitting

License

MIT