Over the years, I've tried multiple Node.js back-end technology stacks, and I can finally say I've found a suitable one that will help you rapidly develop your projects (and kickstart your startups!).
The REST framework
Why? I've extensively used both REST and GraphQL back-ends and I believe GraphQL is an overkill in most cases. While it's great for scalability, it is not the fastest way to develop an API for your project.
My REST framework of choice is koa.js.
Why? I believe it's one of the best frameworks when it comes to quick development of your API, since it makes it extremely easy to go from the idea to the implementation.
The middleware stack is also very intuitive and relies on async/await.
I also feel it's a lot better than Express and generally more lightweight.
TypeScript
Why? It is an extremely good and easy way to make your API type safe and develop more rapidly in the long run. It has saved me a lot of headaches and I could not live without its IntelliSense suggestions.
You'll also see that using TypeScript enables us to easily integrate TypeORM
into our project.
ESLint
Why? Consistency goes a long way. ESLint makes sure you and your co-workers don't yell at each other over code that - in their opinion - is not properly formatted. It can also track things like unused variables/imports and usages of let
instead of const
.
The Database
This depends on your use case. However, there are only two database types you should care about - relational and document-based databases.
If you're not sure,
I'd say for smaller projects you'd want to go with a document-based database, like MongoDB.
However, when your project grows, you'll probably have certain relations between your entities. And thus, you should use a relational database, like MySQL or PostgreSQL (which is my database of choice).
TypeORM
For MongoDB, Mongoose may be more suitable.
Why? TypeORM is an Object-Relational Mapping library for typescript (and babel), which basically means you don't have to deal with raw SQL and you can use certain utilities, like automatic relation joining.
The reason why TypeORM is so interesting is the fact that it uses decorators for entity synchronization. What that means is you won't have to use migrations in your development environment.
Instead, you define a class and decorate it:
@Entity()
class User {
@PrimaryGeneratedColumn('uuid')
id: string;
// automatic type detection thanks to reflection!
@Column()
name: string;
// automatic date columns!
@CreateDateColumn()
createdAt: Date;
}
TypeORM then uses that class to migrate the database and to make IntelliSense suggestions based on the fields.
Why not? Frankly, TypeORM is not beginner friendly, largely due to the docs being quite horrible. However, when you get a hang of it and use IntelliSense to your advantage, it becomes an incredibly powerful tool.
Git Flow
Why? This is a certain philosophy/workflow/strategy that works particularly well when working with git in teams (not particularly needed if you're not in a team).
It defines what branches should be used for certain things, like features, releases, hotfixes.
.env
Why? Using .env
files for defining your environmental variables is pretty much a standard now. It lets you define different variables for your local machine and for the machine you're hosting your project on.
Do not forget to add .env
to .gitignore
!.
File structure
I came up with a file structure that works well with this particular technology stack:
src
├── entities
│ └── User.ts
├── modules
│ └── module-name
│ ├── module-name.router.ts
│ ├── module-name.service.ts
│ └── module-name.spec.ts
├── mw
├── utils
└── app.ts
e2e
└── test-name.e2e.ts
Going from the top:
- The
entities
folder is where you should store your TypeORM entities - The
modules
folder holds the different modules of your application (inspired by NestJS). For instance, anauth
module may have a router that has a/auth/facebook
route which in turn calls theauthorizeWithFacebook()
function in the service file. It's important the router handles the HTTP stuff, and the service deals with pure data. This way, your unit tests (.spec.ts
) can call the service directly. Additionally, your routes should - most of the time - be prefixed with the module name. - The
mw
folder is where you should store your custom middleware. You could also use theutils
folder for this. - The
utils
folder is pretty much every other function that doesn't fit anywhere else. - The
e2e
folder stores the end-to-end tests.
This workflow has proven most successful for my projects and is great for rapid development.
Don't agree with me? Start a discussion in the comments!
Happy coding! 🎉
Top comments (9)
I feel like TypeScript is most appealing to two kinds of devs - those coming from statically typed languages like C, C++, Java, Rust, Go, Scala or those developing projects in JS (or other dynamically typed languages like Ruby, Python, PHP) to the degree that static typing becomes a necessity to debug and manage codebase more easily.
Don't forget C#, since C# and TypeScript were both created by the same guy!
I love to look at the similarities in these languages.
So, you are only talking about NodeJs backends, right?
Sure, the article focuses on Node. However, I feel like Node (+JS) has dominated the web industry and it's a go-to language at this point.
I know it has dominated the blog industry, but is it really dominating actual backends being developed?
I think if you look you will find a good majority of great companies are using Ruby on Rails, Spring, DotNet, or Laravel and are completely happy with that choice.
Why not resort to higher abstraction frameworks then, like nest.js with its ready made toolings, app architecture in place, typescript coding and i'm sure there's an orm thing in a package ..?
So, funnily, I came up with this toolset and workflow after tinkering with Nest.
I tried building an API in it and it was so overly complicated, because it is meant for extreme scalability, with dependency injection and modules to separate different parts of the api.
My toolset is more of a devops one, and is meant for rapid development and fairly small APIs.
I do agree with you about GraphQL being overkill in a lot of cases, but you can move pretty fast with Hasura as a back-end once you're familiar with the basics of it.
I'm waiting on Prisma 2 to be out of the beta!
That may be just enough for me to try a GraphQL api again!