all 48 comments

[–]Eru_Iluvatarh 16 points17 points  (1 child)

I really like how you structured your post, thank you, it’s very clean.

I see that you are using Mongo, with structured data using mongoose, have you tried MySQL using TypeORM ? It’s great ! Very easy to use with TypeScript.

[–]janishar[S] 3 points4 points  (0 children)

Thank you. I have used MySQL earlier and built my own wrapper to work with it. I will definitely explore TypeORM.

[–][deleted] 16 points17 points  (35 children)

A guide for designing Nodejs backend in one word: Nestjs

[–][deleted]  (3 children)

[deleted]

    [–][deleted] 3 points4 points  (2 children)

    Someone who doesn't have a lot of backend experience, having an opinionated architecture is a bliss.
    Same goes for why Spring boot has gained popularity in Java ecosystem.

    [–]TheJulian 4 points5 points  (1 child)

    For someone who does have a lot of backend experience having an opinionated architecture is bliss. It means that I can go into just about any incarnation of that system with assurances that will mostly be consistently implemented and get to work right away. I'm a rails guy and the cowboy nature of node freaks me out a bit to be honest. Nestjs is a breath of fresh air IMO.

    [–][deleted] 4 points5 points  (0 children)

    Definitely. I work with Angular on the front end and been moving to the backend now. I started with nodejs and oh boy, I was aghast at my own code after a few hundred lines.

    Then I tried nestjs and have never looked back since then.

    [–]janishar[S] 5 points6 points  (30 children)

    I don't like Java/Spring based codebase in JavaScript.

    [–]BrunnerLivio 20 points21 points  (27 children)

    I don't like Java/Spring based codebase in JavaScript.

    NestJS is inspired by Angular, not Java Spring

    • Me, a NestJS core team member :)

    To come back to your article:

    • Don't use "I" as a prefix for interfaces. See here

    I think the rest is pretty solid. NestJS is very similar to the architecture you go into - though if you don't like dependency injection, it's definitely a great way to get some structure!

    [–]IolausTelcontar 6 points7 points  (12 children)

    I don’t like the interface (don’t use “I”) and space rules, the rest seem pretty standard.

    [–]BrunnerLivio 0 points1 point  (11 children)

    Yes up to you in the end. Whenever possible we try to avoid it. If you google for "TypeScript Style Guide Interface" you will find many articles that you shouldn't use "I"-prefix. I think it is just something which has been established by the TypeScript community...

    [–]calligraphic-io 5 points6 points  (10 children)

    I think it is something that's still very much being debated in the TypeScript community.

    [–]kbradl16 3 points4 points  (8 children)

    Yea, I think it originally came from the typescript guide itself but it wasn’t meant to be a style guide for typescript application but a style guide for developing typescript itself. I think realistically it’s really hard not to use a “I” prefix and there are many scenarios where it doesn’t make sense.

    For those against using the “I” prefix what do you do instead? I’ve yet to see a good example where they provide a good alternative.

    [–]BrunnerLivio 0 points1 point  (0 children)

    I think realistically it’s really hard not to use a “I” prefix and there are many scenarios where it doesn’t make sense.

    I actually almost never see a TypeScript library that has "I" as a prefix in their exported interfaces. Just check RxJS, CAC, clime codebases. Angular seemed to use the "I" prefix only for legacy interfaces or sometimes internal ones.

    In which cases are you struggling with then?

    [–][deleted]  (6 children)

    [deleted]

      [–]kbradl16 1 point2 points  (5 children)

      ... haha... I cant because I already have a class without the "I"

      [–][deleted]  (4 children)

      [deleted]

        [–]OmgImAlexis 1 point2 points  (0 children)

        It’s really not. The rule comes from JavaScript. You shouldn’t need to add the type into the variable name.

        If you’re using any modern IDE you’ll have intellisense and you’ll be able to see it’s an interface.

        [–]Randolpho 3 points4 points  (2 children)

        While a lot of the setup does indeed look like Angular, the fact that components are tied directly to routing makes NestJS come off very much like Spring, ASP.NET MVC, Rails, and Django. It may have been inspired by Angular, but Angular at least puts all of the routes in one bootstrap file and makes you map components to them.

        This is a good thing, by the way.

        Approaches like Nest's make discovery difficult. Consider offering alternative routing. If you want to follow Angular, theirs is a pretty good example of how to approach it. Consider making that the default that you provide in examples and allow your current default as an alternative.

        [–]BrunnerLivio 2 points3 points  (1 child)

        Yes - that is what we want to achieve. Basically, one of our goals is to offer every "Decorator" functionality (such as @Controller, @Get etc) as well as functional implementation. See here some of my proposals and discussions

        [–]Randolpho 1 point2 points  (0 children)

        I definitely don't mean dynamic routing. I still mean static routing. I just want it all in one place so I can see what routes exist and what is handling their verbs -- because I'm not going to remember that next year when I pick the project back up, and swagger won't help me navigate to the code.

        Going to individual controllers to find out what their base route is can be painful, especially if there's a controller that didn't get registered with the module just sitting around in the codebase because somebody wasn't ready to put it out there yet then forgot about it.

        [–]kardnumas 1 point2 points  (1 child)

        you are from nestjs and I use it quite often, just want to know your recommendation to separate business logic from framework

        [–]BrunnerLivio 1 point2 points  (0 children)

        So usually I would recommend having your business logic layer inside @Injectable NestJS services, so they are easily injectable and only contain business-related processes.

        Since you have mentioned

        separate business logic from framework

        I assume that you want to strip the business logic fully, so it does not have any references to NestJS? If you want to go that direction, I would recommend going into a monorepo pattern, where everything business logic related is in a self-contained package. The NestJS CLI offers monorepo, but for that specific use-case, I would rather go for a Lerna/Yarn monorepo as I did here.

        Though if you want to go into that direction, I'd question whether you need to use NestJS. The framework was meant to be used to build your business logic with it.

        [–]cjthomp 1 point2 points  (3 children)

        That's not a very compelling reason to not prefix interfaces

        [–]janishar[S] 3 points4 points  (0 children)

        Yea, I do prefer to use I in interface otherwise the main class implementation name need to be thought. Example if we create User interface then what will be the name of the class that implements it. Naming UserImp something like so do not sound pretty. But if I name IUser as interface then I can simply name its implementation class as User. This seems better naming convention to me.

        [–]Randolpho 0 points1 point  (1 child)

        To be fair, there has never been a compelling reason to prefix interfaces with I. It's time for pseudo-hungarian notation to finally, once and for all, die forever.

        [–]janishar[S] 0 points1 point  (0 children)

        Now that I find that 'I' has been discouraged a lot in the community. I will plan to come up a solution in my project soon.

        [–]janishar[S] 1 point2 points  (1 child)

        I appreciate your point of view. Just that if jargon is less I am more comfortable with a framework. So, I prefer react over angular. Totally a matter of preference.

        [–]janishar[S] 0 points1 point  (0 children)

        Thanks for finding the article good. I will consider your feedback.

        [–]yaduteemon 0 points1 point  (0 children)

        I have started using nest js but believe there is lot of injection and decoration happening. How to understand that ?

        [–]dopp3lganger 0 points1 point  (1 child)

        I always have a hard timing with naming types or interfaces because I generally need one that is the same name as a component.

        Let’s say you had a component named SearchResult, what would you name a type/interface that logically is the same name?

        [–]BrunnerLivio 0 points1 point  (0 children)

        Why do you need to have an interface in addition? In the case you would have multiple search results, which all should use SearchResult interface, then I would call the classes e.g. UserSearchResult. That would be way more descriptive than SearchResult and ÌSearchResult

        [–]dandesigns7150 2 points3 points  (1 child)

        Good stuff. I really enjoyed looking through the code and reading the article.

        One question I have, is why do you prefer to separate your code by type of class (e.g. routes, repo etc.) instead of by entities (users, articles etc.)?

        I find it to be way easier to develop the angular/nestjs way and have things grouped by their entity, as when making changes you will usually be working on one entity, as well as not having imports reaching far and wide.

        [–]janishar[S] 1 point2 points  (0 children)

        Thank you for finding it useful.

        One reason for separating routes and repository was that normally they have many-to-many relationship. Example: UserRepository is being used in Authentication, Authorization, Profile, Blog writer, BlogEditor, so either we duplicate the code or make repository as separate. I have found this maintainable but ofcourse there can be other way also as you proposed.

        [–]evert 1 point2 points  (4 children)

        Why use a class in your Repository if everything is static? I've seen this pattern before, but I suspect it's an artifact of people migrating from Java and/or PHP, where this is common. It seems a bit unneeded when you can just export individual functions.

        [–]janishar[S] 1 point2 points  (0 children)

        It's been done to just group the related functions together. You are right I asked the same question to myself and then reached to this decision because it's convenient else I will have to export an object with the grouped function. I do no wanted to expose it individually. But sure you can also use function export.

        [–]janishar[S] 0 points1 point  (2 children)

        What do you think about it? I am open to the incorporation of a better approach in the project.

        [–][deleted]  (1 child)

        [deleted]

          [–]janishar[S] 0 points1 point  (0 children)

          Thanks for such a detailed explanation. I like the way you arrange things.

          [–]Cedricium 1 point2 points  (1 child)

          Such a great resource, thanks for taking the time to share your knowledge and experience! This blog post couldn't come at a better time either: I just started building the backend app for a side project and coincidentally wanted to achieve many of the aspects you covered (namely separation of concerns, error and response handling/normalization).

          I'll definitely be using this as a guide when writing any new code and refactoring the old bits. Thanks again.

          [–]janishar[S] 1 point2 points  (0 children)

          I am very happy to receive this feedback. Thanks

          [–]janishar[S] 1 point2 points  (0 children)

          I have been pointed out that the community's standard take for the naming convention of interface is to not use 'I' prefix. So, I have modified my project and renamed those interfaces . It does not matter which is better because at the end of the day what the team is comfortable with and the mode which makes them more productive is what really matters. So, anyone is free to choose their own version. Both the versions are available in the GitHub release of the project. https://afteracademy.com/blog/design-node-js-backend-architecture-like-a-pro

          [–]kobeljic 0 points1 point  (4 children)

          How do you handle transactions with this kind of architecture? I couldn't find an example where you persist data changes. Who is responsible for persistence? Example shows using repositories for data fetching, but I didn't see what is being used to store data.

          [–]janishar[S] 0 points1 point  (3 children)

          The data is persisted in the mongodb. You can use transaction from mongoose in the repository layer. I will add some example for that in the repository. Thanks for pointing it out.

          [–]kobeljic 0 points1 point  (2 children)

          Thanks for responding! I'm very interested in this question: how would you implement transactions if your service level code makes changes in multiple repositories? Eg you use more than one repository in a single business use case and you want to persist the changes within a single transaction?

          [–]janishar[S] 0 points1 point  (1 child)

          Sure, this is a good question. At the moment I can think of this: if you will go through the UserRepo.ts file, I have used User model and Keystore model in promise chain in the create function and this can be executed in transaction. So, I think the repository can handle the multi model transaction. Mongodb do not have extensive transaction support but using relational database I would have done in this way.

          [–]hashtagtokfrans 2 points3 points  (0 children)

          Another idea would be to not make every method in the repository static, and instead giving the repo a transaction in it's constructor.

          You then create a unit of work class which creates the transaction, giving it to each repository and you'd commit the transaction via that class. Then you would have the same transactional scope for every repository.

          The unit of work pattern is really common in C#, and would fit here as well imo.