
By Dearonski
Automatic TypeScript type generation and a fully typed API client for Strapi v5 with populate type inference
released October 22, 2026
npm install strapi-typed-clientAutomatic TypeScript type generation and a fully typed API client for Strapi v5. Install a Strapi plugin, run a single CLI command, and get clean TypeScript interfaces plus a ready-to-use fetch client with full autocomplete.
npm install strapi-typed-client// config/plugins.ts
export default {
'strapi-typed-client': {
enabled: true,
},
}Scaffold the workflow (adds strapi:generate / strapi:check scripts to your package.json), then generate into your source tree and commit the result:
npx strapi-types init
npm run strapi:generateOr run the generator directly:
npx strapi-types generate --url http://localhost:1337 --output ./src/strapiCommitted types survive reinstalls and show up as reviewable diffs.
import { StrapiClient } from '@/strapi'
const strapi = new StrapiClient({
baseURL: 'http://localhost:1337',
})
const articles = await strapi.articles.find({
filters: { title: { $contains: 'hello' } },
populate: { category: true, cover: true },
})
articles[0].category.name // fully typedfind, findOne, create, update, deleteclient.upload.upload/find/findOne/destroyclient.i18n.locales() for the configured localesisStrapiErrorOf for discriminated narrowingjwtManagement: 'refresh') — auto-detected, with transparent token refreshpopulate — no manual casting, relation counts included ({ count: true })--validation zod) — same rules as the live Strapi APIcreatedBy / updatedBy for content types that set populateCreatorFieldswithStrapiTypes, cache, revalidate, tags)Add --validation zod and the generator also writes validation.ts: a Zod 4 schema for every create/update input, generated from the same model as the TS types, so the two can't drift. Install zod in your app first:
npm install zod
npx strapi-types generate --output ./src/strapi --validation zodimport { ArticleCreateInputSchema } from '@/strapi/validation'
const parsed = ArticleCreateInputSchema.safeParse(formData)
if (parsed.success) await strapi.articles.create(parsed.data)The rules mirror what a live Strapi 5 actually enforces — required fields, lengths, ranges, patterns, enumerations, unknown keys, dynamic-zone blocks — so it rejects payloads Strapi would reject for their shape; what it can't know, like a unique value already taken or a missing relation target, still comes back from Strapi. Later runs keep the mode, and the schemas stay out of the client barrel so zod never lands in a bundle that doesn't import them. See Runtime validation.
When your backend runs users-permissions with jwtManagement: 'refresh' (short-lived access JWT + rotating refresh token in an httpOnly cookie), the generator detects it automatically and bakes the session flow into the client — no config needed:
const strapi = new StrapiClient({
baseURL: 'http://localhost:1337',
credentials: 'include', // let the refresh cookie travel
})
await strapi.auth.login({ identifier, password }) // access token kept in memory
await strapi.auth.refresh() // bootstrap the session on page load
await strapi.articles.find() // expired token? refreshed & retried transparently
await strapi.auth.logout() // revoke the session server-sideOn a 401 — or a 403 from an anonymous request, which is what Strapi actually answers when a protected route is hit with no credentials (the page-reload bootstrap case) — the client performs a single-flight POST /api/auth/refresh and retries the request once; a dead session surfaces the original error. The flow is browser-only (server-side code keeps using API tokens) and steps aside whenever you manage the Authorization header yourself. Legacy backends are untouched — the generated client behaves exactly as before. See Authentication for details.
Full documentation is available at boxlab-ltd.github.io/strapi-typed-client:
The docs are published in agent-friendly formats so AI coding assistants answer questions about this library accurately:
llms.txt — /llms.txt and /llms-full.txt. Add the docs root https://boxlab-ltd.github.io/strapi-typed-client/ to Cursor (@Docs), Continue (@docs), or any tool that indexes docs by URL.
context7 — indexed at /boxlab-ltd/strapi-typed-client; the docs site also embeds the context7 chat widget.
GitMCP — point any MCP-capable agent at https://gitmcp.io/BoxLab-Ltd/strapi-typed-client:
{
"mcpServers": {
"strapi-typed-client": {
"url": "https://gitmcp.io/BoxLab-Ltd/strapi-typed-client"
}
}
}DeepWiki — browse or query the auto-generated wiki at deepwiki.com/BoxLab-Ltd/strapi-typed-client, or connect its MCP at https://mcp.deepwiki.com/mcp.
Found a bug or have a feature request? Open an issue on GitHub.
Pull requests are welcome — please open an issue first to discuss what you'd like to change.
The project is MIT-licensed and free to use. If it saved your team time, there are a few ways to support it — starring the repo, reporting bugs, or a donation. Teams that need priority fixes or help with integration can reach commercial support at oss@dearonski.com.
MIT
Share your work with the community and get it listed in the Strapi ecosystem for everyone to discover and use.
Submit
