Skip to content

SubjectClient & Auth

The core package has no React or Vue dependency and can be used standalone:

import {
createSubjectTokenProvider,
SubjectClient,
createChartModel,
createTableController,
} from '@meterry/component'

Configure React’s MeterClientProvider or Vue’s ancestor provideMeterClient with your application route, e.g. /api/meter/subject-token. The helper createSubjectTokenGetter(endpoint, options?) performs a GET, forwards signal, uses credentials: 'include' unless configured otherwise, and returns the grant. Provider options.fetch handles both the token route and Subject API requests. Existing client / getJwt integrations remain supported; precedence is client → getJwt → tokenEndpoint.

The route is implemented by your backend, not by the component or the Meter Subject grant API. Return a grant with access_token plus expiry metadata. See Getting Started for complete examples.

Besides passing getJwt directly, the recommended approach is to explicitly create and reuse a SubjectTokenProvider:

const tokenProvider = createSubjectTokenProvider(getSubjectToken)
const client = new SubjectClient(tokenProvider, {
baseUrl: 'https://api.meterry.com',
credentials: 'include',
})

The signature of getJwt / getSubjectToken is ({ signal }: { signal?: AbortSignal }) => Promise<SubjectTokenGrant>. It should call your backend, which issues a short-lived Subject Token based on the login session.

A given SubjectTokenProvider keeps at most one JWT request in flight; other components or clients await the same Promise. 401 refreshes use the same single-flight logic, and each API request retries at most once.

By default the provider persists Subject Tokens carrying expires_in or expires_at to the browser’s localStorage, under the default key meter:subject-token. You can override the key or disable persistence entirely:

const tokenProvider = createSubjectTokenProvider(getSubjectToken, {
tokenStorageKey: `meter:${tenantId}:${userId}`,
// tokenStorage: null, // in-memory cache only
})

Multi-user, multi-tenant, or multi-environment apps should give each auth scope its own tokenStorageKey. In compatibility mode with tokenScope, if no explicit tokenStorageKey is passed the library automatically uses the default key suffixed with the scope. Call clearToken() or clearSharedSubjectToken(scopeKey) on logout or account switching.

You can also share a single provider across components via getSharedSubjectTokenProvider(scope, getJwt, options) (this is what the tokenScope prop of the React/Vue components uses internally).

For automatic charts, client.clearToken() also invalidates that client’s completed chart cache. Stop/unmount active query components and create an authentication-scoped client before rendering another user/tenant. If an external provider changes identity without clearToken(), explicitly call clearUsageChartCache(client) and remount/refetch active charts; clearing shared token storage alone does not invalidate settled chart results. Automatic table cursor caches are controller-owned and must also be disposed/remounted on an auth-scope change. Do not share one client across simultaneous auth scopes.

  • POST /v1/subject/usage/query
  • POST /v1/subject/analysis/query
  • GET /v1/subject/usage/events/details
  • GET /v1/subject/realtime/balance

Beyond the wrapped methods, client.request(path, init) calls any endpoint directly (including endpoints added later), reusing all the token injection, 401 refresh, and retry logic:

const result = await client.request('/v1/subject/usage/export', {
method: 'POST',
body: JSON.stringify({ format: 'csv' }),
})

MeterClientOptions accepts a fetch to replace the default implementation — commonly used for tests and mocks:

const client = new SubjectClient(tokenProvider, {
fetch: mockMeterFetch, // (input, init) => Promise<Response>
})

The interactive previews in this documentation also use a custom fetch adapter with synthetic fixtures. Requests stay entirely inside that adapter; preview tokens are kept in memory with tokenStorage: null, and no production API or browser session credentials are used.

On failure the client throws MeterApiError; isUnauthorizedError(error) tells you whether it was a 401:

import { MeterApiError, isUnauthorizedError } from '@meterry/component'
try {
const data = await client.analysisQuery(request)
} catch (error) {
if (isUnauthorizedError(error)) {
// still 401 after token refresh and retry — usually means re-login
}
}

The browser still must never hold the tenant API key:

Browser ──session──▶ your backend ──tenant API Key──▶ Meter platform
│ │
└── short-lived Subject Token ──────────▶ Meter Subject API

getJwt should call your backend, which issues a short-lived Subject Token based on the login session.

For the backend grant flow and Subject API contracts, see Subject Token Frontend Integration.