Creating your own ExpressJS from scratch (Part 4) - Modular router and global middlewares
Tags: node, javascript, webdev, programming
Published: 2023-04-23 22:45:02

It's time to deal deeper with middleware and create a way to allow the users from our framework to create their routes separately from the main app object.

Let's get started!

---

## Global Middlewares

`src/app.js`

```js
// (1)
const middlewaresForAll = []

// (2)
const use = (path, ...middlewares) => {
    const possiblePaths = [path + '/GET', path + '/POST', path + '/PUT', path + '/PATCH', path + '/DELETE']
    possiblePaths.forEach(route => {
        const middlewaresAndControllers = routes.get(route) || []

        if (middlewaresAndControllers.length) {
            routes.set(route, [...middlewares, ...middlewaresAndControllers])
        }
    })
}

// (3)
const useAll = (...middlewares) => {
    middlewaresForAll.push(...middlewares)
}

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

    const match = matchUrl(sanitizedUrl)

    if (match) {
        const middlewaresAndControllers = routes.get(match)
// (4)
        await dispatchChain(request, response,
            [requestDecorator.bind(null, routes.keys()), responseDecorator, ...middlewaresForAll, ...middlewaresAndControllers])
    } else {
        response.statusCode = 404
        response.end('Not found')
    }
}
```
**From the code sessions above**

1 - We need an array to store the middlewares that should be applied for all routes.

2 - The `use` function is responsible to add middleware for a specified path independently of the used method.

3 - The `useAll` is a way to add middleware to the middleware store.

4 - Now we can modify our `serverHandler` function to apply the array of middlewares for all routes.

---

## Modular Router

It is a mini-app that we can include in our main application.

`src/router.js`

```js
const Router = () => {
  const routes = new Map()
  const middlewaresForAll = []

  const getRoutes = () => {
    return routes
  }

  const getMiddlewaresForAll = () => {
    return middlewaresForAll
  }

  const useAll = (...middlewares) => {
    middlewaresForAll.push(...middlewares)
  }

  const use = (path, ...middlewares) => {
    const possiblePaths = [path + '/GET', path + '/POST', path + '/PUT', path + '/PATCH', path + '/DELETE']
    possiblePaths.forEach(route => {
      const middlewaresAndControllers = routes.get(route) || []

      if (middlewaresAndControllers.length) {
        routes.set(route, [...middlewares, ...middlewaresAndControllers])
      }
    })
  }

  const get = (path, ...handlers) => {
    const middlewaresAndControllers = routes.get(`${path}/GET`) || []
    routes.set(`${path}/GET`, [...middlewaresAndControllers, ...handlers])
  }

  const post = (path, ...handlers) => {
    const middlewaresAndControllers = routes.get(`${path}/POST`) || []
    routes.set(`${path}/POST`, [...middlewaresAndControllers, ...handlers])
  }

  const put = (path, ...handlers) => {
    const middlewaresAndControllers = routes.get(`${path}/PUT`) || []
    routes.set(`${path}/PUT`, [...middlewaresAndControllers, ...handlers])
  }

  const patch = (path, ...handlers) => {
    const middlewaresAndControllers = routes.get(`${path}/PATCH`) || []
    routes.set(`${path}/PATCH`, [...middlewaresAndControllers, ...handlers])
  }

  const del = (path, ...handlers) => {
    const middlewaresAndControllers = routes.get(`${path}/DELETE`) || []
    routes.set(`${path}/DELETE`, [...middlewaresAndControllers, ...handlers])
  }

  return {
    get,
    post,
    put,
    patch,
    del,
    use,
    useAll,
    getRoutes,
    getMiddlewaresForAll
  }

}

module.exports = Router
```
That is all we have in the main app, the only difference we need to return `routes` and `middlewaresForAll` too. 

To use our modular router we need to get the router handlers and combine them with existent routes.


`src/app.js`

```js
const useRouter = (path, router) => {
        const routerRoutes = router.getRoutes()
        const middlewaresFromRouter = router.getMiddlewaresForAll()
        const existentHandlers = routes.get(path) || []
        routerRoutes.forEach((middlewares, key) => {
            routes.set(`${path + key}`, [...existentHandlers, ...middlewaresFromRouter, ...middlewares])
        })
    }

```

---

## Testing

```js
router.get('/users', (req, res) => res.end('User route from router instance'))
router.get('/admins', (req, res) => res.end('Admins route'))

router.useAll((req, res, next) => {
    console.log('middleware for router instance /admins and /users')
    next()
})

router.use('/users', (req, res, next) => {
    console.log('middleware for /users')
    next()
})

router.use('/admins', (req, res, next) => {
    console.log('middleware for /admins')
    next()
})


app.useRouter('', router)

app.use('/admins', (req, res, next) => {
    console.log('middleware for all admins routes')
    next()
})

app.useAll((req, res, next) => {
    console.log('middleware for all routes')
    next()
})
```

If I try to access `admins` route running in a terminal this command:

```
curl --location 'localhost:3000/admins'
```

You must see in the console something like that:

```
middleware for all routes
middleware for all admins routes
middleware for router instance /admins and /users
middleware for /admins
```

---

That's all for now. You can see the entire code [here](https://github.com/wesleymreng7/web-framework-scratch)

In the next tutorial, I will show how to serve static files.

See you soon!