Creating your own ExpressJS from scratch (Part 3) - Handling Request and Response objects
Tags: webdev, javascript, node, programming
Published: 2023-04-15 23:56:43

In [part 2](https://dev.to/wesleymreng7/creating-your-own-expressjs-from-scratch-part-2-middlewares-and-controllers-2fbc), We applied the middleware pattern for our web framework, which make us able to execute how many middlewares that we want for a route.

Today We are going to create Decorators for our `Request` and `Response` objects, applying the `decorator` and `middleware` pattern together to get parameters and query strings from the URL, also create helper functions to respond to the requests as a JSON transformer, for example.

Let's go!

## Request Decorator

`src/request.js`

```js
const { match } = require('path-to-regexp')

const RequestDecorator = (routes, request, response, next) => {

// (1)
    const getParams = () => {
        const urlParams = request.url.split('/').slice(1)

        // remove querystrings from the last parameter
        const [lastParam] = urlParams[urlParams.length - 1].split('?')
        urlParams.splice(urlParams.length - 1, 1)

        // joining all params without querystrings
        const allParams = [...urlParams, lastParam].join('/')

        // matching url pattern to get the params
        for (const path of routes) {
            const urlMatch = match(path, {
                decode: decodeURIComponent,
            });
            const url = `/${allParams}/${request.method.toUpperCase()}`
            const found = urlMatch(url)
            if (found) {
                Object.keys(found.params).forEach(key => {
                    request.params = {
                        ...request.params,
                        [key]: found.params[key]
                    }
                })
                break
            }
        }
    }

// (2)
    const getQuery = () => {
        const urlParams = request.url.split('/').slice(1)

        // Isolating query strings
        const [lastParam, queryString] = urlParams[urlParams.length - 1].split('?')
        let params = new URLSearchParams(queryString);
        let entries = params.entries();

        request.query = {
            ...request.query,
            ...Object.fromEntries(entries)
        }
    }

    getParams()
    getQuery()
    next()
}

module.exports = RequestDecorator
```
**From the code sessions above**:

1 - Treating the URL to compare with the existent routes and get the parameters and set the `request` object.

2 - Transforming the query string into an object and setting the `request` object.

---
## Response Decorator

`src/response.js`

```js
const ResponseDecorator = (req, res, next) => {
// (1)
    res.status = (status) => {
        res.statusCode = status
        return res
    }
// (2)
    res.json = (data) => {
        res.setHeader('Content-type', 'application/json')
        res.end(JSON.stringify(data))
    }
// (3)
    res.send = async (data) => {
        res.end(data)
    }

    res.render = async (templatePath, data) => {
        // We are going to implement it later
    }

    next()

}

module.exports = ResponseDecorator
```
**From the code sessions above**:

1 - Changing the status code and returning the `response` object, to set the status and response in one line.

2 - Transforming the response into JSON.

3 - Send a text to the client.

---
## Applying Decorators as Middlewares

`src/app.js`

We need to import our two decorators:

```js
const requestDecorator = require('./request')
const responseDecorator = require('./response')

```

Now, We have to modify our `serverHandler` function to apply the decorators as middlewares for all routes.

```js

const serverHandler = async (request, response) => {
        const sanitizedUrl = sanitizeUrl(request.url, request.method)

        const match = matchUrl(sanitizedUrl)

        if (match) {
            const middlewaresAndControllers = routes.get(match)
            await dispatchChain(request, response,
                [requestDecorator.bind(null, routes.keys()), responseDecorator, ...middlewaresAndControllers])
        } else {
            response.statusCode = 404
            response.end('Not found')
        }
    }

```
---
## Testing

`index.js`

```js

// (1)
app.get('/params/:id/:name', (req, res) => {
    res.end(JSON.stringify({ params: req.params, query: req.query }, null, 2))
})

// (2)
app.get('/response/:id', (req, res) => {
    if (req.params.id === '123') {
        res.status(200).json(req.params)
        return
    }

    res.status(400).json({ message: 'Invalid id' })
})
```

**From the code sessions above**: 

1 - Returning the parameters and query strings from the URL as text.

2 - Here is an example of how you can return JSON and change the status code at the same time.

---


## Conclusion

Using the same that We use for middlewares, we can treat the resquest and response and make them fancier.

You can take a look at the entire code [here](https://github.com/wesleymreng7/web-framework-scratch)

I hope you like it.