10

I would like to create a REST API with NestJs. But I want to add GraphQL as another top level layer later on. So for the start I have the basic layers controller, service and TypeORM repository. Let's assume you want to update a user's username by id. The controller route could be

PATCH /users/:id/username

Two problems might come up in the service or repository layer:

  • The user id might not exist
  • The username exists already

The basic flow of this operation would be

  • Fetch the user by id
  • Handle error if the user does not exist
  • Check if the username exists already
  • Handle error if the username exists already
  • Update the user's username

I'm thinking about how I should handle those errors. I could throw exceptions immediately based on this concept

https://en.wikipedia.org/wiki/Fail-fast

NestJs provides some out of the box exceptions I can use

https://docs.nestjs.com/exception-filters#built-in-http-exceptions

The problem is that I don't think I should throw HTTP exceptions in my service layer. They should be thrown in my controller logic. So what is a common approach for those errors?

  • Should I just return undefined instead of an updated user? The controller wouldn't know which part failed.
  • Should I create my own exceptions extending Error and throw them?
  • Due to the fact exceptions come with low performance should the return type of the function be something like <User | NotFoundError | ConflictError>?
hrp8sfH4xQ4
  • 209
  • 2
  • 4
  • Seems like you are trying to fit your requirements to a design rather than the other way around. Forget about services and ORMs, start with your controller logic and then implement the rest to the contract your controller defines. You might not (probably don't) even need a "service layer." What is it for? – Ant P Feb 14 '20 at 11:32
  • The service layer is for the separation of concerns. So the repository should only deal with the database stuff, the service should handle the main logic and the controller should handle the requests/responses – hrp8sfH4xQ4 Feb 14 '20 at 12:00
  • so the service layer could be an external project / library too – hrp8sfH4xQ4 Feb 14 '20 at 12:02
  • Sounds like you are overengineering your solution and it's causing you problems. You've built an abstraction you don't actually need and now you're fighting against it. – Ant P Feb 14 '20 at 13:08
  • so you think I should handle everything in the controller =? – hrp8sfH4xQ4 Feb 14 '20 at 13:20
  • I think you should abstract your domain. If you have one. A service layer is not a domain abstraction. Your question essentially boils down to "how can I create an abstraction from HTTP without having to abstract from HTTP." You have to choose - do you want a service layer with abstract representations for failure conditions (bearing in mind that this abstraction will have ONE consumer and you won't know if it's appropriate until (unless) another comes along) and a bunch of mappings for your HTTP layer... or do you just stick to HTTP and only build what you need. – Ant P Feb 14 '20 at 13:24

1 Answers1

5

You are correct to say you should not throw HTTP specific exceptions in the service layer. The service layer should not have any knowledge of http things.

Most server frameworks have a top level exception handler and an error handling middleware. That top level exception handler could catch any exceptions not handled by your code and return 500 Internal server error.

Usually you can customize that and say okay if it's this language level exception, use this http response. I don't know the framework you are using, as an example in asp.net, when we throw (System.Collections.Generic) KeyNotFoundException then on the top level we return 404. It's not perfect, but it better than directly coupling the two.

Look here: https://docs.nestjs.com/exception-filters

Martin K
  • 2,867
  • 6
  • 17
  • thanks for your reply. Would you mind describing the `error handling middleware` a little bit? I thought those run before the incoming request and not after the outgoing response ... right? – hrp8sfH4xQ4 Feb 17 '20 at 06:52