Automatic 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 typed- Clean, flat TypeScript interfaces from your Strapi schema
- Typed API client —
find,findOne,create,update,delete - Built-in upload plugin support —
client.upload.upload/find/findOne/destroy - Built-in i18n support —
client.i18n.locales()for the configured locales - Typed errors with
isStrapiErrorOffor discriminated narrowing - Session auth (Strapi 5.43+
jwtManagement: 'refresh') — auto-detected, with transparent token refresh - Automatic type inference for
populate— no manual casting, relation counts included ({ count: true }) - Nested populate with unlimited depth
- Separate Input types for create/update (relations as IDs)
- Optional Zod 4 validators for every create/update input (
--validation zod) — same rules as the live Strapi API - Creator fields —
createdBy/updatedByfor content types that setpopulateCreatorFields - DynamicZone support with union types
- Components and nested components
- Entity-specific filter types
- Next.js integration (
withStrapiTypes, cache, revalidate, tags) - Schema hashing — skips regeneration when nothing changed
- Framework-agnostic — works with any TypeScript project
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.
- Strapi v5
- Node.js >= 22
Full documentation is available at boxlab-ltd.github.io/strapi-typed-client:
- Getting Started — installation, plugin setup, first generation
- CLI Commands — generate, check, watch
- Client Usage — CRUD operations, error handling
- Populate — type inference, nested populate
- Filtering & Sorting — filters, pagination, field selection
- Next.js Integration — auto-generation, cache options
- Plugin Config — requireAuth, endpoints, schema hashing
- API Reference — full StrapiClient API
The docs are published in agent-friendly formats so AI coding assistants answer questions about this library accurately:
-
llms.txt —
/llms.txtand/llms-full.txt. Add the docs roothttps://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