Skip to content

Typed TypeScript Client for Strapi 5 OpenAPI in Next.js

Learn step‑by‑step how to export Strapi 5’s OpenAPI spec, generate a type‑safe TypeScript client with swagger‑typescript‑api, and hook it into a Next.js 13 (React 18, Node 20) project using Axios and SWR.

By the Revisionary team · · 4 min read

MacBook Pro showing programming language
Photo by Emile Perron on Unsplash

Strapi 5 can emit an OpenAPI 3.1 specification for its Content API. By feeding that spec into swagger‑typescript‑api, you get a fully typed TypeScript client that works out of the box with Axios. The following guide shows how to generate the spec, create the client, and consume it in a Next.js 13 application built with React 18, Node 20, TypeScript 5, SWR, and server‑action‑aware request handling.

1. Export the OpenAPI spec from Strapi

Strapi includes an experimental CLI command that writes a JSON OpenAPI document.

bash
# From the Strapi project root
yarn strapi openapi generate --output ./docs/api-spec.json
  • yarn strapi openapi generate is the official command (see the Strapi CLI docs). --output lets you place the file wherever your front‑end repository can read it. The generated file contains all CRUD routes, custom routes, authentication endpoints, and plugin extensions. A typical header looks like this:
json
{
  "openapi": "3.1.0",
  "x-powered-by": "strapi",
  "x-strapi-version": "5.21.0",
  "info": {
    "title": "My Strapi API",
    "version": "1.0.0"
  }
}

2. Add the generator to your front‑end repo

In the Next.js project, install the generator and its peer dependencies.

bash
npm install swagger-typescript-api axios swr js-cookie
# or with yarn
yarn add swagger-typescript-api axios swr js-cookie

Create a script that runs the generator against the spec file.

json
// package.json
{
  "scripts": {
    "generate:api": "swagger-typescript-api generate --path ./docs/api-spec.json --output ./src/api --axios"
  }
}

Running npm run generate:api (or yarn generate:api) produces a folder src/api with two main artifacts:

  • api.ts – an Api class that wraps an AxiosInstance pre‑configured with the base URL and type‑safe method signatures.
  • models.ts – TypeScript interfaces for every schema defined in the OpenAPI spec (e.g., Article, User, Meta).

Because the client is regenerated from the spec on each CI build, any breaking change in Strapi will surface as a type error before the code reaches production.

3. Configure the generated client for Next.js

3.1 Centralised Axios instance

Create a tiny wrapper that injects the JWT token from cookies. This works for both server and client components.

ts
// src/lib/strapiClient.ts
import { Api } from '@/api';
import Cookies from 'js-cookie';

export const createStrapiClient = () => {
  const baseURL = process.env.NEXT_PUBLIC_STRAPI_URL ?? 'http://localhost:1337';
  const token = typeof window !== 'undefined' ? Cookies.get('token') : undefined;

  return new Api({
    baseURL,
    headers: token ? { Authorization: `Bearer ${token}` } : undefined,
    // optional: enable request/response logging in dev
    ...(process.env.NODE_ENV === 'development' && {
      requestInterceptor: (cfg) => {
        console.log('Strapi request →', cfg.method?.toUpperCase(), cfg.url);
        return cfg;
      },
    }),
  });
};

The Api constructor accepts any Axios configuration object, so you can add interceptors, timeout, or custom adapters as needed.

3.2 Server‑side helper (getStaticProps / server actions)

Server‑only code can read the JWT directly from the cookies() Next.js API (available in server components and server actions).

ts
// src/app/profile/page.tsx (server component)
import { createStrapiClient } from '@/lib/strapiClient';
import { cookies } from 'next/headers';

export default async function ProfilePage() {
  const token = cookies().get('token')?.value;
  const client = createStrapiClient();
  // Override token for this request only
  client.instance.defaults.headers.Authorization = token ? `Bearer ${token}` : '';

  const { data } = await client.v1.usersControllerGetProfileV1();
  return (
    <section>
      <h1>Profile</h1>
      <p>Name: {data.username}</p>
      <p>Email: {data.email}</p>
    </section>
  );
}

3.3 Client‑side data fetching with SWR

For interactive pages you typically want caching and revalidation. SWR works nicely with the typed client.

tsx
// src/components/ArticleList.tsx
'use client';
import useSWR from 'swr';
import { createStrapiClient } from '@/lib/strapiClient';

const client = createStrapiClient();

export default function ArticleList() {
  const { data, error, isLoading } = useSWR(
    '/articles',
    () => client.v1.articlesControllerGetAllArticles().then((res) => res.data)
  );

  if (isLoading) return <p>Loading…</p>;
  if (error) return <p>Failed to load articles.</p>;

  return (
    <ul>
      {data?.map((article) => (
        <li key={article.id}>{article.title}</li>
      ))}
    </ul>
  );
}

Because the function returns a Promise of the typed response, SWR’s generic inference preserves the Article[] type, giving you full IntelliSense in the component.

4. CI/CD pipeline integration

Add the spec generation and client regeneration steps to your CI workflow so the front‑end always matches the back‑end.

yaml
# .github/workflows/deploy.yml
name: Deploy
on:
  push:
    branches: [main]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Install Node
        uses: actions/setup-node@v3
        with:
          node-version: '20'
      - name: Install dependencies (frontend)
        run: yarn install --cwd ./frontend
      - name: Generate OpenAPI spec from Strapi
        run: |
          cd ./backend
          yarn install
          yarn strapi openapi generate --output ../frontend/docs/api-spec.json
      - name: Regenerate TypeScript client
        run: |
          cd ./frontend
          yarn generate:api
      - name: Run type check & tests
        run: |
          cd ./frontend
          yarn typecheck
          yarn test
      - name: Build Next.js app
        run: |
          cd ./frontend
          yarn build

If a change in Strapi adds, removes, or renames an endpoint, the generated client will change accordingly. The subsequent yarn typecheck step guarantees that any mismatched usage in the codebase is caught early.

lines of HTML codes
Photo by Florian Olivo on Unsplash

5. Testing the generated client

Because the client’s method signatures are derived from the OpenAPI schema, you can write type‑level tests that compile‑time fail when the spec diverges.

ts
// tests/api/articles.test.ts
import { createStrapiClient } from '@/lib/strapiClient';

test('fetch all articles returns Article[]', async () => {
  const client = createStrapiClient();
  const { data } = await client.v1.articlesControllerGetAllArticles();
  // TypeScript will enforce that data is Article[]
  expect(Array.isArray(data)).toBe(true);
  // Optional runtime sanity check
  expect(data[0]).toHaveProperty('title');
});

If the backend changes the shape of Article (e.g., adds a required publishedAt field), the test will still compile, but your component code that consumes Article may need updates – the TypeScript compiler will point those out.

6. Handling versioned content with Revisionary (optional)

If your Strapi project uses the Revisionary plugin, the OpenAPI spec already includes the version‑aware routes (/api/articles?publicationState=preview). The generated client will expose those as separate method signatures, letting you request drafts, published versions, or specific revisions without additional code.

For more on how Revisionary extends the Content API, see the Content API guide in the Strapi docs /docs/reference/content-api.

7. Deploy and verify

  1. Deploy the Strapi backend (ensure the OpenAPI spec is part of the build artefacts).
  2. Deploy the Next.js app.
  3. Open a page that uses the generated client.
  4. Open the browser console – you should see request logs if you enabled the dev interceptor.
  5. Inspect the network tab – responses are typed, and any 4xx/5xx errors surface as thrown exceptions you can catch in your components.

8. Common pitfalls

SymptomLikely causeFix
api object is undefinedGenerated files not compiled or path mismatchVerify import { Api } from '@/api'; matches the output folder.
Type errors after a Strapi upgradeSpec regenerated but client not re‑generatedRun yarn generate:api locally or add it to CI.
Auth headers missing on client‑side fetchCookie library not loaded or token name differsEnsure js-cookie reads the same cookie name you set after login.
404 on /api/openapi.json in productionServer config blocks the routeAdd the openapi key to config/server.ts and enable public access.

9. Summary

  • Export the OpenAPI spec with strapi openapi generate.
  • Generate a typed Axios client using swagger-typescript-api.
  • Wrap the client in a helper that injects the JWT token for both server and client contexts.
  • Consume the client directly in server components, server actions, or SWR‑based client components.
  • Integrate the generation step into your CI/CD pipeline to keep front‑end types in lockstep with Strapi changes.

Following these steps gives you a maintainable, type‑safe bridge between Strapi 5 and a modern Next.js codebase, eliminating manual request‑hand‑coding and reducing runtime bugs.

TagsStrapiOpenAPITypeScriptNext.jsAxiosSWR

Sources

  1. Strapi OpenAPI documentation
  2. Strapi CLI reference
  3. swagger‑typescript‑api npm package
  4. Swagger‑TypeScript‑API README
  5. Generating an Axios client with swagger‑typescript‑api
  6. Strapi Content API overview

The Revisionary team

Developers who build Revisionary, a version history plugin for Strapi 5. We write about Strapi, web development, AI and application security.

This article was written with the help of AI.

All articles