DEV Community

Cover image for Decouple your express applications using the amazing express-decorator-router
Marcos Henrique
Marcos Henrique

Posted on • Edited on

7 2

Decouple your express applications using the amazing express-decorator-router

Sick of that bunch of route files for our beloved express?

Looking for a way to use middleware and inject dependencies succinctly and clearly?

Looking for that package that will help you test your projects by decoupling intelligently?

Your problems end here and now, I present to you the package whose premise doesn't need transpilation, and can be used with koa, express or any package following express API.

You who want to escape from transpilation and are a hipster who likes the good old vanilla JS.

This package is intended to avoid unnecessary creation of two files where one contains the route definition and the other has the function that handles the route request/response processes, leaving simpler maintenance and more scalable code.

With only 32kb minified (source: bundlephobia), being able to inject the dependencies in a "manual" way or with the extremely tough tool awilix

Cool dude! But, how do I use it in practice?

Without Awilix

First let's talk about the root mode 👨‍💻👩‍💻

Registering your controllers with that mastery 🧐

The useControllers method uses two parameters, the first is the routing mechanism and the second is a glob expression that has the responsibility of finding all controllers that match the pattern of the expression.

server.js

const express = require('express')
const { resolve } = require('path')
const { useControllers } = require('express-decorator-router')

const app = express()
const router = express.Router()

app.use(express.json())

app.use('/api', useControllers({
    controllerExpression: `${resolve('src')}/**/controller.js`,
    router
}))

app.listen(3000, () => console.log('🔮 magic happens on port 3000'))
Enter fullscreen mode Exit fullscreen mode

The controller function returns a high order function where the decorator definition is made by associating a decorator with a class method as seen in the example above.

controller.js

const {
    get,
    put,
    del,
    post,
    controller
} = require('express-decorator-router')

class UserController {

    getUsers(ctx) {
        return ctx.response.json({ message: 'get all users' })
    }

    postUser(ctx) {
        const { user } = ctx.response.body
        return ctx.response.json({ message: `create user with name ${user}` })
    }

    putUser(ctx) {
        const { id } = ctx.request.params
        const { user } = ctx.request.body
        return ctx.response.json({ message: `update user with name ${user} with id ${id}` })
    }

    deleteUser(ctx) {
        const { id } = ctx.request.params
        return ctx.response.json({ message: `delete user with id ${id}` })
    }
}

module.exports = controller('/users')(UserController, {
    getUsers: get(),
    postUser: post(),
    putUser: put('/:id'),
    deleteUser: del('/:id')
})
Enter fullscreen mode Exit fullscreen mode

With Awilix

Awilix has a pretty simple API (but with many possible ways to invoke it). At minimum, you need to do 3 things:

  • Create a container
  • Register some modules in it
  • Resolve and use!

To make your life easier it already comes as an internal dependency so you will not need to install the same.

Now let's take a look at how it becomes even simpler to decouple your application with it.

Registering your controllers is even easier 🤩

server.js

const express = require('express')
const { resolve } = require('path')
const userService = require('./users/service')
const { useAwilixControllers, awilix, scopePerRequest } = require('express-decorator-router')

const app = express()
const router = express.Router()
const container = awilix.createContainer()

container.register({
    userService: awilix.asValue(userService).scoped()
})

app.use(express.json())
app.use(scopePerRequest(container))

app.use('/api/user', useAwilixControllers({
    controllerExpression: `${resolve('src')}/**/controller.js`,
    router
}))

app.listen(3200, () => console.log('🔮 magic happens on port 3200'))
Enter fullscreen mode Exit fullscreen mode

controller.js

const {get, controller, inject } = require('express-decorator-router')

const getUsers = (req, res) => {
    const { userService } = req
    return res.json(userService.getUsers())
}

module.exports = controller('/users', inject('userService'))({
    getUsers
}, {
    getUsers: get()
})
Enter fullscreen mode Exit fullscreen mode

Now you can be creative and discover highly scalable, testable and decoupled applications.

This package can be found on awesome-express list on middleware category

GitHub logo rajikaimal / awesome-express

:octocat: A curated list of awesome express.js resources

You can go even deeper into the repository documentation:

GitHub logo LucasMendesl / express-decorator-router

⚡ use decorators in a simple way without transpiling javascript code

express-decorator-router

NPM

use decorators in a simple way without transpiling javascript code

Why?

Have you ever considered using the decorators feature using vanilla javascript to automate the creation of express routes?

The express-decorator-router package came to solve this problem in a simple and didactic way, without the need for transpiling processes in your code.

This package is intended to avoid unnecessary creation of two files where one file contains the route definition and the other file has the function that handles the route request / response process, leaving simpler maintenance and more scalable code.

New in 0.2.0

Now, you can work with dependency injection.

Usage

Let's take a short example using the decorators on a prototype-based controller.

const {
  get,
  controller
} = require ('express-decorator-router')
const controllerFactoryDecorator = controller('/users')

class UsersController {
  constructor () {/*...class constructor definition*/}
        
  getUsers (ctx) {
Enter fullscreen mode Exit fullscreen mode

Sentry blog image

How I fixed 20 seconds of lag for every user in just 20 minutes.

Our AI agent was running 10-20 seconds slower than it should, impacting both our own developers and our early adopters. See how I used Sentry Profiling to fix it in record time.

Read more

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

AWS GenAI LIVE!

GenAI LIVE! is a dynamic live-streamed show exploring how AWS and our partners are helping organizations unlock real value with generative AI.

Tune in to the full event

DEV is partnering to bring live events to the community. Join us or dismiss this billboard if you're not interested. ❤️