SDK Development Guide¶
This page is for contributors who maintain the SDK or add new API endpoints. For usage documentation, see the SDK Overview.
Location: ui-clients/sdk/
How adding endpoints / modules works¶
The SDK pipeline is fully discovery-driven. Two scenarios:
| Change | What you edit | What regenerates automatically |
|---|---|---|
New endpoint in an existing group (e.g. add a @GetMapping to a controller) |
The controller, nothing else | Spec, *.gen.ts, reference docs |
| New API group (e.g. a brand-new module) | One GroupedOpenApi bean in OpenApiConfig.java |
Spec, generated SDK folder, staff.ts / parent.ts aggregators, package.json exports, reference docs |
Both happen during the pre-commit hook (or any backend test run). Nothing in ui-clients/sdk/ is hand-edited.
Naming convention for new groups¶
The aggregator generator buckets groups by spec filename suffix:
| Spec filename | Bundled into |
|---|---|
*-staff.json |
./staff only |
*-parent.json |
./parent only |
| anything else | both bundles |
system, sample |
neither (excluded) |
So a new module that's staff-only should be registered as library-staff; a shared module can be library or customer-library. No code change needed in the SDK to wire it up.
Pretty import aliases¶
By default, the import alias for a group is the camelCase of its name (canteen-staff → canteenStaff, customer-library-staff → customerLibraryStaff).
To get a shorter alias for a group, add an entry to ALIAS_OVERRIDES at the top of scripts/generate-aggregators.mjs. This is the only SDK-folder edit that's ever optional, and only for aesthetics — adding a new group does not require touching this file.
Available API groups¶
| Import alias | API group | Bundle |
|---|---|---|
auth |
platform-auth |
Both |
audit |
platform-audit |
Both |
chat |
platform-chat |
Both |
engagement |
platform-engagement |
Both |
notifications |
platform-notifications |
Both |
wallet |
customer-wallet |
Both |
canteenStaff |
canteen-staff |
./staff |
attendanceStaff |
attendance-staff |
./staff |
school |
school-staff |
./staff |
academicReport |
customer-academic-report-staff |
./staff |
invoice |
customer-invoice-staff |
./staff |
canteenParent |
canteen-parent |
./parent |
attendanceParent |
attendance-parent |
./parent |
schoolParent |
school-parent |
./parent |
This table is informational — the bundle/alias for each group is derived at generation time from the rules above, not maintained here.
Setup¶
cd ui-clients/sdk
npm install
Regenerating the SDK¶
The pre-commit hook regenerates everything when backend files are staged, so manual regen is rarely needed. When you do need it:
# Manual full sync (requires backend running)
cd backend && APP_SEED_DATA=true ./gradlew bootRun --continuous
# Then in another terminal
cd ui-clients/sdk
npm run sync # fetches specs + regenerates TypeScript + aggregators + exports
Individual steps:
| Script | What it does | Requires backend? |
|---|---|---|
npm run fetch-specs |
Discovers groups via /v3/api-docs/swagger-config, downloads each one to specs/ |
Yes |
npm run generate |
Runs openapi-ts per spec, then regenerates staff.ts, parent.ts, and package.json exports |
No |
npm run sync |
Runs both steps in sequence | Yes |
How it works¶
- Backend exposes OpenAPI specs at
GET /v3/api-docs/{group}via springdoc-openapi. The full group list is atGET /v3/api-docs/swagger-config. - Spec export. Either
OpenApiSpecExportTest(duringgradle test) orscripts/fetch-specs.sh(against a running backend) writes one JSON file per group intospecs/. Both discover the group list dynamically — no hardcoded list. - Per-spec codegen.
scripts/generate.shglobsspecs/*.jsonand runs@hey-api/openapi-tsfor each, producingsrc/<group>/{client,types,sdk,zod}.gen.ts. - Aggregator + exports generation.
scripts/generate-aggregators.mjsthen:- Buckets groups via the
*-staff/*-parent/ shared naming rule. - Writes
src/staff.tsandsrc/parent.ts(both marked@generated). - Rewrites the
package.jsonexportsfield with one subpath per group per generated file (client.gen,types.gen,sdk.gen,zod.gen, pluscore/auth.gen).
- Buckets groups via the
- Doc generation.
scripts/generate-sdk-docs.py(in the repo rootscripts/) discovers groups from the samespecs/*.jsonglob, applies the same suffix convention, and regeneratesdocs/sdk/{staff,parent}-reference.md. ItsALIAS_OVERRIDESmap mirrors the one ingenerate-aggregators.mjs— keep them in sync if you add an alias override. - Both
specs/andsrc/are committed — frontend developers don't need the backend running to build.
The pre-commit hook orchestrates steps 2 → 5 whenever backend files are staged. See .githooks/pre-commit.
Adding a new API module — full walkthrough¶
-
Backend. Add a new
GroupedOpenApibean inOpenApiConfig.java:@Bean public GroupedOpenApi libraryStaffGroup() { return GroupedOpenApi.builder() .group("library-staff") // suffix decides the bundle .displayName("Library – Staff") .packagesToScan("com.smartsapp.system.modules.customer.library.controllers") .build(); } -
Commit. Stage the controller files and commit. The pre-commit hook will:
- Run
OpenApiSpecExportTest, which writesui-clients/sdk/specs/library-staff.json. - Run
npm run generate, which generatessrc/library-staff/, refreshessrc/staff.ts, and adds the new module's subpath exports topackage.json. - Run
generate-sdk-docs.py, which adds the module todocs/sdk/staff-reference.md. - Stage every regenerated file.
- Run
-
Use it. Frontend can immediately:
import { libraryStaff } from '@smartsapp/sdk/staff'; const books = await libraryStaff.listBooks();Or import internals directly via the auto-generated subpath exports:
import type { BookResponse } from '@smartsapp/sdk/library-staff/types.gen'; import { zCreateBookRequest } from '@smartsapp/sdk/library-staff/zod.gen';
(Optional) If the import alias is unwieldy, add 'library-staff': 'library' to ALIAS_OVERRIDES in scripts/generate-aggregators.mjs and re-run npm run generate.