↓ Ir para o conteúdo principal

← todas as notas

📎 Webclip

Building a Node.js/TypeScript REST API, Part 2: Models, Middleware, and Services

The article splits the users API into layers. DTOs describe the user shape, the DAO stores users in an in-memory array, the service wraps CRUD calls, controllers handle requests, and middleware runs validation before requests reach the controller.

Reading notes
#

  • The series continues from Part 1 and assumes the project structure and tooling are already in place.
  • The API is organized around services, middleware, controllers, and models.
  • The example uses a temporary in-memory database that stores users in an array and supports basic CRUD only.
  • The article introduces DAO and DTO as separate concepts.
  • A DAO performs CRUD operations against a database.
  • A DTO carries raw data that matches the data model.
  • DTOs help TypeScript objects conform to the model structure.
  • TypeScript does not perform runtime validation of incoming user input.
  • User input may contain extra fields, miss required fields, or use the wrong types.
  • Packages such as ajv can help with runtime validation.
  • The example user DTO requires id, email, and password, with firstName, lastName, and permissionLevel optional.
  • The DAO is implemented as a singleton so every import shares the same users array.
  • shortid is used to generate user IDs.
  • The DAO includes create, read, update, patch, delete, and get-by-email operations.
  • putUserById can accept values outside the DTO-defined model because TypeScript does not validate at runtime.
  • patchUserById updates only selected fields and depends on a separate allowlist.
  • The service layer implements a CRUD interface and delegates work to the DAO.
  • The service also follows the singleton pattern.
  • The controller hashes passwords with argon2 before storing them.
  • Controller methods list users, fetch one user, create a user, patch a user, update a user, and delete a user.
  • Middleware validates required fields, checks whether an email already exists, prevents changing the email after creation, checks that a user exists, and copies userId from the route into the request body.
  • Routes combine middleware and controller functions.
  • The .all() handler on /users/:userId runs the existence check before GET, PUT, PATCH, and DELETE handlers.
  • POST and PUT share the required-fields validation.
  • The article says the example only covers basic validations and assumes users cannot change their email.
  • Testing is shown with curl requests.
  • A GET on /users initially returns an empty array.
  • A POST without required fields returns a middleware error.
  • A valid POST returns a generated user id.
  • PUT updates the full resource, while PATCH updates only part of it.
  • The example response shows the password stored as an argon2 hash.
  • DELETE removes the user, and the list becomes empty again.
  • The article closes by saying the next part will replace the in-memory database with MongoDB and Mongoose, add JWT security, and configure automated testing.