Type-safe API usage
This guide explains how to consume the Mindoo API with full type safety using generated types from the OpenAPI specification.
The instructions below are specific to TypeScript. In other programming languages, the same principles apply.
Prerequisites
Before you begin, make sure you have:
- Completed the authentication flow and obtained a valid
id_token - A TypeScript project configured
Generating TypeScript types
The Mindoo API provides an OpenAPI specification that can be used to generate TypeScript types automatically.
Install dependencies
Install openapi-typescript and openapi-fetch:
npm i openapi-fetch
npm i -D openapi-typescript typescript
pnpm add openapi-fetch
pnpm add -D openapi-typescript typescript
yarn add openapi-fetch
yarn add -D openapi-typescript typescript
bun add openapi-fetch
bun add -D openapi-typescript typescript
Generate types from the OpenAPI spec
Run the CLI to generate type definitions:
npx openapi-typescript https://api.mindoo.ai/openapi.json -o ./src/lib/mindoo.d.ts
pnpm exec openapi-typescript https://api.mindoo.ai/openapi.json -o ./src/lib/mindoo.d.ts
yarn dlx openapi-typescript https://api.mindoo.ai/openapi.json -o ./src/lib/mindoo.d.ts
bunx openapi-typescript https://api.mindoo.ai/openapi.json -o ./src/lib/mindoo.d.ts
This creates a TypeScript definition file with all API types.
Create a type-safe client
Use openapi-fetch to create a fully typed API client:
import createClient from "openapi-fetch"
import type { paths } from "./mindoo.ts"
const mindooClient = createClient<paths>({
baseUrl: "https://api.mindoo.ai",
headers: {
Authorization: `Bearer ${idToken}`,
},
})
Making API requests
All requests are fully typed, with autocompletion for paths, parameters, and response types.
Example: creating an agent
const { data, error } = await mindooClient.POST("/agent", {
body: {
type: "AI_CONSULTATION",
name: "Cardiology Pre-Visit",
specialization: "CARDIOLOGY",
consultationAgentType: "PRE_VISIT",
},
})
if (error) {
console.error("Failed to create agent:", error)
return
}
// `data` is fully typed as the Agent response
console.log("Created agent:", data.id)
Handling future API changes
The Mindoo API uses a special %future added value pattern for enums and discriminated unions. This ensures your code remains forward-compatible when new values are added to the API.
Why %future added value exists
When writing code that handles the API response, you can use linting rules to make sure that all possible response types are handled. All enumerated properties (“enums” or string literal unions) will contain an additional property %future added value that encourages you to consider what to do when a future version of the API widens the response type, and returns a value that is not explicitly considered by the consuming code.
By properly considering what to do in the default case, your code won’t break when the Mindoo API adds a new enum member (e.g., a new specialization or status), and hence additive changes to the API won’t be considered breaking changes.
For example, the Agent type is a discriminated union:
type Agent = {
type: "AI_CONSULTATION"
id: string
name: string | null
// ... other fields
} | {
type: "%future added value"
}
When consuming an Agent-typed response, it is a good idea to handle all possible types of agents:
function handleAgent(agent: Agent) {
switch (agent.type) {
case "AI_CONSULTATION":
console.log("Handling AI consultation agent with id", agent.id)
break
default:
exhaustivenessCheck(agent.type)
console.log("Unknown agent type, skipping")
break
}
}
The exhaustivenessCheck helper function enforces that all known cases are handled:
/**
* No-op utility function to enforce exhaustive switch statements.
* TypeScript will error if a known case is not handled above.
*/
export const exhaustivenessCheck = (_: "%future added value") => undefined
Never explicitly handle %future added value as a case. It should only appear in the default branch with exhaustivenessCheck. This ensures TypeScript catches any unhandled known values.
Bad example
switch (agent.type) {
case "AI_CONSULTATION":
handleAiConsultationAgent(agent)
break
case "%future added value":
// ❌ Wrong: this case will never match; the Mindoo API will never send an
// explicit `%future added value` response. Any future unknown values will
// still fall through.
handleUnknown()
break
}
Good example
switch (agent.type) {
case "AI_CONSULTATION":
handleAiConsultationAgent(agent)
break
default: // ✅ Correct: handles all future-added cases
exhaustivenessCheck(agent.type) // Ensures that we handled all known types
handleUnknown()
break
}