This document describes the initial frontend package structure for the Co-App React project. Co-App frontend follows a basic component/api architecture.
src
├── main.jsx
├── App.jsx
├── App.css
├── index.css
│
├── api
├── assets
├── components
├── constants
├── contexts
├── hooks
├── pages
└── utils
Purpose: JavaScript files for managing Axios API requests.
This folder contains a core api.js file that is used to send api requests, along with supporting .js files for different API endpoints.
Purpose: Website assets
Contains images and other assets used by the site.
Purpose: Contains reusable components that make up the majority of the app.
Purpose: Contains constant values to be used elsewhere. Includes error message mappings.
Purpose: Contains contexts for global state management.
Purpose: Contains React hooks for logic re-use.
Purpose: Contains page components
These components will represent pages in the app, from which all of displayed content will be rendered.
Purpose: Files containing utility functions for logic reuse, that aren't React hooks.
Page routing is managed by React Router.
To define new routes:
- Decide if the route needs protection (requires the user to be authenticated)
- If the route needs protection, add it in the sub-group of routes, below the comment
{/* Protected (requires auth) routes */}in App.jsx - If the route is unprotected, add it in the main group of routes below
{/* Unprotected routes */}that includes theNotFoundPageroute
API calls in the app are made through an error handling axios wrapper formed by /api/api.js, /utils/errorUtils.js, /hooks/useApi.js and /constants/errors.js.
This wrapper serves several important functions:
- Response Interception
- Error Translation
- A hook for using API calls in components
An axios interceptor intercepts all error responses, updating the auth context when a 401 is recieved, and selecting the most appropriate fallback error message to use.
Fallback error message priority is as follows:
- Server provided error messages, the
messagefield in the below example response:
{
"error":"ACCOUNT_NOT_ACTIVATED",
"message":"The account has not yet been activated."
}- Server provided Spring default response,
errorfield in the below example response:
{
"timestamp":"xyz",
"status":404,
"error":"Not Found",
"path":"/api/path/to/endpoint"
}- The axios provided message
- Returning "Unknown Error"
getErrorMessage in /utils/errorUtils.js defines a function that will map server error messages (like ACCOUNT_NOT_ACTIVATED) to readable messages for the UI.
The function takes the thrown error from useApi, and returns an error message with the following priorities:
- Component specified overrides. If the component calling
getErrorMessagepasses in overrides, such asACCOUNT_NOT_ACTIVATED: "Some error message", those overrides will checked first. - Global error mappings defined in
/constants/errors.js, based on the string error message returned by the server - Global error mappings defined in
/constants/errors.js, based on the HTTP error code returned by the server (ex:404) - The fallback message set by the axios interceptor
GLOBAL_ERRORS.SERVER_ERRORfrom/constants/errors.js
The useApi hook in /hooks/useApi.js defines a useful wrapper for API requests returning state like response data, loading state, and error messages.
Example use of the wrapper can be seen below:
const errorMappings = {
REQUEST_HAS_NULL_OR_EMPTY_FIELD: 'Incorrect email or password',
INVALID_EMAIL_OR_PASSWORD: 'Incorrect email or password',
ACCOUNT_NOT_ACTIVATED: 'Please activate your account',
};
const LoginPage = () => {
// "login" is a function that calls /api/auth/login defined in /api/authApi.js
const { request: loginCallback } = useApi(login);
const submit = async () => {
try {
await loginCallback(email, password);
// On success code below
} catch (error) {
// Error is an instance ApiError defined in /api/api.js
// Translated error message
const message = getErrorMessage(error, errorMappings);
if (error.status === 400) {
// Per HTTP code mappings can be done like this
}
}
};
// ..... rest of component
return (
<>
{ /* Some JSX here */}
</>);
}The useApi hook also returns data, loading, error, which are state variables that will be updated as the request happens, providing an alternate way to use the hook for different use cases.