Architecture
When you run the generator, Reex API Builder creates a structured api-services directory in the src folder of your project. Understanding this structure is key to leveraging the tool effectively.
Folder Structure Diagram
Here is the folder structure that gets generated:
src/
└── api-services/
├── api-client/ # Core HTTP configuration and proxy
├── auth/ # Modular authentication strategies & guards
├── definitions/ # Your API implementations (editable)
├── generated/ # Auto-generated React Query hooks
├── hooks/ # Authentication & custom hooks
├── providers/ # Application providers (e.g. QueryProvider)
├── types/ # TypeScript interfaces
├── user-config/ # User configuration files
├── index.ts # Main barrel export
└── ...api-client/ - Core API Client
This folder contains the core Axios configuration and the interceptors.
index.ts
index.ts: The main API client that dynamically uses the active strategy.
View code
import { createApiClient } from "./core";
import { proxyTokenProvider } from "../auth/manager";
/**
* The globally configured API client instance.
* It dynamically uses the Active Strategy defined in the AuthProvider.
*/
export const apiClient = createApiClient(proxyTokenProvider);auth/ - Authentication Strategies
Provides modular authentication strategies and guards.
AuthProvider.tsx
AuthProvider.tsx: Sets the global strategy and provides the guards.
View code
"use client";
import React, { useState } from "react";
import { CookieAuthGuard } from "./cookie-auth/CookieAuthGuard";
import { NextAuthGuard } from "./next-auth/NextAuthGuard";
import { LocalStorageAuthGuard } from "./localstorage-auth/LocalStorageAuthGuard";
import { setActiveStrategy, type AuthStrategy } from "./manager";
export const AuthProvider = ({ strategy, children }: { strategy: AuthStrategy; children: React.ReactNode }) => {
useState(() => {
setActiveStrategy(strategy);
return true;
});
switch (strategy) {
case "cookie": return <CookieAuthGuard>{children}</CookieAuthGuard>;
case "next-auth": return <NextAuthGuard>{children}</NextAuthGuard>;
case "localstorage": return <LocalStorageAuthGuard>{children}</LocalStorageAuthGuard>;
default: return <LocalStorageAuthGuard>{children}</LocalStorageAuthGuard>;
}
};definitions/ - REST API Endpoints
This folder contains sample API endpoint definitions.
- Editable: You can make changes to these files to match your API.
- Two-Way Sync: Changes here are reflected in the Reex API Builder UI.
authentication.ts
authentication.ts: Authentication endpoints (Refresh, Signup, Login, Logout).
View code
import { apiClient } from "../api-client";
import { type post_authRefresh } from "../types/authentication/post_authRefresh";
import { type post_authSignup } from "../types/authentication/post_authSignup";
import { type post_authLogin } from "../types/authentication/post_authLogin";
import { type post_authLogout } from "../types/authentication/post_authLogout";
export interface AuthSignupPayload {
username: string;
password: string;
}
export interface AuthLoginPayload {
username: string;
password: string;
}
export const authenticationApi = {
/** @description Refresh access token */
post_authRefresh: (): Promise<post_authRefresh> =>
apiClient.post(`/auth/refresh`, {}),
/** @description Register a new user */
post_authSignup: (payload: AuthSignupPayload): Promise<post_authSignup> =>
apiClient.post(`/auth/signup`, payload),
/** @description Login a user */
post_authLogin: (payload: AuthLoginPayload): Promise<post_authLogin> =>
apiClient.post(`/auth/login`, payload),
/** @description Logout a user */
post_authLogout: (): Promise<post_authLogout> =>
apiClient.post(`/auth/logout`, {})
};generated/ - React Query Hooks
Auto-generated hooks based on your definitions/. These are regenerated whenever you save changes in definitions.
index.ts
index.ts: React Query wrapper hooks.
View code
// Generated file - DO NOT EDIT
import {
type QueryKey,
type UseMutationOptions,
type UseQueryOptions,
type UseQueryResult,
type DefaultError,
useMutation,
useQuery,
} from "@tanstack/react-query";
// 1. Mutation Wrapper
export const useApiMutation = <
TData = unknown,
TVariables = void,
TError = DefaultError,
TContext = unknown,
>(
mutationFn: (variables: TVariables) => Promise<TData>,
options?: Omit<
UseMutationOptions<TData, TError, TVariables, TContext>,
"mutationFn"
>,
) => {
return useMutation<TData, TError, TVariables, TContext>({
mutationFn,
...options,
});
};
// 2. Query Wrapper
export const useApiQuery = <
TQueryFnData = unknown,
TError = DefaultError,
TData = TQueryFnData,
TQueryKey extends QueryKey = QueryKey,
>(
queryKey: TQueryKey,
queryFn: () => Promise<TQueryFnData>,
options?: Omit<
UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
"queryKey" | "queryFn"
>,
): UseQueryResult<TData, TError> => {
return useQuery<TQueryFnData, TError, TData, TQueryKey>({
queryKey,
queryFn,
refetchOnWindowFocus: false,
retry: 1,
...options,
});
};
export * from "./useAuthenticationQueries";
export * from "./useTodosQueries";hooks/ - Custom Hooks
useAuth.ts
useAuth.ts: Managed login/logout state and events.
View code
// @internal — No changes needed
/* eslint-disable @typescript-eslint/no-explicit-any */
import { useCallback, useState, useEffect } from "react";
import { useMutation, type UseMutationOptions } from "@tanstack/react-query";
import { activeTokenProvider } from "../auth/manager";
import { apiClient } from "../api-client";
import { authConfig } from "../user-config/auth";
/**
* Login hook that stores tokens on success via the active provider.
*/
export const useLogin = (
options?: Omit<UseMutationOptions<any, any, any, any>, "mutationFn">,
) => {
const mutation = useMutation({
mutationFn: async (credentials: any) => {
const response = await apiClient.post(authConfig.loginUrl, credentials);
const isRawAxiosResponse =
response?.config && response?.headers && response?.status;
const unwrappedData = isRawAxiosResponse ? response.data : response;
return unwrappedData?.data ?? unwrappedData;
},
...options,
onSuccess: (data: any, variables, context) => {
const accessToken = data?.accessToken ?? data?.access_token ?? data?.token;
const refreshToken = data?.refreshToken ?? data?.refresh_token ?? data?.refresh;
activeTokenProvider?.setTokens?.({ accessToken, refreshToken });
if (typeof window !== "undefined") {
window.dispatchEvent(new Event("auth:login"));
}
if (options?.onSuccess) {
(options.onSuccess as any)(data, variables, context);
}
},
});
return {
login: mutation.mutate,
loginAsync: mutation.mutateAsync,
...mutation,
};
};
/**
* Logout hook that clears tokens and optionally calls the server logout endpoint.
*/
export const useLogout = (options?: { callServer?: boolean }) => {
const { callServer = true } = options ?? {};
const logout = useCallback(async () => {
try {
if (callServer) {
if (authConfig.logoutMethod === "POST") {
await apiClient.post(authConfig.logoutUrl);
} else {
await apiClient.get(authConfig.logoutUrl);
}
}
activeTokenProvider?.clearTokens?.();
if (typeof window !== "undefined") {
window.dispatchEvent(new Event("auth:logout"));
}
} catch (error) {
console.error("[useLogout] Logout failed:", error);
activeTokenProvider?.clearTokens?.();
if (typeof window !== "undefined") {
window.dispatchEvent(new Event("auth:logout"));
}
}
}, [callServer]);
return { logout };
};
/**
* Hook to check if the user is currently authenticated.
*/
export const useIsAuthenticated = () => {
const [isAuthenticated, setIsAuthenticated] = useState<boolean>(false);
const [isLoading, setIsLoading] = useState<boolean>(true);
useEffect(() => {
const checkAuth = async () => {
try {
const token = await activeTokenProvider?.getToken?.();
setIsAuthenticated(!!token);
} catch {
setIsAuthenticated(false);
} finally {
setIsLoading(false);
}
};
checkAuth();
const handleLogin = () => setIsAuthenticated(true);
const handleLogout = () => setIsAuthenticated(false);
if (typeof window !== "undefined") {
window.addEventListener("auth:login", handleLogin);
window.addEventListener("auth:logout", handleLogout);
}
return () => {
if (typeof window !== "undefined") {
window.removeEventListener("auth:login", handleLogin);
window.removeEventListener("auth:logout", handleLogout);
}
};
}, []);
return { isAuthenticated, isLoading };
};providers/ - Query Provider
QueryProvider.tsx
View code
"use client";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { useState } from "react";
export function QueryProvider({ children }: { children: React.ReactNode }) {
const [queryClient] = useState(() => new QueryClient());
return <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>;
}types/ - TypeScript Interfaces
View Authentication Types
export type post_authLogin = unknown;export type post_authLogout = unknown;export type post_authRefresh = unknown;export type post_authSignup = unknown;View Todo Types
export type get_todos = unknown;export type post_todos = unknown;user-config/ - User Configuration
This folder is designed specifically for you to edit. It contains constants and mappings customized to your backend settings.
auth.ts
auth.ts: Centralized authentication configuration.
View code
// @user-config — changes needed: Update routes, keys, and endpoints to match your backend
import axios from "axios";
import { baseURL } from "./constants";
/**
* Centralized Authentication Configuration
*
* Customize these values and functions to match your backend's
* authentication requirements.
*/
export const authConfig = {
/** Redirect path for unauthenticated users */
loginUrl: "/auth/login",
/** Server-side logout endpoint */
logoutUrl: "/auth/logout",
/** HTTP Method for logout endpoint (e.g., "GET", "POST") */
logoutMethod: "POST" as "GET" | "POST",
/** localStorage key for access token */
accessTokenKey: "access_token",
/** localStorage key for refresh token (if applicable) */
refreshTokenKey: "refresh_token",
/** Refresh token endpoint */
refreshEndpoint: `${baseURL}/auth/refresh`,
/** Refresh via httpOnly cookie. Edit the method/headers to match your backend. */
refreshWithCookie: async () => {
return await axios.post(authConfig.refreshEndpoint, undefined, { withCredentials: true });
},
/** Refresh via stored token payload. Edit the property name to match your backend. */
refreshWithToken: async (storedRefreshToken: string) => {
return await axios.post(authConfig.refreshEndpoint, {
refreshToken: storedRefreshToken,
});
},
};index.ts - Main Export
This file serves as the main entry point to all of your API definitions. It merges all defined API modules into a single api object.
index.ts
index.ts: The main barrel export.
View code
import { authenticationApi } from "./definitions/authentication";
import { todosApi } from "./definitions/todos";
export const api = {
...authenticationApi,
...todosApi,
};