DEV Community

Cover image for Creating your own ExpressJS from scratch (Part 3) - Handling Request and Response objects
Wesley Miranda
Wesley Miranda

Posted on • Edited on

Creating your own ExpressJS from scratch (Part 3) - Handling Request and Response objects

In part 2, 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

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
Enter fullscreen mode Exit fullscreen mode

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

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
Enter fullscreen mode Exit fullscreen mode

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:

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

Enter fullscreen mode Exit fullscreen mode

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


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')
        }
    }

Enter fullscreen mode Exit fullscreen mode

Testing

index.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' })
})
Enter fullscreen mode Exit fullscreen mode

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

I hope you like it.

Top comments (1)

Collapse
 
duongphan profile image
Thanh Dương Phan

Nice bro