Skip to content

Commit eb04904

Browse files
committed
Align the React API documentation with the templates
1 parent e383f0c commit eb04904

4 files changed

Lines changed: 57 additions & 37 deletions

File tree

docs/en/Community-Articles/2026-06-08-getting-started-with-the-abp-react-template/Post.md

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
ABP has supported multiple UI approaches for a long time, but many teams building line-of-business apps have been waiting for a first-class React option that feels native to the framework instead of bolted on. That is exactly what the ABP React template brings.
22

3-
If you are already using ABP for application services, modules, authentication, multi-tenancy, and code generation, the React template gives you a modern frontend stack without forcing you to hand-wire the same infrastructure in every project. You get React + TypeScript, a sensible project structure, generated API clients, authentication, localization, permission-aware UI, and a prebuilt admin experience that matches how ABP applications are typically built.
3+
If you are already using ABP for application services, modules, authentication, multi-tenancy, and code generation, the React template gives you a modern frontend stack without forcing you to hand-wire the same infrastructure in every project. You get React + TypeScript, a sensible project structure, typed Axios API modules, authentication, localization, permission-aware UI, and a prebuilt admin experience that matches how ABP applications are typically built.
44

55
This article explains what the ABP React template is, how it is structured, what you get out of the box, where it fits well, and what to watch for before adopting it.
66

@@ -158,49 +158,49 @@ In many projects, teams secure the backend correctly but forget to make the fron
158158

159159
The ABP React template reduces that mismatch.
160160

161-
## API integration without hand-written client boilerplate
161+
## API integration with typed Axios modules
162162

163-
One of the most useful parts of the template is the generated API client approach.
163+
The main React application organizes its application-specific backend calls in typed modules under `src/lib/api/`. These modules define the DTO interfaces and call the backend through a shared Axios instance that centralizes authentication, tenant and language headers, and common 401/403 handling.
164164

165-
ABP can generate frontend API clients from OpenAPI definitions, so your React app consumes backend endpoints using generated contracts instead of duplicated DTO definitions or hand-written fetch code.
165+
The Web React template does not generate these modules from OpenAPI. When a backend contract changes, update the matching DTOs and functions under `src/lib/api/`, update their callers, and run the TypeScript build to catch mismatches.
166166

167167
### Why this is a big deal
168168

169-
Without generated clients, frontend/backend integration often drifts over time:
169+
Keeping the API calls in typed modules gives the application one place to maintain each backend integration:
170170

171-
- DTOs change but frontend types do not
172-
- query strings are built inconsistently
173-
- error handling varies by developer
174-
- service layers become repetitive
171+
- components do not build request URLs themselves
172+
- DTOs and request functions stay together
173+
- authentication and tenant headers use the shared Axios client
174+
- TanStack Query remains focused on fetching, caching, and invalidation
175175

176-
With the ABP React template, Axios is already set up and typically used together with TanStack Query. That gives you a cleaner pattern for data fetching, caching, invalidation, and loading states.
176+
With the ABP React template, Axios is already set up and typically used together with TanStack Query. That gives you a clean pattern for data fetching, caching, invalidation, and loading states.
177177

178178
A simplified example looks like this:
179179

180180
```tsx
181181
import { useQuery } from '@tanstack/react-query';
182-
import { identityUserControllerGetList } from '@/client';
182+
import { getUsers } from '@/lib/api/identity';
183183

184184
export function UsersPage() {
185185
const query = useQuery({
186186
queryKey: ['users'],
187-
queryFn: () => identityUserControllerGetList({ maxResultCount: 10, skipCount: 0 }),
187+
queryFn: () => getUsers({ maxResultCount: 10, skipCount: 0 }),
188188
});
189189

190190
if (query.isLoading) return <div>Loading...</div>;
191191
if (query.isError) return <div>Failed to load users.</div>;
192192

193193
return (
194194
<ul>
195-
{query.data.items.map((user) => (
195+
{query.data?.items.map((user) => (
196196
<li key={user.id}>{user.userName}</li>
197197
))}
198198
</ul>
199199
);
200200
}
201201
```
202202

203-
The exact generated function names may vary based on your solution, but the pattern is the point: use generated contracts, wrap them with TanStack Query, and keep components focused on UI.
203+
The available modules vary based on the features selected for the solution. Keep application-specific backend calls in `src/lib/api/`, wrap them with TanStack Query, and keep components focused on UI.
204204

205205
## UI system and customization model
206206

@@ -406,7 +406,7 @@ Use it when:
406406
- you are starting a new ABP project with a modern template
407407
- you want React + TypeScript with ABP conventions already wired in
408408
- you need authentication, permissions, localization, and multi-tenancy from day one
409-
- you want generated API clients instead of duplicated DTOs
409+
- you want typed API modules with a shared Axios client
410410
- you prefer source-owned UI components
411411
- your app is admin-heavy, form-heavy, or module-heavy
412412

@@ -470,7 +470,7 @@ A plain React starter gives you flexibility, but also leaves many critical conce
470470
Compared to a generic starter, ABP gives you tighter integration for:
471471

472472
- auth and authorization
473-
- generated API clients
473+
- typed API modules
474474
- localization
475475
- tenant-aware applications
476476
- modular backend alignment
@@ -483,14 +483,14 @@ That makes it less minimal than a blank React scaffold, but much more useful for
483483

484484
The ABP React template is not interesting because it says React on the label. It is interesting because it brings React into ABP's application model in a way that feels intentional.
485485

486-
You get a modern frontend stack, source-owned customization, generated client integration, and the ABP features many teams actually need in production: permissions, localization, multi-tenancy, and admin tooling.
486+
You get a modern frontend stack, source-owned customization, typed API integration, and the ABP features many teams actually need in production: permissions, localization, multi-tenancy, and admin tooling.
487487

488488
If your team already values ABP on the backend and wants React on the frontend, this template is one of the fastest ways to get to a serious foundation without spending the first sprint rebuilding plumbing.
489489

490490
## TL;DR
491491

492492
- The ABP React template is available in ABP's modern template system, not classic templates.
493493
- It uses a practical stack: React, TypeScript, Vite, TanStack Router/Query, shadcn/ui, Tailwind, Zod, and Axios.
494-
- Key strengths are generated API clients, OIDC auth, permission-aware UI, localization, and multi-tenancy.
494+
- Key strengths are typed API modules, OIDC auth, permission-aware UI, localization, and multi-tenancy.
495495
- The frontend is source-owned, which gives you flexibility but also requires discipline.
496-
- It is a strong choice for ABP-based business apps, especially admin-heavy and SaaS-style applications.
496+
- It is a strong choice for ABP-based business apps, especially admin-heavy and SaaS-style applications.

docs/en/framework/ui/react/environment-variables.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ The template loads `/dynamic-env.json` first and then tries `/getEnvConfig` for
6262
| `oAuthConfig.clientId` | OpenIddict client ID. The main React app uses `<ProjectName>_App`. |
6363
| `oAuthConfig.scope` | OAuth scopes requested by the SPA. |
6464
| `apis.default.url` | Backend API base URL. In microservice solutions, this normally points to the Web Gateway. |
65-
| `apis.default.rootNamespace` | Root namespace used by generated API code and module-specific clients. |
65+
| `apis.default.rootNamespace` | Root namespace populated by the solution template. The current React applications do not read this value. |
6666
| `adminConsoleUrl` | Origin of the Admin Console app. The React template uses it to open `/admin-console`. |
6767

6868
The `DynamicEnv` type also includes fields such as `production`, `oAuthConfig.requireHttps`, `oAuthConfig.responseType`, `oAuthConfig.strictDiscoveryDocumentValidation`, and `oAuthConfig.skipIssuerCheck`. The template's OIDC setup always uses the Authorization Code flow by setting `responseType` to `code`.

docs/en/framework/ui/react/http-requests.md

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ Use this instance for application API modules instead of creating new Axios clie
5757
Before each request, the template:
5858

5959
- Sets `baseURL` from runtime configuration.
60-
- Adds `Authorization: Bearer <token>` from the OIDC user.
60+
- Gets the OIDC access token through `ensureAccessToken`, silently renewing a missing or expired token when a refresh token is available, and adds any returned token as `Authorization: Bearer <token>`.
6161
- Adds `__tenant` when the user has selected a tenant.
6262
- Adds `Accept-Language` from i18next.
6363
- Keeps default AJAX headers such as `X-Requested-With`.
@@ -66,9 +66,9 @@ Before each request, the template:
6666
api.interceptors.request.use(async (config) => {
6767
config.baseURL = getApiBaseUrl()
6868

69-
const user = await userManager.getUser()
70-
if (user?.access_token) {
71-
config.headers.Authorization = `Bearer ${user.access_token}`
69+
const accessToken = await ensureAccessToken()
70+
if (accessToken) {
71+
config.headers.Authorization = `Bearer ${accessToken}`
7272
}
7373

7474
const tenantId = sessionStorage.getItem('abp_tenant_id')
@@ -89,8 +89,8 @@ api.interceptors.request.use(async (config) => {
8989

9090
The response interceptor handles common authorization failures:
9191

92-
- `401 Unauthorized`: redirects to login unless `skipAuthRedirect` is set.
93-
- `403 Forbidden`: redirects to `/403` unless `skip403Redirect` is set.
92+
- `401 Unauthorized`: unless `skipAuthRedirect` is set, tries to refresh the access token and retries the request once. If the token cannot be refreshed, it redirects to login. With `skipAuthRedirect`, the original error is rejected to the caller.
93+
- `403 Forbidden`: redirects non-mutating requests to `/403` unless `skip403Redirect` is set. Mutation errors are rejected so TanStack Query or the caller can handle them.
9494
- Other errors are rejected so the caller can handle them.
9595

9696
```ts
@@ -99,12 +99,17 @@ api.interceptors.response.use(
9999
async (error) => {
100100
const status = error.response?.status
101101

102-
if (status === 401 && !error.config?.skipAuthRedirect) {
103-
await userManager.signinRedirect()
104-
return Promise.reject(new Error('Unauthorized - redirecting to login'))
102+
const config = error.config
103+
104+
if (status === 401 && !config?.skipAuthRedirect) {
105+
return handleUnauthorizedResponse(error)
105106
}
106107

107-
if (status === 403 && !error.config?.skip403Redirect) {
108+
if (
109+
status === 403 &&
110+
!config?.skip403Redirect &&
111+
!isMutatingRequest(config?.method)
112+
) {
108113
window.location.href = '/403'
109114
return Promise.reject(new Error('Forbidden'))
110115
}
@@ -134,11 +139,20 @@ export interface BookDto {
134139
price: number
135140
}
136141

137-
export async function getBooks(): Promise<PagedResultDto<BookDto>> {
142+
export interface PagedAndSortedResultRequestDto {
143+
maxResultCount?: number
144+
skipCount?: number
145+
sorting?: string
146+
}
147+
148+
export async function getBooks(
149+
params: PagedAndSortedResultRequestDto = {}
150+
): Promise<PagedResultDto<BookDto>> {
138151
const { data } = await api.get<PagedResultDto<BookDto>>('/app/book', {
139152
params: {
140-
maxResultCount: 10,
141-
skipCount: 0,
153+
maxResultCount: params.maxResultCount ?? 10,
154+
skipCount: params.skipCount ?? 0,
155+
sorting: params.sorting,
142156
},
143157
})
144158
return data
@@ -202,11 +216,17 @@ const productsQuery = useQuery({
202216
})
203217
```
204218

205-
## Keeping API Modules in Sync
219+
## Keeping the Main React SPA's API Modules in Sync
220+
221+
The main developer-owned React SPA lives under `react/` in layered and single-layer solutions, and under `apps/react/` in microservice solutions. Its application-specific typed API modules are maintained under `src/lib/api/`.
222+
223+
These instructions apply to the main developer-owned React SPA. They do not describe the React Public Web app, the Admin Console, React Native clients, or API calls implemented inside Low-Code packages.
224+
225+
`abp generate-proxy` has no React target. Its `-t js` generator produces jQuery proxy scripts for MVC / Razor Pages applications, must be run from a directory containing a top-level `.csproj` file, and writes scripts that use `abp.ajax` and `$` to `wwwroot/client-proxies/<module>-proxy.js` by default. It does not generate the TypeScript / Axios modules used by the React application.
206226

207-
`abp generate-proxy` has no React target. Its `-t js` generator produces jQuery proxy scripts for MVC / Razor Pages applications, and it must run in a folder that contains a Web project file, so it does not apply to the React application. Update the modules under `src/lib/api/` yourself when a backend contract changes:
227+
Update the modules under `src/lib/api/` yourself when a backend contract changes:
208228

209-
1. Start the backend and check the new contract on its Swagger UI or `/api/abp/api-definition?includeTypes=true`.
229+
1. Start the backend that owns the application service and check the new contract on its Swagger UI or `/api/abp/api-definition?includeTypes=true`. In a microservice solution, use the owning service's entry in the Web Gateway Swagger UI, or call that service's `/api/abp/api-definition?includeTypes=true` endpoint directly. By default, the generated Web Gateway routes `/api/abp/*` to the Administration service, so its gateway URL does not expose the API-definition models of the other services.
210230
2. Update the DTO interfaces and function signatures in the matching module.
211231
3. Update the callers and run `npm run build` so TypeScript reports the mismatches.
212232

docs/en/solution-templates/microservice/mobile-applications.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ The generated React Native app is configured in `Environment.ts` with:
3737
* the `MobileGateway` base URL
3838
* the `ReactNative` client id and scopes
3939

40-
At runtime, the mobile client uses the password grant to exchange credentials for access and refresh tokens at the `AuthServer` `/connect/token` endpoint, then sends bearer tokens to backend APIs through the `MobileGateway`. Account-related operations such as registration, password reset, profile picture management, and logout use the generated API client under `src/api`.
40+
At runtime, the mobile client uses the password grant to exchange credentials for access and refresh tokens at the `AuthServer` `/connect/token` endpoint, then sends bearer tokens to backend APIs through the `MobileGateway`. Account-related operations such as registration, password reset, and profile picture management use the template-provided API client under `src/api`.
4141

4242
## Built-in Capabilities
4343

0 commit comments

Comments
 (0)