Next.js Frontend API Client Patterns Skill
When to use this Skill
Use this Skill whenever you are:
- Creating or modifying the code that calls a backend API from a Next.js
16+ App Router frontend.
- Designing how the frontend talks to any HTTP/REST/JSON API
(FastAPI, Node, Go, etc.).
- Adding new API functions (getTasks, createTodo, updateProfile, etc.).
- Improving error handling, typing, or auth token handling for API calls.
This Skill must work for any Next.js App Router project that calls a
backend over HTTP, not just a single repo.
Core goals
- All API calls go through one central client module instead of being
scattered
fetch calls across the app.
- API requests and responses are strongly typed with TypeScript.
- Error handling is consistent and predictable for UI components.
- Auth headers (e.g. JWT Bearer tokens) are attached in one place,
not manually per call.
- The pattern is reusable across many projects with minimal changes.
File and module conventions
Place the main client in a dedicated module, for example:
src/lib/api.ts or
app/(lib)/api.ts
Adjust the exact path to match the project, but keep a single, obvious
API entrypoint.
Export functions from this module with clear names, such as:
getTasks(), createTask(payload), updateTask(id, payload),
deleteTask(id), etc.
getUserProfile(), updateUserProfile(payload), etc.
Do not call fetch or low-level HTTP functions directly from pages
or components unless there is a very strong reason. Prefer calling
the functions exposed by the API client module.
HTTP client choices
Default to the built-in fetch API in Next.js:
- Use
fetch in server components for server-side data fetching.
- Use
fetch or a small wrapper in client components when
client-side fetching is required (e.g. in hooks).
If a third-party client (axios, ky, etc.) is used, it must be
configured in a single place and then imported from there.
Do not configure clients in multiple files.
Typing requests and responses
For each API function, define TypeScript types or interfaces that
describe:
- The request payload (if any).
- The expected response shape.
Prefer importing shared types from a central types module if the
project has one; otherwise, define local types next to the API
client code.
Do not use any for API responses. If the schema is not yet stable,
start with minimal but meaningful types (e.g. Task, User, ApiError).
Error handling patterns
Wrap API calls in helper functions that translate low-level HTTP errors
into a consistent error shape for the UI.
Define a simple error model, for example:
{ message: string; status?: number; details?: unknown }
On non-2xx HTTP status codes:
- Parse the response body (if JSON) and map it to the error model.
- Throw or return a predictable error object that components can use
to show messages.
Do not scatter try/catch with custom logic in every component.
Centralize error interpretation inside the API client.
Auth and headers
Do not manually attach auth headers (e.g. Authorization: Bearer ...)
in every component.
Provide a single place in the API client where headers are built:
- For example, a helper that receives a token or session and returns
the appropriate
headers object.
- Or a wrapper function that reads the token from a trusted source
(e.g. cookies, session object) and attaches it.
Keep the pattern generic:
- The Skill should not assume a specific auth provider name.
- It may refer to “JWT Bearer token in the Authorization header” as
a common pattern.
Server vs client usage
Base URL and configuration
Store the API base URL and other configuration in a single place:
e.g. lib/config.ts with:
API_BASE_URL
- any feature flags or environment-dependent settings.
Never hard-code full URLs all over the codebase.
Central configuration makes it easy to switch between local, staging,
and production backends.
Caching and revalidation (optional)
When using Next.js data fetching in server components, respect the
project’s chosen caching strategy:
cache: "no-store" for always-fresh data.
next: { revalidate: N } for periodic revalidation.
Keep these options close to the API client so behaviour is consistent
for all callers.
Things to avoid
- Copy-pasting raw
fetch calls into many components with slightly
different error handling and headers.
- Returning raw
Response objects from the API client; prefer returning
typed data or throwing a clear error.
- Mixing multiple different HTTP client libraries in the same project.
- Hard-coding tokens, secrets, or environment-specific URLs in
components or pages.
References inside the repo
Whenever possible, this Skill should align with the project’s existing
conventions, for example:
@/lib/api.ts or similar central API client module.
@/lib/config.ts or .env-backed configuration helpers.
- Shared types under
@/types or @/lib/types.
If these files are missing, propose creating them using the patterns
described above instead of inventing a completely new API access style.
1---2name: nextjs-frontend-api-client-patterns3description: Standard patterns for HTTP/API clients in Next.js 16+ App Router frontends: where to put the client, how to type it, how to handle errors, and how to attach auth headers in a reusable way.4---5
6# Next.js Frontend API Client Patterns Skill
7
8## When to use this Skill
9
10Use this Skill whenever you are:
11
12- Creating or modifying the code that calls a backend API from a Next.js
13 16+ App Router frontend.
14- Designing how the frontend talks to any HTTP/REST/JSON API
15 (FastAPI, Node, Go, etc.).
16- Adding new API functions (getTasks, createTodo, updateProfile, etc.).
17- Improving error handling, typing, or auth token handling for API calls.
18
19This Skill must work for **any** Next.js App Router project that calls a
20backend over HTTP, not just a single repo.
21
22## Core goals
23
24- All API calls go through **one central client module** instead of being
25 scattered `fetch` calls across the app.
26- API requests and responses are **strongly typed** with TypeScript.
27- Error handling is **consistent** and predictable for UI components.
28- Auth headers (e.g. JWT Bearer tokens) are attached in one place,
29 not manually per call.
30- The pattern is **reusable** across many projects with minimal changes.
31
32## File and module conventions
33
34- Place the main client in a dedicated module, for example:
35
36 - `src/lib/api.ts` or
37 - `app/(lib)/api.ts`
38
39 Adjust the exact path to match the project, but keep a **single, obvious**
40 API entrypoint.
41
42- Export functions from this module with clear names, such as:
43
44 - `getTasks()`, `createTask(payload)`, `updateTask(id, payload)`,
45 `deleteTask(id)`, etc.
46 - `getUserProfile()`, `updateUserProfile(payload)`, etc.
47
48- Do not call `fetch` or low-level HTTP functions directly from pages
49 or components unless there is a very strong reason. Prefer calling
50 the functions exposed by the API client module.
51
52## HTTP client choices
53
54- Default to the **built-in `fetch`** API in Next.js:
55
56 - Use `fetch` in server components for server-side data fetching.
57 - Use `fetch` or a small wrapper in client components when
58 client-side fetching is required (e.g. in hooks).
59
60- If a third-party client (axios, ky, etc.) is used, it must be
61 configured in a single place and then imported from there.
62 Do not configure clients in multiple files.
63
64## Typing requests and responses
65
66- For each API function, define TypeScript types or interfaces that
67 describe:
68
69 - The request payload (if any).
70 - The expected response shape.
71
72- Prefer importing shared types from a central `types` module if the
73 project has one; otherwise, define local types next to the API
74 client code.
75
76- Do not use `any` for API responses. If the schema is not yet stable,
77 start with minimal but meaningful types (e.g. `Task`, `User`, `ApiError`).
78
79## Error handling patterns
80
81- Wrap API calls in helper functions that translate low-level HTTP errors
82 into a consistent error shape for the UI.
83
84- Define a simple error model, for example:
85
86 - `{ message: string; status?: number; details?: unknown }`
87
88- On non-2xx HTTP status codes:
89
90 - Parse the response body (if JSON) and map it to the error model.
91 - Throw or return a predictable error object that components can use
92 to show messages.
93
94- Do not scatter `try/catch` with custom logic in every component.
95 Centralize error interpretation inside the API client.
96
97## Auth and headers
98
99- Do not manually attach auth headers (e.g. `Authorization: Bearer ...`)
100 in every component.
101
102- Provide a single place in the API client where headers are built:
103
104 - For example, a helper that receives a token or session and returns
105 the appropriate `headers` object.
106 - Or a wrapper function that reads the token from a trusted source
107 (e.g. cookies, session object) and attaches it.
108
109- Keep the pattern **generic**:
110
111 - The Skill should not assume a specific auth provider name.
112 - It may refer to “JWT Bearer token in the Authorization header” as
113 a common pattern.
114
115## Server vs client usage
116
117- For **Server Components**:
118
119 - Prefer direct `fetch` calls with server-side environment variables
120 and base URLs.
121 - Use the same API client module, but ensure any client-only code
122 (window, localStorage) is not used.
123
124- For **Client Components**:
125
126 - Expose simple, typed functions or hooks (e.g. `useTasks`) that call
127 the central API client and manage loading/error state.
128
129- Do not mix server-only and client-only logic in the same file.
130 Keep server and client concerns clearly separated.
131
132## Base URL and configuration
133
134- Store the API base URL and other configuration in a single place:
135
136 - e.g. `lib/config.ts` with:
137
138 - `API_BASE_URL`
139 - any feature flags or environment-dependent settings.
140
141- Never hard-code full URLs all over the codebase.
142- Central configuration makes it easy to switch between local, staging,
143 and production backends.
144
145## Caching and revalidation (optional)
146
147- When using Next.js data fetching in server components, respect the
148 project’s chosen caching strategy:
149
150 - `cache: "no-store"` for always-fresh data.
151 - `next: { revalidate: N }` for periodic revalidation.
152
153- Keep these options close to the API client so behaviour is consistent
154 for all callers.
155
156## Things to avoid
157
158- Copy-pasting raw `fetch` calls into many components with slightly
159 different error handling and headers.
160- Returning raw `Response` objects from the API client; prefer returning
161 typed data or throwing a clear error.
162- Mixing multiple different HTTP client libraries in the same project.
163- Hard-coding tokens, secrets, or environment-specific URLs in
164 components or pages.
165
166## References inside the repo
167
168Whenever possible, this Skill should align with the project’s existing
169conventions, for example:
170
171- `@/lib/api.ts` or similar central API client module.
172- `@/lib/config.ts` or `.env`-backed configuration helpers.
173- Shared types under `@/types` or `@/lib/types`.
174
175If these files are missing, propose creating them using the patterns
176described above instead of inventing a completely new API access style.