Sloth
Sloth

Reputation: 53

Express Routes & Controllers

I understand that out of the box Express isn't an MVC framework, however I'm trying to set it up like one.

I've used other similar frameworks in PHP like Laravel where in a route in you can use a controller like

Route::get('user/profile', 'UserController@showProfile');

Which will run all the code in the showProfile method in the UserController class, so my question is, how would I achieve the same thing or something similar using Express?

I'm using Node 5 and writing the code in ECMAScript 6.

Currently I have a class I want to use as the controller and a method I want to return the data, I'm able to log the data to the console when a user navigates to the route but haven't figured out how to send it back as the response.

Upvotes: 1

Views: 2406

Answers (2)

kranthi
kranthi

Reputation: 1

I did something like this

usercontroller.js

class UserController() {
  constructor() {
    this.users = ['user1', 'user2'];
  }

  getUsers(req, res) {
    res.status(200).json(this.users);
  }
}

//router.js 
const port = 3000;
const app = express();


const _invoke = function(controller) {
  return function(req, res) {
    const [controllerClass, method] = controller.split('@')
    const className = require(controllerClass)
    const obj = new className
    obj[method](req, res)
  }
}

app.get('/get-users',
  _invoke('./controllers/UserController@getUsers'));

app.listen(port);

Upvotes: 0

Esteban
Esteban

Reputation: 2579

If you dive into the documentation, you'll find that the "controller methods" you refer to need to conform to a specific signature. Namely, they receive (at least) the request and response representations.

If you have already created a router, this will be a rough equivalent to the PHP you posted:

router.get('user/profile', userController.showProfile)

Your showProfile "method" needs to have this signature:

const userController = {
  showProfile(req, res) { /*...*/}
}

I put "method" in quotes because express won't call it as a method unless you explicitly bind it to the controller object. We're passing it as an unbound function here. If you wanted to use it as a method (to have access to the controller as this), pass userController.showProfile.bind(userController) to router.get.

But for now let's stick to those req and res parameters of the showProfile handler (that's the proper name). req represents the HTTP request, you can get headers, request payload and other stuff from it. res is the HTTP response that will be sent. So you can use it to set an HTTP status code, send body data and so on.

For illustrative purposes, let's assume you can get your user profile synchronously by calling userController.getProfile(id). And let's assume that, by some mechanism, a property userId is set on the request that is the currently authenticated user's ID.

const userController = {
  showProfile(req, res) {
    // We call some code to get what we want to send back
    const profile = userController.getProfile(req.userId)
    // We send it in the response as JSON
    res.send(profile)
  }
}

res.json will JSON.stringify the profile and send it as response payload.

How do you get req.userId set, you ask? Well, this is just an example, but you can achieve similar results by using middleware. A middleware is simply a handler that does something and then lets other handlers continue processing the request. But again, there's plenty to read from the docs.

† It's usually not necessary though, since controllers tend to be singletons. You can simply access its properties by doing userController.otherProperty. Of course, you don't even need to define a handler as a method of a controller object, it can be a function that stands on its own.

Upvotes: 2

Related Questions