TypeScript types for the DHIS2 REST API, generated from the official OpenAPI specification.
Covers the last four supported DHIS2 API versions: v40, v41, v42, v43.
npm install --save-dev @dhis2/api-typesImport types directly by name, or use the components["schemas"] namespace:
// Named imports — every schema in the spec is exported directly
import type { DataElement, OrganisationUnit, ValueType } from "@dhis2/api-types"
// Namespace imports — useful when you need paths/operations types too
import type { components, paths } from "@dhis2/api-types"
type DataElement = components["schemas"]["DataElement"] // identical to the named import
// Pin to a specific DHIS2 version (default resolves to latest, currently v43)
import type { DataElement } from "@dhis2/api-types/v42"
// Endpoint request/response types (only available via namespace)
type GetDataElementsParams = paths["/dataElements"]["get"]["parameters"]
type DataElementResponse =
paths["/dataElements"]["get"]["responses"][200]["content"]["application/json"]Pair with openapi-fetch for fully type-safe API calls:
import createClient from "openapi-fetch"
import type { paths } from "@dhis2/api-types" // paths is only in the namespace import
const client = createClient<paths>({ baseUrl: "https://play.dhis2.org/api" })
const { data } = await client.GET("/dataElements", {
params: { query: { fields: "id,name,valueType" } },
})Import from @dhis2/api-types/utils for version-agnostic helpers that work with any version's schemas.
Types a response from DHIS2's /api/*.json?type=gist endpoint, which reduces payload size by collapsing the response:
- Array fields (collections) →
number(the total count) - Object fields (references and embedded objects) →
string(the href or serialised value) - Scalar fields (string, number, boolean, enums) → unchanged
import type { components } from "@dhis2/api-types/v43"
import type { GistModel } from "@dhis2/api-types/utils"
type DataElement = components["schemas"]["DataElement"]
type DataElementGist = GistModel<DataElement>
// dataElementGroups: BaseIdentifiableObject[] → number (count)
// categoryCombo: IdentifiableObject → string (href)
// name: string → string (unchanged)
// aggregationType: AggregationType → AggregationType (enum preserved)Narrows a model type to exactly the fields requested in a ?fields= query, including nested fields using bracket notation. Each entry in the array is one top-level field specifier.
import type { components } from "@dhis2/api-types/v43"
import type { PickWithFieldFilters } from "@dhis2/api-types/utils"
type DataElement = components["schemas"]["DataElement"]
// Flat pick — mirrors ?fields=id,name,valueType
type DEFlat = PickWithFieldFilters<DataElement, ["id", "name", "valueType"]>
// → { id?: string; name?: string; valueType?: ValueType }
// Nested pick — mirrors ?fields=id,categoryCombo[id,name]
type DEWithCombo = PickWithFieldFilters<DataElement, ["id", "categoryCombo[id,name]"]>
// → { id?: string; categoryCombo?: { id?: string; name?: string } }
// Array field nested pick — mirrors ?fields=status,dataValues[dataElement,value]
type EventPick = PickWithFieldFilters<
components["schemas"]["Event"],
["status", "dataValues[dataElement,value]"]
>
// → { status: EventStatus; dataValues?: Array<{ dataElement?: string; value?: string }> }Note: Nested picks only resolve fields that exist on the declared type. Reference fields like
categoryComboare typed asIdentifiableObject(withid,name,code, etc.), not the full concrete type. Deeper fields unavailable onIdentifiableObject(e.g.categorieswithin aCategoryCombo) are silently dropped. See ADR 0001 for background.
| Import path | DHIS2 version |
|---|---|
@dhis2/api-types |
DHIS2 2.43 (latest) |
@dhis2/api-types/v43 |
DHIS2 2.43 |
@dhis2/api-types/v42 |
DHIS2 2.42 |
@dhis2/api-types/v41 |
DHIS2 2.41 |
@dhis2/api-types/v40 |
DHIS2 2.40 |
The package major version tracks the latest included DHIS2 API version. When DHIS2 v44 ships:
v44is added,v40is dropped- Package version bumps to
44.x.y
Pin to a major version to avoid unexpected API version drops:
"@dhis2/api-types": "^43.0.0"Secrets required in GitHub Actions:
NPM_TOKEN— npm publish token with access to the@dhis2orgDHIS2_USERNAME/DHIS2_PASSWORD— credentials for the DHIS2 Play servers (defaults:admin/district)
Types are generated from OpenAPI spec snapshots stored in specs/. The specs are
fetched from DHIS2 Play servers. Never hand-edit the files in src/.
# Fetch fresh specs for all versions and regenerate types
npm run update
# Or step by step
npm run fetch-specs # updates specs/vN.json
npm run generate # regenerates src/vN.d.ts
# Target a single version
npm run fetch-specs -- --version v42 --force
npm run generate -- --version v42The publish workflow runs on two triggers, intentionally different:
Stable releases — tag-based, manual.
Pushing a v* tag from any branch publishes to the latest dist-tag on npm. The version in package.json is used as-is. This requires a deliberate action, which is the point — stable releases affect everyone who runs npm install @dhis2/api-types and should never happen by accident.
# Bump the version, commit, then tag and push
npm version patch # or minor / major
git push && git push --tagsPrerelease channels — branch-based, automatic.
Any push to the beta or alpha branches immediately publishes to the matching dist-tag on npm. The version is auto-generated as {base}-{channel}.{short-sha} (e.g. 43.0.0-beta.abc1f3a) so every commit produces a unique, traceable package version without any manual version management.
# Make changes on the beta branch — publishing is automatic on push
git checkout beta
git merge my-feature-branch
git push
# → publishes 43.0.0-beta.abc1f3a to npm@betaConsumers opt into a channel explicitly:
npm install @dhis2/api-types # stable, unaffected by prerelease activity
npm install @dhis2/api-types@beta # latest beta build
npm install @dhis2/api-types@alpha # latest alpha buildWhy this split is a good compromise.
Stable releases carry real weight — a bad publish to latest breaks every downstream project that runs npm install. Tag-based publishing forces a deliberate decision: someone has to run git tag, review what they're shipping, and push the tag intentionally. That friction is a feature, not a bug.
Prerelease channels are the opposite: speed and low ceremony matter more than caution. A branch-based trigger means merging a PR to beta is all it takes to make types available for integration testing — no context-switching to create a tag, no deciding on a prerelease version number. The SHA-stamped version also means you can always trace a @beta install back to the exact commit it came from.
The result: latest stays stable and trustworthy, while beta and alpha move fast and stay current with in-progress work.
- Run
npm run updateand review the diff inspecs/andsrc/ - Bump the version in
package.json(npm version patch|minor|major) - Commit, push, then tag:
git push && git push --tagsThe regenerate workflow also runs monthly on a schedule and opens a PR automatically if any specs have changed.
- Add an entry to
scripts/versions.ts - Add the new version to
package.jsonexports andtypesVersions; remove the oldest - Update
src/latest.d.tsto re-export the new version - Update the publish workflow's type check list
- Run
npm run update