Node.js SDK
The Node.js SDK provides typed resources, response validation, cancellation, timeouts, and structured errors. It is deliberately thin: the HTTP API remains the canonical contract.
Create one client
Section titled “Create one client”import { Strent } from "@strent-dev/node";
export const strent = new Strent({ apiKey: process.env.STRENT_API_KEY!, baseUrl: process.env.STRENT_BASE_URL, timeoutMs: 1_000,});| Option | Default | Purpose |
|---|---|---|
apiKey |
Required | Project API key used as a Bearer token |
baseUrl |
https://api.strent.dev |
Override for local or self-managed API access |
timeoutMs |
1000 |
Default positive integer timeout per request |
fetch |
globalThis.fetch |
Optional compatible fetch implementation |
entitlements |
None | What each Stripe product grants; see Check entitlements |
cache |
true |
Enable positive reuse and availability fallback |
onFallback |
Server warning | Replace the warning with a synchronous event handler |
Create one client per API key and base URL in your server process and reuse it. Never instantiate it in browser code.
Resource methods
Section titled “Resource methods”Every method takes a params object with the Stripe customer ID.
| Method | Result | HTTP operation |
|---|---|---|
activeItems.list({ customer }) |
Active Stripe subscription items | POST /v1/active_items/list |
customers.hasActiveSubscription({ customer }) |
boolean: at least one active item |
Derived from active_items/list |
activeItems.hasProduct({ customer, product }) |
boolean product membership |
Derived from active_items/list |
entitlements.retrieve({ customer }) |
Every declared entitlement and its value | Derived from active_items/list |
entitlements.has({ customer, entitlement }) |
boolean entitlement value |
Derived from active_items/list |
customers.retrieveBillingState({ customer }) |
Detailed subscription state | POST /v1/customers/retrieve_billing_state |
customers.refresh({ customer }) |
void after the refresh succeeds |
POST /v1/customers/refresh |
The entitlement methods require the entitlements option and accept a null or undefined customer, answering from your defaults without a request.
customers.hasActiveSubscription is true when the customer has at least one active item. Stripe never leaves a subscription without items, so that is the same as having an access-bearing subscription.
The package exports ActiveItem, BillingState, BillingSubscription, BillingSubscriptionItem, BillingPrice, Entitlements, EntitlementsConfig, and EntitlementValue types for application code.
Check several plans with one request
Section titled “Check several plans with one request”Each derived check reuses a recent saved list that contains the requested value, but a value missing from that list always costs a request. Chaining hasProduct calls across several plans therefore sends one request per absent product, which is every request for a free customer. When one request checks more than one product, read the list once and resolve in memory; for several entitlements, call entitlements.retrieve once:
const { data: items } = await strent.activeItems.list({ customer });const productIds = new Set(items.map((item) => item.price.product.id));
const plan = productIds.has("prod_enterprise") ? "enterprise" : productIds.has("prod_pro") ? "pro" : "free";Refresh new customers and Checkout returns
Section titled “Refresh new customers and Checkout returns”await strent.customers.refresh({ customer: "cus_123" });const hasSubscription = await strent.customers.hasActiveSubscription({ customer: "cus_123",});Call refresh when a user returns from Checkout or the Customer Portal. Also call it right after creating a Stripe customer if your app may check access before Strent processes Stripe’s customer.created webhook, a few seconds later, so that check returns false instead of CUSTOMER_STATE_NOT_READY.
refresh returns void after a successful refresh whose Stripe read starts
after the call arrives. Its SDK timeout defaults to 15 seconds independently of
the client read timeout; override it per call when needed. Strent waits up to 10
seconds and may reuse queued work. CUSTOMER_REFRESH_NOT_FINISHED means the wait
expired while durable work continues.
After success, this client clears all cached facts for that customer, including
older reads still in flight. A failed refresh keeps the saved facts.
Check one product
Section titled “Check one product”const hasPro = await strent.activeItems.hasProduct({ customer: "cus_123", product: "prod_pro",});This returns true when an active item has that product, with any price, and
false when no item matches. It uses POST /v1/active_items/list and shares the
saved list with activeItems.list and other product checks. Your application
decides what access that fact permits. Product IDs must be non-empty strings;
custom IDs without a prod_ prefix are valid. Invalid customers or products
throw StrentError before a request.
Override one request
Section titled “Override one request”Every method accepts request options as its last argument:
const controller = new AbortController();
const result = await strent.customers.hasActiveSubscription( { customer: "cus_123" }, { timeoutMs: 500, signal: controller.signal },);Request options apply when the SDK sends a request. Caller cancellation produces StrentAbortError. An elapsed SDK timeout produces StrentTimeoutError unless an eligible saved answer supplies fallback.
Positive reuse and fallback
Section titled “Positive reuse and fallback”The cache is enabled by default and belongs to this client. It works equally with test and live keys.
Every check and entitlement result derives from one saved active-item list per customer, shared with activeItems.list. A boolean check returns true without a request when that list is less than 5 minutes old and yields true. An absent value, a missing list, or a list aged 5 minutes or more triggers a request, and each successful request replaces the shared list. activeItems.list and entitlements.retrieve always go to Strent, so a saved list cannot hide an upgrade. Successful refresh clears the saved list in this client. With cache: false, every check goes to Strent.
If a request fails with a timeout, network error, HTTP 5xx, invalid successful response, or HTTP 429, a check returns true only when its saved list is less than 1 hour old and yields true, and activeItems.list and entitlements.retrieve answer from the saved list under the same age limit. The fallback warning names the operation you called. Empty saved lists are valid fallback; a check’s false is never served from cache. HTTP 400, 401, 403, 409, other non-429 4xx, and caller abort never use fallback. Without an eligible saved answer, the original error is thrown.
customers.retrieveBillingState and customers.refresh never use cached responses. The cache holds up to 10,000 customers and evicts the least recently used. Both windows are fixed, measured from the request start with a monotonic clock. The SDK does not coalesce or automatically retry requests.
For immediate revocation, turn off both reuse and fallback with cache: false. For different policies in different paths, create separate clients; there is no per-call switch.
import { Strent } from "@strent-dev/node";
export const strent = new Strent({ apiKey: process.env.STRENT_API_KEY!, cache: false,});Every fallback writes a server warning with the customer, operation, age in milliseconds, and original error. Positive reuse is silent. Replace the warning with a synchronous onFallback handler if you use your own logger:
import { Strent, type StrentFallbackEvent } from "@strent-dev/node";
export const strent = new Strent({ apiKey: process.env.STRENT_API_KEY!, onFallback(event: StrentFallbackEvent) { console.warn("Using saved Strent facts", event); },});The event fields are operation, customer, ageMs, and the original error. A throwing handler is ignored so fallback still succeeds. With cache: false, the handler never runs.