From project-setup
Sets up the openapi-fetch + openapi-typescript autogenerated API client workflow in a Vite + React + TypeScript frontend. Use this skill whenever the user wants to add or configure the typed API client in a TypeScript/React project, set up openapi-typescript type generation from an OpenAPI spec, create or update the API client with auth middleware, configure the generate script in package.json, set up env validation with Zod, or wire up type-safe fetch calls. Triggers on phrases like "set up TypeScript API client", "add openapi-fetch", "generate TypeScript API types", "setup type-safe fetch", "add auth middleware to fetch client", "configure openapi-typescript", "wire up the API in React", or when the user needs to call a backend from a Vite/React/TypeScript app.
How this skill is triggered — by the user, by Claude, or both
Slash command
/project-setup:setup-ts-api-clientThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Creates and configures a type-safe API client workflow:
Creates and configures a type-safe API client workflow:
src/libs/api.schema.g.ts (auto-generated), src/libs/api.ts (openapi-fetch client with auth middleware), src/libs/env.ts (Zod-validated env vars), the generate scripts in package.json, and CLAUDE.md documentation.
Check what already exists so you only create what's missing:
ls src/libs/env.ts src/libs/api.ts src/libs/api.schema.g.ts 2>/dev/null
node -e "const p = require('./package.json'); console.log(JSON.stringify({deps: Object.keys(p.dependencies||{}), devDeps: Object.keys(p.devDependencies||{}), scripts: Object.keys(p.scripts||{})}))"
Note which files and scripts already exist. Use these answers to skip steps that are already done rather than re-running them.
Ask (or confirm from existing files) two things before touching anything:
| Setting | Default |
|---|---|
| API base URL | http://localhost:5000 |
| Auth endpoint paths to skip (no token header + no 401 redirect) | /api/v1/auth/login, /api/v1/auth/register |
If values are already present in .env or src/libs/api.ts, show them and ask the user to confirm or override.
Only install what is not already in package.json.
Runtime dependencies (go in dependencies):
npm install openapi-fetch zod
Dev dependencies (go in devDependencies):
npm install -D openapi-typescript dotenv-cli
Check package.json first — if a package is already listed, skip its install command entirely.
For each file below, skip it entirely if it already exists. If it exists but is outdated or missing pieces, show a diff and ask before modifying.
src/libs/env.tsValidates required env vars at startup so the app fails fast on misconfiguration.
import { z } from "zod";
const envSchema = z.object({
VITE_API_URL: z.url(),
});
export const Env = envSchema.parse(import.meta.env);
src/libs/api.tsThe main typed fetch client. The auth middleware:
Authorization and skips the 401 redirect for the configured auth paths (login/register don't have a token yet)./login and clears the stored token on any 401, keeping the app in a consistent unauthenticated state.Substitute {AUTH_PATHS} with the paths collected in Step 2. Adapt the token storage key and redirect path to match the project's conventions (check CLAUDE.md or existing auth utilities if present).
import createFetchClient, { type Middleware } from "openapi-fetch";
import type { paths } from "./api.schema.g";
import { Env } from "./env";
const AUTH_SKIP_PATHS = ["{AUTH_PATH_1}", "{AUTH_PATH_2}"];
const createAuthMiddleware = (): Middleware => ({
async onRequest({ request }) {
if (AUTH_SKIP_PATHS.some((path) => request.url.includes(path))) {
return request;
}
const token = localStorage.getItem("auth_token");
if (token) {
request.headers.set("Authorization", `Bearer ${token}`);
}
return request;
},
async onResponse({ request, response }) {
if (AUTH_SKIP_PATHS.some((path) => request.url.includes(path))) {
return response;
}
if (response.status === 401) {
localStorage.removeItem("auth_token");
window.location.href = "/login";
}
return response;
},
});
const baseApi = createFetchClient<paths>({ baseUrl: Env.VITE_API_URL });
baseApi.use(createAuthMiddleware());
export const Api = {
GET: baseApi.GET.bind(baseApi),
POST: baseApi.POST.bind(baseApi),
PUT: baseApi.PUT.bind(baseApi),
DELETE: baseApi.DELETE.bind(baseApi),
PATCH: baseApi.PATCH.bind(baseApi),
};
If the project has dedicated auth utilities (e.g. an
Authhelper orStorageKeysconstants), import from those instead of callinglocalStoragedirectly.
package.jsonSkip if both scripts are already present.
Add to the scripts section of package.json:
"generate:api": "npx openapi-typescript@latest $VITE_API_URL/openapi/v1.json -o ./src/libs/api.schema.g.ts",
"generate": "npx dotenv-cli -v NODE_TLS_REJECT_UNAUTHORIZED=0 -- npm run generate:api"
generate:api — fetches the OpenAPI spec from $VITE_API_URL/openapi/v1.json and writes typed definitions to src/libs/api.schema.g.ts.generate — injects env vars from .env (via dotenv-cli) before running generate:api. The NODE_TLS_REJECT_UNAUTHORIZED=0 flag allows self-signed certs in local dev.The OpenAPI spec path (
/openapi/v1.json) may vary by backend framework. Confirm with the user if uncertain.
.env (if missing)If no .env exists, create one with the API URL from Step 2:
VITE_API_URL=http://localhost:5000
If .env exists but lacks VITE_API_URL, append the line. Never overwrite an existing .env without showing the user what will change.
Also confirm .env is in .gitignore — if not, remind the user to add it.
Skip if src/libs/api.schema.g.ts already exists and looks up to date.
Run the generate script (requires the backend to be running at VITE_API_URL):
npm run generate
If the backend is not running, explain that the user needs to start it first and re-run npm run generate. The generated file (src/libs/api.schema.g.ts) should never be hand-edited — it is fully replaced on each run.
Append the section below to CLAUDE.md in the project root (create if absent). Check first — if a section mentioning api.schema.g.ts or npm run generate is already present, skip or ask before updating.
## API Client
`src/libs/api.ts` is a typed fetch client built on `openapi-fetch`. All API calls go through `Api.GET`, `Api.POST`, etc.
`src/libs/api.schema.g.ts` is **auto-generated** from the backend's OpenAPI spec — do not hand-edit it.
**Regenerate after**: adding, removing, or changing any backend endpoint.
**How to regenerate** (backend must be running):
\```bash
npm run generate
\```
The token storage key and 401 redirect path are configured in `src/libs/api.ts`. If the project has auth utilities, the middleware imports from those instead.
End with a concise table of everything done or skipped:
✅ Installed openapi-fetch, zod
✅ Installed openapi-typescript, dotenv-cli (dev)
✅ Created src/libs/env.ts
✅ Created src/libs/api.ts
✅ Added generate / generate:api scripts to package.json
✅ Created .env
✅ Generated src/libs/api.schema.g.ts
✅ Updated CLAUDE.md
Next step: import { Api } from "@/libs/api" and start making type-safe calls.
Regenerate types after any backend endpoint change: npm run generate
Use ⏭ Skipped for anything that was already in place.
npx claudepluginhub damian0401/dev-skills --plugin project-setupProvides a checklist for code reviews covering functionality, security, performance, maintainability, tests, and quality. Use for pull requests, audits, team standards, and developer training.