@thejob/admin-client
v1.0.1
Published
Typed TypeScript/JavaScript client for the thejob-admin-service HTTP API.
Readme
@thejob/admin-client
Typed client for thejob-admin-service: the aggregations behind the admin dashboard.
import { AdminClient } from '@thejob/admin-client';
const admin = new AdminClient({
baseUrl: process.env.ADMIN_SERVICE_URL!,
token: adminJwt,
});Auth
Every insights endpoint is requireAdmin: a JWKS-verified Bearer token and
the admin role. There is no API-key path on this service, so apiKey alone
produces a 401. Pass token.
This is also why a browser must not hold this client's token. The Svelte admin panel reaches these through its server-side proxy, which injects the admin's token so the browser never sees it.
health() is the exception: public, and 503 when the DB is down.
The API
| method | endpoint |
|---|---|
| getDashboardStats() | GET /insights/dashboard/stats |
| getJobsByLocation() | GET /insights/jobs/by-location |
| getCounts(type, query?) | GET /insights/:type/count |
| health() | GET /health |
type is users, jobs or closed (COUNT_TYPES).
Two things that bite
totalUniqueJobs is not the sum of the rows. getJobsByLocation dedupes
per (job, country), so a job listed in three countries appears in three rows but
is one job. Derive percentages from totalUniqueJobs, never from a sum over
items.
An unknown groupByRange does not 400. The service silently falls back to
day. So asking for weekly (not a value; week is) returns a plausible daily
series that gets charted under a weekly axis, with nothing in the response
revealing it. The client rejects an unknown range before it is sent, which is
the only place that failure can be caught.
The closed series is sparse by design
users and jobs bucket by createdAt. closed counts closed jobs by
closedAt, which only the auto-close path stamps, so closes predating that are
absent from the series rather than counted as zero.
Dashboard stats is one call on purpose
Not split into per-widget methods: the service computes every figure in one parallel batch, so fetching them separately would multiply the aggregation cost and let widgets disagree by drifting a few seconds apart.
Development
pnpm build # tsc -p tsconfig.json
pnpm test # vitest runThe test suite reads the service's own route files and asserts this client has a method for every endpoint they register, so an endpoint added without a client method fails here.
