Skip to Content
Project ModuleArchitecture

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: The main API client that dynamically uses the active strategy.

View code

api-client/index.ts
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: Sets the global strategy and provides the guards.

View code

auth/AuthProvider.tsx
"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 endpoints (Refresh, Signup, Login, Logout).

View code

definitions/authentication.ts
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: React Query wrapper hooks.

View code

generated/index.ts
// 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: Managed login/logout state and events.

View code

hooks/useAuth.ts
// @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

View code

providers/QueryProvider.tsx
"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

types/authentication/post_authLogin.ts
export type post_authLogin = unknown;
types/authentication/post_authLogout.ts
export type post_authLogout = unknown;
types/authentication/post_authRefresh.ts
export type post_authRefresh = unknown;
types/authentication/post_authSignup.ts
export type post_authSignup = unknown;

View Todo Types

types/todos/get_todos.ts
export type get_todos = unknown;
types/todos/post_todos.ts
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: Centralized authentication configuration.

View code

user-config/auth.ts
// @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: The main barrel export.

View code

index.ts
import { authenticationApi } from "./definitions/authentication"; import { todosApi } from "./definitions/todos"; export const api = { ...authenticationApi, ...todosApi, };
Last updated on