If you are relatively new to building large API applications, the shape the project should take may not be one of the first things that come to mind. You may spend time learning about the API life cycle OAuth and whatever else your application may need. However, I would argue that folder structure is as important as understanding these concepts.
Imagine you go into a house; upon entering the front door, you see the kitchen, and right next to the stovetop is a shower. Then as you continue exploring, you find that the sinks are in the bedrooms… Woah right! This is a candid example of disorganization, although extreme, a disorganized house or codebase can cause some severe problems.
This article will guide you through my personal tactics in creating a scalable backend structure.
The File Structure
│ ├─ exceptions/
│ ├─ http/
│ │ ├─ interfaces/
│ │ ├─ guards/
│ │ ├─ controllers/
│ │ ├─ middleware/
│ │ ├─ providers/
│ ├─ jobs/
│ ├─ models/
│ ├─ services/
│ ├─ repositories/
│ ├─ unit/
│ ├─ integration/
Let’s breakdown the above directory structure. In the root application folder we have the directories:
app/––contains files and folders specific to the applications’ functionalities. Including things like api endpoints, functions and jobs, models and modules.
config/––this folder is not always necessary, however if you have additional configurations for different stages of applications you can use this folder to store them.
database/––contains files specific to establishing a connection to your database, and configuring factories where you can read and write to those factories.
logs/––contains application runtime logs. I would add this in the
tests/ — contains unit and integration tests for the application.
.env ––for hidden environment variables
README.md — every application needs a README, this provides much-needed context for what your application does, and how to set up among other very important things. If you are unsure where to start, take a look at some templates.
The App Directory
Inside of the app directory is where most of the “work” happens. Here we have:
exceptions/ — This folder is for custom exceptions. I consider this folder optional. Most languages and frameworks will come pre-built with built-in exceptions that in most cases work fine. However, a custom exception can also go a long way, for example, you could configure a custom exception that extends a base exception but the added hook is that it sends notifications to bugsnag.
http/ — in here is where we would configure our REST APIs. My preferred course of action here is to use dependency injection to separate the individual components related to the API lifecycle separating controllers, providers, middleware, and requests validations. Internal folder breakdown:
controllers/[container] — Is the “function” an API request is targeting. This function has the job of getting and formatting different responses [200,4XX, 5XX]
providers/[functional]— this is the functional component of the API. The functions/methods inside of these files are responsible for performing the actual service of the API [exposing data, performing calculations, etc.]
middlewares/[authorization, wrapper] — middlewares for authorization [ex: verifying a bearer token] or creating functional wrappers [ex: logging] for controllers.
guards/[Validation, Authorization] — guards perform validations and authorizations specific to each controller. The outcome of these functions is to validate the data being passed to the controller and ensure the user trying to access the controller is authorized to do so. In NestJS this feature is known as a
guardin Laravel/Lumen this feature is known as a
interfaces/[DTOs, Response Objects] —to define data types used in the controller, for instance, data type objects (DTOs) or acceptable responses.
Note. This component separation may look different depending on the language or framework that you are using.
For example, if you are in Python using the
FastAPI framework, it may make sense to replace the internal component directory of
routes/. However, conceptually the structure above is usually maintained.
jobs/ — contains application-specific jobs. I would flag this as an optional folder as well as jobs can be chaotic if they are not set up properly. But an example of a job would be an event-based job that triggers a data pipeline every 12 hours.
models/ — contains the objects that are frequently used throughout the application.
services/ — services contain functional code that can be generally used throughout your application.
repositories/ — repositories are used to control data coming to and from different data sources.
This article does not go into full detail for all of these topics but hopefully, it does provide some helpful context in setting up a clean application. If you would like me to go deeper into any of these topics leave a comment and I’ll see what I can do :D!
Until then —