# Registering the User - Implementation

## 1 - Route Name

`GET /api/v1/users`

Let's add an additional `users` route to the `routes/index.js file`.

*File: src/routes/index.js*

```javascript
const express = require('express')
const router = express.Router()

const userRoutes = require('./user.route')
const bookRoutes = require('./book.route')

function getRouter() {
  router.use('/books', bookRoutes)
  router.use('/users', userRoutes) // our new route

  return router
}

module.exports = getRouter
```

Here, we will then add a `registerUser` controller function.

*File: src/routes/user.route.js*

```javascript
const express = require('express')
const router = express.Router()

const isAuthenticated = require('../middleware/auth.middleware')
const { registerUser } = require('../controllers/auth')

router.post('/', registerUser)

module.exports = router
```

We will then proceed to fill up the rest of the controller below.

*File: src/controllers/auth.controller.js*

```javascript
/**
 * Inserts the user into the database and fires off an email notification
 * to that user's email if successful.
 */
const registerUser = catchExceptions(async (req, res) => {
  // Our code goes here...
})
```

## 2 - Input Request

Now we will add in a custom DTO that will help us specify the contract between us and the client.

This layer can be seem as a validation layer, but what we are ultimately trying to accomplish here is to get the frontend and backend aligned on the same page. Notice there is no real validation of having the `password` being a certain length or the `phone_number` being a certain format. The DTO simply helps the client who's calling our API to provide the correct structure in the payload.

*File: src/requests/registerUserDTO.js*

```javascript
const ApiException = require('../utils/ApiException')

const fields = [
  'first_name',
  'last_name',
  'email',
  'password',
  'password_confirmation',
  'phone_number'
]

/**
 * @param Object data
 */
const registerUserRequestDto = (data) => {
  const errors = []
  fields.forEach((field) => {
    if (!(field in data)) {
      errors.push(`This DTO's property is required: ${field}.`)
    }
  })

  if (errors.length > 0) {
    throw new ApiException({
      status: 'error',
      code: 422,
      message: 'Input fields are not in the correct form.',
      data: null,
      errors
    })
  }

  return data
}

module.exports = registerUserRequestDto
```

## 3 - Middleware

As stated in the previous section, we will be adding in a global middleware to help us catch errors.

You may have noticed a utility wrapper being used in the controllers up until now called `catchExceptions`.

*File: src/controllers/auth.controller.js*

```javascript
const registerUser = catchExceptions(async (req, res) => {
  // Our code goes here...
})
```

*File: src/utils/catchExceptions.js*

```javascript
const catchExceptions = (func) => {
  return (req, res, next) => {
    Promise.resolve(func(req, res, next)).catch(next)
  }
}

module.exports = catchExceptions
```

The reason for this is due to our `globalErrorHandler` that we'll be putting as follows.

*File: src/server.js*

```javascript
const globalErrorHandler = require('./utils/globalErrorHandler')

app.use(globalErrorHandler)
```

*File: src/utils/globalErrorHandler.js*

```javascript
const globalResponseDTO = require('../responses/globalResponseDTO')

const globalErrorHandler = async (err, req, res, next) => {
  console.log('===============================')
  console.log('Global Error Catcher:', err.name)
  console.log('===============================')

  if (err.name === 'ApiException') {
    console.error('ApiException', err)

    res.status(err.code).json(
      globalResponseDTO({
        status: err.status,
        code: err.code,
        message: err.message,
        data: err.data,
        errors: err.errors
      })
    )
  } else {
    console.error('Other Error', err)
  }
}

module.exports = globalErrorHandler
```

This is really going to make our lives easier, because every time we throw an `ApiException` anywhere in our application, it is going to automatically catch the error we have thrown and output it out as JSON output.

## 4 - Validation

Next is the form validation, we'll be using the [*validatorjs library*](https://www.npmjs.com/package/validatorjs) to help us achieve the validation rules we specified in our planning section of this endpoint.

*File: src/validators/registerUserValidator.js*

```javascript
const Validator = require('validatorjs')
const ApiException = require('../utils/ApiException')

/**
 * @param {*} data {
 *  - first_name
 *  - last_name
 *  - email
 *  - password
 *  - password_confirm
 *  - phone_number
 * }
 *
 * @returns Validator
 */
const registerUserValidator = (data) => {
  const rules = {
    first_name: 'required',
    last_name: 'required',
    email: 'required|email',
    password: 'required|min:6',
    password_confirmation: 'required|min:6|same:password',
    phone_number: 'required|telephone'
  }

  const validator = new Validator(data, rules)

  if (validator.fails()) {
    let errors = []
    for (const field in validator.errors.errors) {
      errors = errors.concat(validator.errors.errors[field])
    }

    throw new ApiException({
      status: 'error',
      code: 422,
      message: 'There were errors with the validation.',
      data: null,
      errors
    })
  }

  return validator
}

// This is our custom 'telephone' validation rule
Validator.register(
  'telephone',
  function (value) {
    return value.match(/^\(?([0-9]{3})\)?[-.●]?([0-9]{3})[-.●]?([0-9]{4})$/)
  },
  'The :attribute field is not in a correct format.'
)

module.exports = registerUserValidator
```

## 5 - Domain

For our domain layer, we'll first create our user model as follows.

*File: src/domain/models/user.model.js*

```javascript
const mongoose = require('mongoose')
const Schema = mongoose.Schema

const userModel = new Schema({
  first_name: {
    type: String,
    required: true
  },
  last_name: {
    type: String,
    required: false
  },
  email: {
    type: String,
    required: true,
    unique: true
  },
  password: {
    type: String,
    required: true
  },
  phone_number: {
    type: String,
    required: true
  },
  createdAt: {
    type: Date,
    default: Date.now
  }
})

module.exports = mongoose.model('user', userModel)
```

Then, we will create the `createUser` method, which will simply save the `userData` into our database.

*File: src/domain/services/userRepository.js*

```javascript
const UserModel = require('../models/user.model')

/**
 *
 * @param {*} user {
 *  - name
 *  - email
 *  - password
 * }
 *
 * @returns user
 */
const createUser = async (userData) => {
  const user = new UserModel(userData)
  const userReturn = await user.save(userData)

  return userReturn
}

module.exports = {
  createUser
}
```

Lastly, we will create the `authService` layer and use our `userRepository.createUser()` utility to create our service for registering our user.

*File: src/domain/services/authService.js*

```javascript
/**
 * @returns User
 */
const registerUser = async (user) => {
  try {
    const createdUser = await userRepository.createUser(user)
    return createdUser
  } catch (err) {
    if (err.code === 11000 && err?.keyPattern?.email === 1) {
      throw new ApiException({
        message: `This email is already taken.`,
        status: 'error',
        code: 409,
        data: null,
        errors: [`This email is already taken.`]
      })
    }
  }
}
```

Notice how we do a try-catch here where we throw a custom `ApiException` in order to catch a specific type of error, in this case it's that the email must be unique.

## 6 - Events

As noted in the planning section of this endpoint, when the user has successfully registered, an event will fire off. We can take advantage of Node.js' built-in `eventEmitter` to create a pub-sub structure for us.

*File: src/events/index.js*

```javascript
const events = require('events')

const eventEmitter = new events.EventEmitter()

const userHasRegistered = require('./userHasRegisteredEvent')

eventEmitter.on('userHasRegistered', userHasRegistered)
```

*File: src/events/userHasRegisteredEvent.js*

```javascript
const mailer = require('../utils/mailer')

/**
 *
 * @param {*} user
 *
 * @returns boolean
 */
const userHasRegisteredEvent = (user) => {
  let bodyText = `Hello ${user.firstName}. Thanks for registering!`
  return mailer.sendEmailToUser(user.email, 'Welcome aboard!', bodyText)
}

module.exports = userHasRegisteredEvent
```

Notice here that we are not really going to be sending any real emails, we will just be stubbing it out. Of course, if you really wanted to, I suggest using [SendGrid](https://sendgrid.com) or [Amazon SES](https://aws.amazon.com/ses).

*File: src/utils/mailer.js*

```javascript
/**
 * Sends an email to a user, if it was successfully sent, then return true, else return false
 *
 * @param {*} toEmail
 * @param {*} subject
 * @param {*} bodyText
 *
 * @returns boolean
 */
const sendEmailToUser = () => {
  return false
}

module.exports = {
  sendEmailToUser
}
```

## 7 - Response

Recall in the previous section [*Registering the User - Planning*](https://book.restfulnode.com/part-3/1-registering-the-user-planning#7-response). We indicated that there were going to be 4 different responses.

Remember, our `globalErrorHandler` is able to catch all these `ApiException`s.

The first, ***DTO validation*** is triggered when we throw the following `ApiException`.

*File: src/requests/registerUserDTO.js*

```javascript
throw new ApiException({
  status: 'error',
  code: 422,
  message: 'Input fields are of not the correct form.',
  data: errors
})
```

```json
{
  "status": "error",
  "code": 422,
  "message": "Input fields are of not the correct form.",
  "data": null,
  "errors": [
    "This DTO's property is required: email.",
    "This DTO's property is required: password.",
    "This DTO's property is required: password_confirmation.",
    "This DTO's property is required: phone_number."
  ]
}
```

The second, ***form validation*** is triggered when we throw the following `ApiException`.

*File: src/validators/registerUserValidator.js*

```javascript
throw new ApiException({
  status: 'error',
  code: 422,
  message: 'There were errors with the validation.',
  data: null,
  errors
})
```

```json
{
  "status": "error",
  "code": 422,
  "message": "There were errors with the validation.",
  "data": null,
  "errors": [
    "The email format is invalid.",
    "The password confirmation and password fields must match."
  ]
}
```

The third, **service layer validation** in which we check if email already taken, is triggered when we throw the following `ApiException`.

*File: src/domain/services/authService.js*

```javascript
/**
 * @returns User
 */
const registerUser = async (user) => {
  try {
    const createdUser = await userRepository.createUser(user)
    return createdUser
  } catch (err) {
    if (err.code === 11000 && err?.keyPattern?.email === 1) {
      throw new ApiException({
        message: `This email is already taken.`,
        status: 'error',
        code: 409,
        data: null,
        errors: [`This email is already taken.`]
      })
    }
  }
}
```

```json
{
  "status": "error",
  "code": 409,
  "message": "This email is already taken.",
  "data": null,
  "errors": ["This email is already taken."]
}
```

Finally, the last response, which is the **success** response.

If everything goes through and there are no errors, then we will return a `User` object.

For that, we will use a DTO as follows.

*File: src/responses/userResponseDTO.js*

```javascript
const userResponseDTO = (user) => {
  return {
    id: user['id'],
    first_name: user['first_name'],
    last_name: user['last_name'],
    email: user['email'],
    phone_number: user['phone_number']
  }
}

module.exports = userResponseDTO
```

```json
{
  "status": "success",
  "code": 201,
  "message": "The email: yichenzhu13371@email.com has successfully registered.",
  "data": {
    "id": "61f889bbc6bbf81a97ba69d6",
    "first_name": "Yichen",
    "last_name": "Zhu",
    "email": "yichenzhu13371@email.com",
    "phone_number": "1234567890"
  },
  "errors": null
}
```

## Putting It All Together

If you've followed a long, this is how our controller should look like by the end.

Nice and clean :sunglasses:.

```javascript
/**
 * Inserts the user into the database and fires off an email notification
 * to that user's email if successful.
 */
const registerUser = catchExceptions(async (req, res) => {
  // request
  const registerUserRequest = registerUserRequestDTO(req.body)

  // validation
  registerUserValidator(registerUserRequest)

  // domain logic
  const user = await authService.registerUser(registerUserRequest)

  // events
  eventEmitter.emit('userHasRegistered', user)

  // response - success
  return res.json(
    globalResponseDTO({
      status: 'success',
      code: 201,
      message: `The email: ${registerUserRequest.email} has successfully registered.`,
      data: userResponseDTO(user),
      errors: null
    })
  )
})
```
