Before you learn about the complex subject of authentication, let's reorganize the back-end structure to make it easier to understand and maintain. While it is technically possible to leave all of the routing and business logic in your app.js
file, it can quickly become far too large to maintain, so let's make things a bit more modular.
Set Up Routing
Let see in this next video how to set up routing!
The first thing you must do is separate your routing logic. Create a new folder inside your back-end folder called routes
, and create a file inside that called stuff.js
, which will contain the logic for your stuff
routes:
const express = require('express');
const router = express.Router();
module.exports = router;
Here, you are creating an Express router. Until now, we have been registering routes directly to the app. Now you'll register them to your Express router and then register that router to the app.
It's time to cut all of the routes from app.js
and paste them inside your router. Be careful to replace all occurrences of app
with router
, as you are registering the routes to your router:
const express = require('express');
const router = express.Router();
const Thing = require('../models/thing');
router.post('/', (req, res, next) => {
const thing = new Thing({
title: req.body.title,
description: req.body.description,
imageUrl: req.body.imageUrl,
price: req.body.price,
userId: req.body.userId
});
thing.save().then(
() => {
res.status(201).json({
message: 'Post saved successfully!'
});
}
).catch(
(error) => {
res.status(400).json({
error: error
});
}
);
});
router.get('/:id', (req, res, next) => {
Thing.findOne({
_id: req.params.id
}).then(
(thing) => {
res.status(200).json(thing);
}
).catch(
(error) => {
res.status(404).json({
error: error
});
}
);
});
router.put('/:id', (req, res, next) => {
const thing = new Thing({
_id: req.params.id,
title: req.body.title,
description: req.body.description,
imageUrl: req.body.imageUrl,
price: req.body.price,
userId: req.body.userId
});
Thing.updateOne({_id: req.params.id}, thing).then(
() => {
res.status(201).json({
message: 'Thing updated successfully!'
});
}
).catch(
(error) => {
res.status(400).json({
error: error
});
}
);
});
router.delete('/:id', (req, res, next) => {
Thing.deleteOne({_id: req.params.id}).then(
() => {
res.status(200).json({
message: 'Deleted!'
});
}
).catch(
(error) => {
res.status(400).json({
error: error
});
}
);
});
router.get('/' +
'', (req, res, next) => {
Thing.find().then(
(things) => {
res.status(200).json(things);
}
).catch(
(error) => {
res.status(400).json({
error: error
});
}
);
});
module.exports = router;
You now need to register your new router in your app.js
file. First, import it:
const stuffRoutes = require('./routes/stuff');
Then register it as you would a single route. You want to register your router for all requests to /api/stuff
, so:
app.use('/api/stuff', stuffRoutes);
Now, if you refresh the front-end app (stay in the section for parts 1 + 2 for now), everything should still function as before.
Set Up Controllers
Let see in this next video how to set up controllers!
Now separate your routes' business logic into controllers to make the structure even more modular and the code easier to read and maintain.
Create a new controllers
folder in your backend
folder, and create another stuff.js
file — this one will be your stuff controller. Next, copy your first piece of business logic from your POST route to your controller:
// in controllers/stuff.js
const Thing = require('../models/thing');
exports.createThing = (req, res, next) => {
const thing = new Thing({
title: req.body.title,
description: req.body.description,
imageUrl: req.body.imageUrl,
price: req.body.price,
userId: req.body.userId
});
thing.save().then(
() => {
res.status(201).json({
message: 'Post saved successfully!'
});
}
).catch(
(error) => {
res.status(400).json({
error: error
});
}
);
};
Here, you are exposing the logic from your POST route as a function called createThing()
. Import your controller, and then register createThing
to implement this back in your route.
// in routes/stuff.js
const stuffCtrl = require('../controllers/stuff');
router.get('/', stuffCtrl.getAllStuff);
You can now do the same for all of your other routes. Here is the final controller:
const Thing = require('../models/thing');
exports.createThing = (req, res, next) => {
const thing = new Thing({
title: req.body.title,
description: req.body.description,
imageUrl: req.body.imageUrl,
price: req.body.price,
userId: req.body.userId
});
thing.save().then(
() => {
res.status(201).json({
message: 'Post saved successfully!'
});
}
).catch(
(error) => {
res.status(400).json({
error: error
});
}
);
};
exports.getOneThing = (req, res, next) => {
Thing.findOne({
_id: req.params.id
}).then(
(thing) => {
res.status(200).json(thing);
}
).catch(
(error) => {
res.status(404).json({
error: error
});
}
);
};
exports.modifyThing = (req, res, next) => {
const thing = new Thing({
_id: req.params.id,
title: req.body.title,
description: req.body.description,
imageUrl: req.body.imageUrl,
price: req.body.price,
userId: req.body.userId
});
Thing.updateOne({_id: req.params.id}, thing).then(
() => {
res.status(201).json({
message: 'Thing updated successfully!'
});
}
).catch(
(error) => {
res.status(400).json({
error: error
});
}
);
};
exports.deleteThing = (req, res, next) => {
Thing.deleteOne({_id: req.params.id}).then(
() => {
res.status(200).json({
message: 'Deleted!'
});
}
).catch(
(error) => {
res.status(400).json({
error: error
});
}
);
};
exports.getAllStuff = (req, res, next) => {
Thing.find().then(
(things) => {
res.status(200).json(things);
}
).catch(
(error) => {
res.status(400).json({
error: error
});
}
);
};
And the final router:
const express = require('express');
const router = express.Router();
const stuffCtrl = require('../controllers/stuff');
router.get('/', stuffCtrl.getAllStuff);
router.post('/', stuffCtrl.createThing);
router.get('/:id', stuffCtrl.getOneThing);
router.put('/:id', stuffCtrl.modifyThing);
router.delete('/:id', stuffCtrl.deleteThing);
module.exports = router;
As you can see, this makes your router file easier to understand. In addition, it is clear which routes are available at which endpoints, and the descriptive controller function names make it easier to understand each route.
While not necessary for every project, structuring code in a modular manner is a good habit, making maintenance far easier.
Let's Recap!
express.Router()
lets you create separate routers for each main route in your app — you then register individual routes to that router.A controller file exports methods which are then attributed to routes to improve your app's maintainability.
Now that's all ready, let's start implementing user authentication in the next chapter!