Production-Grade Node.js & TypeScript Folder Structure for 2024

Mingyang Li
4 min readJan 5, 2024

Disclaimer:

The goal of this article is not telling you that this is the best approach.

My opinion on this topic will change overtime as well, so the key takeaway for youis applying SOME of the conecpts from this article into your projects, then add your own twists based on your requirements.

Coding is a form of art, structuring your code is another.

✅ This article is for you if you tick any of the following boxes:

  1. You know how to consume REST APIs, but not how to build them.
  2. You’re not the only person working on a backend project
  3. You’ve learnt the basics of Express.js & TypeScript, but have little experience in commercial backend projects (university / bootcamp students, interns, etc)
  4. You’re learning to code not just for the sake of it, but to become more employable (let’s be real:)

😎 Let’s dive in.

📂 Here’s the folder structure

project-root/
💡 Documentations & diagrams on local development steps, deployment processes, etc
/docs

💡 If you prefer keeping testing files seperately
/__tests__

💡 Keep all your code files seperate from configuration files
/src

💡 REST API routes, keep them clean & short
/routes

💡 Responsible for receiving & returning data to routes
/controllers

💡 Core business logic
/services

💡 Database logic only (data-in/data-out, no business logic)
/repositories

💡 Optional, a place to define your DB schema if needed
/models

💡 Optional: Static values you might use across the project
/constants

💡 Wrappers for 3rd party SDKs/APIs, such as Stripe/Shopify APIs
/libs

💡 Parsing errors, protecting endpoints, caching, etc
/middlewares

💡 Optional: Type definitions if needed
/types

💡 Optional: Functions / classes for validating incoming API payloads
/validators

💡 Optional, only if you rely on generated types/functions
/generated

💡 Optional: Some teams call it 'utils' folder
/common

💡 Configure your logger here
logger.ts

💡 Centralise your environment variables in one place
env.ts

💡 Keep configuration files in root folder
package.json
.prettierrc
.prettierignore
.eslintrc.js
.eslintignore
yarn.lock

👍 Rules of thumbs:

  1. Keep your code files in a seperate folder, away from configuration files
  2. Name your folders by their technical capabilities (Onion / Clean Architecture approach), avoid naming folders by features (Vertical Slice Architecture). Business requirements change overtime. Calling folders by their features make it harder to onboard new engineers / team members because they haven’t yet learnt the domain-specific knowledge of your project; it creates more ambiguity / frictions for your team in terms of the overall architecture & call to name folders & files; it also introduces lots of context-switching when the business decides to rename a collections features on a product because of a pivot or something similar. Do you also rename tons of files because of this change? I don’t know. In my opinion, the Vertical Slice Approach for folder structure works much better in the front-end context. Frontend needs to be highly adaptable, backend needs to be stable.
  3. Seperate database logic into it own layer/folder, rather than keeping them in the service layer. This approach is more suitable for projects doing more than just CRUD operations.
  4. Seperate logic for 3rd-party APIs/SDKs into its own folder. This approach gives you a peace of mind in terms of which code are yours, and which code are to do with 3rd-party APIs. In addition, making calls to these APIs/SDKs typically require initialisation, be it using API keys, JWT tokens or both. It distracts you from building actual features when you have to instantiate the SDKs & write functions to calls to these APIs every time you need to use them, therefore it is also one of the reasons to seperate this type of code into its own files/folders.
  5. Create a centralised point (file) for accessing environment variables. This approach seperates concerns of using them in your business logic and validating whether they exist in your project. If done properly, you’ll also get nice autocompletion from your editor when you’re trying to access environment variables.

There’s a lot more areas to address, but for simplicity’s sake, I’ve kept it short to focus on seperation of concerns.

The codebases I had FUN working with, are the ones with well-defined boundaries of responsibilities. The ones that took me longer to understand however, were less focued on seperating logic into easily-digestable chunks.

Every project is different, so use your own judgements, decide for your yourself, which tips you’d like to take for your project & your team.

Hope this helps:)

--

--

Mingyang Li

Software Engineer @ KPMG | Open-water swimmer | Opinions are my own