React Services Folder

Creating a comprehensive and well-organized services folder in a React project is critical for keeping the codebase clean, assuring scalability, and making maintenance easier.

The services folder often contains files that handle data fetching, business logic, and interactions with external APIs.

This model separates concerns, letting your components focus on display and user interaction while the services handle data and logic.

React Services Folder: Basic Structure

1. Basic Structure

At its core, the services folder contains JavaScript or TypeScript files that control various aspects of your application’s data flow. Here’s a basic structure you may follow:

css

src/

services/

apiService.js

authService.js

userService.js

  • apiService.js: A generic service to handle all API calls. This file often includes functions to make HTTP requests using libraries like Axios or Fetch.
  • authService.js: Manages authentication logic, such as login, logout, and token management.
  • userService.js: Handles user-specific operations like fetching user details, updating profiles, etc.

2. Common Patterns

Services frequently follow the singleton design, which ensures that only one instance manages the state and data flow. This method is useful for maintaining consistency and minimizing repetitive activities.

javascript

// apiService.js

import axios from 'axios';

class ApiService {

constructor() {

this.api = axios.create({

baseURL: 'https://api.example.com',

});

}

get(endpoint, params) {

return this.api.get(endpoint, { params });

}

post(endpoint, data) {

return this.api.post(endpoint, data);

}

// Other HTTP methods...

}

const apiService = new ApiService();

export default apiService;

Factory Functions

Factory functions can dynamically generate service instances based on settings, which is beneficial for a variety of environments or endpoints.

javascript

// createApiService.js

import axios from 'axios';

const createApiService = (baseURL) => {

const api = axios.create({ baseURL });

return {

get: (endpoint, params) => api.get(endpoint, { params }),

post: (endpoint, data) => api.post(endpoint, data),

// Other HTTP methods.

export default create ApiService;

3. Advanced Usage

Interceptors in Axios allow you to change requests or answers before they are handled by them or caught. This is very useful for adding authentication tokens and managing failures on a global scale.

javascript

// apiService.js

import axios from 'axios';

class ApiService {

constructor() {

this.api = axios.create({

baseURL: 'https://api.example.com',

});

// Request interceptor

this.api.interceptors.request.use((config) => {

const token = localStorage.getItem('token');

if (token) {

config.headers.Authorization = `Bearer ${token}`;

}

return config;

}, (error) => Promise.reject(error));

// Response interceptor

this.api.interceptors.response.use(

(response) => response,

(error) => {

if (error.response.status === 401) {

// Handle unauthorized errors

}

return Promise.reject(error);

}

);

}

get(endpoint, params) {

return this.api.get(endpoint, { params });

}

post(endpoint, data) {

return this.api.post(endpoint, data);

}

// Other HTTP methods...

}

const apiService = new ApiService();

export default apiService;

React folder structure: How To Structure React Projects

Before I get into the various folder structures, I’d like to mention one point. All of these folder topologies will only deal with files/folders in the src folder.

Outside of the src folder, your files will be very project-dependent; therefore, there is no suitable universal structure to follow because it will be heavily influenced by your project and the libraries you use.

1. Simple Folder Structure

When you first run create-react-app, there are no folders in the src folder, and usually people add two directories: components and hooks. This is obviously a very simplistic folder structure, but for smaller projects with fewer than 10–15 components, it is actually not a bad strategy.

  • hooks

The hooks folder contains all of the custom hooks in your project. This is a useful folder to have in any size project because practically every project will have several custom hooks, so having a central location to store them all is really beneficial.

  • components

The components folder in the simple folder structure is really easy because it contains every component in your entire program. Naturally, once your project expands above 10–15 components, this folder may become extremely unruly to deal with, which is why in all other folder designs, our components are divided across numerous directories and given greater organization. For minor projects, however, this additional complexity is unnecessary, and a single folder is sufficient.

  • __tests__

The final folder in this arrangement holds all of your test code. On smaller projects like this, I find that people like to keep all of their tests in one folder (assuming they write any tests at all). Overall, I believe this is appropriate for smaller projects, but I would update it as your project expands in size.

2. Intermediate Folder Structure

As you can see from the figure above, this folder structure includes a plethora of other folders that cover nearly every file type imaginable in a React project. For the most part, using this folder layout, you should have essentially no files in the root of your src folder except for items like your index.js file.

The other significant difference between this folder structure and the simple folder layout is that we now divide our project into pages, which include all of the logic for individual pages in a single area.

This is especially beneficial for larger projects because it allows you to discover all of the material connected to your pages in one area rather than having to search across numerous folders and sift through unrelated files to find what you’re looking for.

You will also notice that our tests are now specific to the folder/files being tested. This makes it easier to see which code is being tested, which in turn makes testing code easier when the tests are in the same area as the code being tested.

  • pages

The most significant modification to this folder layout is the addition of the pages folder. This folder should contain a folder for each page of your application. Inside those page-specific folders, there should be a single root file (usually index.js) as well as all of the files that are unique to that page.

For example, in the image above, we have a Login page with the root file index.js, a component called LoginForm, and a custom hook called uselogin. This component and hook are only used on the Login page; thus, they are kept with it rather than in the global hooks or components files.

  • components

Another significant modification in this example is that our components folder is further divided into subfolders. These subfolders are quite handy since they help divide your components into various sections rather than simply one large blob of components.

In our example, we have a UI folder that contains all of our UI components, such as buttons, modals, and cards. We also have a form folder that contains form-specific controls such as checkboxes, inputs, and date pickers.

You can edit and break down this component folder as you see fit based on your project requirements, but this folder should not grow too large because many of your more complicated components will be housed in the pages folder.

  • hooks

Hooks is the final folder that repeats the simple folder layout. This folder is nearly identical to the previous hooks folder, except that instead of storing every hook in your application, it only stores global hooks that are utilized across numerous pages. This is because all page-specific hooks are located in the pages folder.

  • assets

The assets folder contains all of your project’s photos, CSS files, fonts, and so on. This folder will contain almost everything that isn’t code-related.

  • context

The context folder contains all of your React context files that are utilized on various pages. I found that on larger projects, you will have several contexts you use throughout your application, and having a single folder to store them in is quite beneficial. If you use a different global data store, such as Redux, you can replace this folder with a more appropriate set of folders for Redux file storage.

  • data

The data folder is identical to the assets folder, except it is intended to store data assets such as JSON files containing code-related information (store items, theme information, etc.). This folder can also include a file with global constant variables.

This is important if you have a number of constants that you use throughout your application, like environment variables.

  • utils

The final new folder is the utils folder. This folder is used to store all utility functions, such as formatters. This is a rather simple folder, and all of the files within it should be similarly simple. I prefer to place only pure functions in this subdirectory since if a utility function contains side effects, it is most certainly not a simple utility function. There are exceptions to this rule.

3. Advanced Folder Structure

If you merely glance at these two folder structures, you’ll find a lot of similarities, but there is one key difference: the features folder.

This features folder is a more elegant approach to putting similar code together, and it does not suffer from the same issues as the pages folder in the intermediate folder structure, because your features will virtually never overlap significantly.

Because so many of the files in this structure are duplicates from the intermediate structure, I will only discuss the ones that have changed between the two configurations.

  • features

The characteristics folder represents the most significant difference between these two structures. This folder is quite similar to the pages folder from the intermediate structure; however, instead of grouping by page, we organize by feature.

As a developer, this is already easier to understand because, in 90% of cases, when you add new code to a project, you are either implementing a new feature, such as adding user accounts or modifying an existing feature, such as allowing you to edit todos.

This makes working with the code easier because all of the code for each feature is in one location, making it simple to update and add to.

This folder’s layout is similar to that of the pages, with unique folders for each feature (authentication, todos, projects, and so on), and all of the files for that feature are contained within those folders.

The most noticeable distinction between the pages and feature folders is that each feature contains its own set of folders. This folder structure for each feature consists of a complete copy of all the folders in the src folder (except the features folder, of course) as well as an index.js file. This means that within your feature, all of your code can be arranged by type (context, hook, etc.) while remaining collocated together.

The index.js file is then used to expose a public API for everything that is accessible outside of the feature folder for that specific feature. It is usual to want to keep a bunch of code secret to the specific feature you are working on, but with JS, if you make an export in one file, it can be used in any other file you choose. In larger projects, this can be problematic if we just want to expose a few components/methods for our feature, which is where the index.js file comes in.

This file should only export the code that needs to be accessible outside of the feature, and you should import it from the index.js file whenever you utilize it in your application. This is extremely beneficial because it reduces your global code footprint significantly and makes it easier to use the features because you have a constrained API to work with. This can also be enforced by an ESLint rule that prevents any import from a feature folder that does not originate from index.js.

  • pages

The pages folder is the other key difference in this new arrangement. This folder currently only contains one file per page, which is due to the fact that the features folder contains all of the logic for the page’s features. This means that the files in the pages folder are rather simple, as they only bind together a few feature components and some general components.

  • layouts

The first new folder is called layouts, and it is really straightforward. This is simply a unique folder for storing layout-based components. This includes sidebars, navbars, containers, and so on. If your application only has a few layouts, this folder is unnecessary, and you can simply place the layout components in the components folder; however, if you have a variety of layouts utilized throughout your program, this is an excellent place to store them.

  • lib

The lib folder is another rather simple folder. This folder includes facades for the various libraries used in your project. For example, if you use the Axios library, this folder will contain a file that constructs your own API on top of the Axios API, which you can then use in your application. This means that instead of importing axios directly into your project, you would import the file from the folder associated with axios.

This makes it much easier to update and replace libraries because all of the library-specific code is in one location in your application. It also makes it easier to tailor third-party libraries to your requirements.

  • services

The final created folder is the services directory. This folder contains all of your code for interacting with any external API. In general, larger projects will require access to a variety of APIs, and this folder is where the code that interacts with those APIs should be stored.

Again, this helps to clean up your code by containing all of the API interaction code in one folder rather than littering your application with it.

Leave a comment

Index