Skip to content

Node and React quickstart

This page gets the Monaiq client installed and authorized in a Node application, and points at the React and assertion pieces that build on it.

The package is @sidub-inc/licensing-client. It is written in TypeScript and works in plain Node and in React.

Terminal window
npm install @sidub-inc/licensing-client
import { LicensingClient } from '@sidub-inc/licensing-client';
const client = new LicensingClient({
licenseServiceUri: 'https://api.monaiq.com/licensing',
encodedCredential: process.env.MONAIQ_CREDENTIAL // SIDUB_LIC_...
});
const authorization = await client.getAuthorization();

licenseServiceUri is the licensing API and is required. getAuthorization() returns the authorization for the credential you configured, which is what tells you what this customer is licensed for.

You have two ways to give the client a credential, and you pick one:

  • encodedCredential, the portable string starting SIDUB_LIC_ that carries everything in one value. This is the simpler option and the one above.
  • The parts separately: seatId, serviceKeyId, serviceKeyPublicMember and runtimeToken. Use this when your configuration already holds them individually. They are the same four values the encoded string carries.

Either way the client calls the runtime operations — {licenseServiceUri}/runtime and {consumptionServiceUri}/runtime — and presents the runtime token as Authorization: Bearer. It appends /runtime itself, so set licenseServiceUri and consumptionServiceUri to the plain addresses. The token is opaque: never decode, parse or display it.

The constructor also accepts consumptionServiceUri and billableResourceId for metered features, covered in Consumption and metering, plus timeout, validateSignatures, cacheEnabled and cacheMaxSize. The Node client reference lists them with what each one does.

The package ships React bindings on top of the same client: LicensingProvider puts a licensing context into your component tree, and useLicensingContext reads it from a component. It is the same client underneath, so nothing you learn here is wasted when you move from a script to a component tree. See Node client reference.

The same package carries a second client, PurchaseClient, for an application that sells your own offerings to the person using it. It is separate from LicensingClient on purpose: nothing about it needs a credential of yours.

  • The catalog reads are anonymous. listOfferings(issuerClientId) returns your storefront as https://monaiq.com/marketplace/{issuerClientId} shows it, and getOffering(issuerClientId, offeringId) returns one offering’s review with the features it grants, so the app never bakes in an offering id.
  • Checkout is the buyer’s. Your app signs the buyer in with their Monaiq identity and gives the client a getAccessToken function; createCheckout sends that token, and the license is minted for the buyer. There is no API key and no customer email on this path, so no secret of yours ships in the app.
import { PurchaseClient } from '@sidub-inc/licensing-client';
const purchase = new PurchaseClient({
purchaseServiceUri: 'https://api.monaiq.com/purchase',
getAccessToken: () => acquireBuyerToken() // MSAL or native auth in your shell
});
const requestId = crypto.randomUUID(); // keep it: a retry must send the same id
const session = await purchase.createCheckout({ requestId, offeringId, issuerClientId });
if (session.sessionUrl) {
await openInSystemBrowser(session.sessionUrl); // paid: the hosted Stripe Checkout page
}
const result = await purchase.pollResult(session.sessionId);
// result.encodedCredential is the buyer's SIDUB_LIC_... credential

A paid offering returns a Stripe Checkout URL to open in the system browser; a free one completes at once with no URL. Poll the result either way rather than waiting for a redirect, and pass originContext: 'mobile_app' from a mobile shell. Failures are LicensingError values with the server’s code (BuyerTokenInvalid, CheckoutResultForbidden, RequestIdInUse and so on) and correlationId.

The Node client reference lists the client’s methods and their errors.

Rather than reading an authorization and writing your own conditionals, you can express the condition as an assertion. The package ships six:

  • ServiceAccessAssertion
  • FeatureExistsAssertion
  • RateLimitAssertion
  • QuotaAssertion, for an allowance
  • CompositeAssertion, which combines other assertions
  • NotAssertion, which negates one

RateLimitAssertion is the one to reach for when a feature is rate limited; see Consumption and metering.

If you would rather not wire the call sites by hand, the AI-assisted path returns the base wiring and a feature check for your own project, and can plumb the credential in for you. See AI-assisted setup.