• 10 hours
  • Medium

Free online content available in this course.

course.header.alt.is_video

course.header.alt.is_certifying

Got it!

Last updated on 2/21/22

Optimize the Back End's Structure

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!

Example of certificate of achievement
Example of certificate of achievement