Project structure

The complete code for the backend server is available in CH07/server. In this chapter, we are going to understand how the REST API can be built. Start with an empty folder and follow these steps:

  1. Initiate the project with yarn init or by creating a package.json file. We are already familiar with the process of adding and removing any npm packages from the project. Simply copy the package.json file from CH08 into your new project folder. 
  2. Create a folder called server where we can place all our backend logic. We are going to use the express framework to create backend. Inside the server folder, create a file called index.js. Inside the file, we initiate the express server with the required parameter, as follows: 
/* eslint consistent-return:0 import/order:0 */

const path = require('path');
const express = require('express');
const cookieParser = require('cookie-parser');
const methodOverride = require('method-override');
const session = require('express-session');
const bodyParser = require('body-parser');
const moduleAlias = require('module-alias');
moduleAlias.addAlias('@server', __dirname);

const logger = require('./logger');

const argv = require('./argv');
const port = require('./port');

const app = express();
require('./helpers/prototype');
require('./models');

const secret = process.env.SECRET || 'AAdasds23djasd3ASd2ss@';

app.use(bodyParser.json({ limit: '50mb' }));
app.use(bodyParser.urlencoded({ limit: '50mb', extended: true }));
app.use(cookieParser());
app.use(methodOverride('X-HTTP-Method-Override'));
app.use(session({ secret, resave: false, saveUninitialized: true }));
app.use(express.static(path.join(__dirname, '../build/')));

require('./api')(app);

const customHost = argv.host || process.env.HOST;
const host = customHost || null;
const prettyHost = customHost || 'localhost';

app.get('*.js', (req, res, next) => {
req.url = req.url + '.gz'; // eslint-disable-line
res.set('Content-Encoding', 'gzip');
next();
});

app.listen(port, host, async err => {
if (err) {
return logger.error(err.message);
}

logger.appStarted(port, prettyHost);
});

We are using cookie-parser to store the token of a logged-in user. We also added our root of the models folder, require('./models'). In the root model's folder, we will connect to the MongoDB database. 

  1. Use a logger.js file to log messages on the Terminal. Create a logger.js file inside CH08/server/logger.js:
/* eslint-disable no-console */

const chalk = require('chalk');
const ip = require('ip');

const divider = chalk.gray(' -----------------------------------');

const logger = {
error: err => {
console.error(chalk.red(err));
},

appStarted: (port, host) => {
console.log(`Server started ! ${chalk.green('✓')}`);

console.log(`
${chalk.bold('Access URLs:')}${divider}
Localhost: ${chalk.magenta(`http://${host}:${port}`)}
LAN: ${chalk.magenta(`http://${ip.address()}:${port}`)}${divider}
${chalk.blue(`Press ${chalk.italic('CTRL-C')} to stop`)}
`);
},
};

module.exports = logger;
  1. Use the provided port or default port of 3000. The logic goes inside the port.js file, inside CH08/server/port.js:
const argv = require('./argv');

module.exports = parseInt(argv.port || process.env.PORT || '3000', 10);
  1. Create a new file, argv.js, inside CH08/server/argv.js. We are going to use the minimist package, which helps to parse argument options:
module.exports = require('minimist')(process.argv.slice(2));
  1. Create a models folder. Create an index.js file inside it. We are going to connect to MongoDB inside it (we installed MongoDB in Chapter 6, Extending Redux by Middleware. If you don't have it, read more about how to install MongoDB at https://www.mongodb.com/ or follow the instructions in Chapter 6Extending Redux by Middleware):
const fs = require('fs');
const path = require('path');
const mongoose = require('mongoose');
const mongooseDelete = require('mongoose-delete');

const uristring = process.env.MONGODB_URI || 'mongodb://localhost/rask-lege';

mongoose.connect(
uristring,
err =>
console.log(
err
? `ERROR, connecting to: ${uristring}. ${err}`
: `Succeeded connected to: ${uristring}`,
),
);

const db = {};

fs.readdirSync(__dirname)
.filter(
file =>
file.indexOf('.') !== 0 &&
file !== 'index.js' &&
file !== 'migrations' &&
file !== 'seeds.js',
)
.forEach(file => {
// eslint-disable-next-line global-require
const model = require(path.join(__dirname, file))(mongoose, mongooseDelete);
db[model.collection.collectionName] = model;
});

module.exports = db;

At the end of this, your folder structure should look like the following screenshot:

 

Figure 8.1 - Project folder structure

Mongoose (https://mongoosejs.com/) is a MongoDB object-modeling tool that is created to function in an asynchronous environment. Installing mongoose is straightforward, as follows: 

yarn add mongoose --exact
or
npm install mongoose --save-exact
  1. Let's create our first users model and add a schema to it. Inside the models folder, create a file called users.js (you can read more about the list of available data types for defining schema on the MongoDB documentation website):
module.exports = (mongoose, mongooseDelete) => {
const Schema = new mongoose.Schema(
{
email: {
type: String,
trim: true,
unique: true,
},
username: String,
password: String,
loginKey: String,
secondaryEmail: String,
active: {
type: Boolean,
default: false,
},
name: String,
telecom: [
{
use: String,
rank: Number,
value: String,
system: String,
period: String,
},
],
birthDate: Date,
gender: {
type: String,
default: 'other',
enum: ['male', 'female', 'other'],
},
role: {
type: String,
default: 'user',
enum: ['user', 'practitioner', 'patient', 'admin'],
},
address: [
{
use: String,
type: String,
text: String,
line: String,
city: String,
district: String,
state: String,
postalCode: String,
country: String,
period: String,
},
],
photo: String,
language: String,
preferredLanguage: String,
maritalStatus: {
type: String,
enum: ['married', 'unmarried', 'divorced', 'separated',
'widow'],
},
qualifications: [String],
},
{
timestamps: true,
},
);
  1. Create another folder called helpers, and create a file inside it named prototype.js. We will add utility functions inside it so that we can use it later. We have added a new method, humanize, in the String class, https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/prototype:
/* eslint-disable */

String.prototype.humanize = function () {
return this.replace(/(?:_| |)(w)/g, function(key, p1) {
return p1.toUpperCase()
});
};
  1. Create a routing file inside server/api/index.js. We are going to write all our endpoints in this file:
const express = require('express');
const User = require('./user');
const Practitioner = require('./practitioner');

const { authenticate, injectUserToReq } = require('../helpers/auth');

module.exports = app => {
const router = express.Router();
const routerAuth = express.Router();

router.post('/users', User.signup);
router.post('/users/signin', User.signin);

routerAuth.get('/users/auth', User.auth);
routerAuth.get('/users', User.index);
routerAuth.get('/users/:id', User.show);
routerAuth.put('/users/:id', User.update);
routerAuth.delete('/users/:id', User.destroy);

router.get('/practitioners', Practitioner.index);
routerAuth.post('/practitioners', Practitioner.create);
routerAuth.put('/practitioners/:id', Practitioner.update);
routerAuth.delete('/practitioners/:id', Practitioner.destroy);

app.use('/api', injectUserToReq, router);
app.use('/api', authenticate, routerAuth);
};

The following table specifies the operations of our API in detail:

Method URI Description
GET /users Retrieves all the available users
GET /users/auth Retrieves the authenticated users
GET /users/:id Retrieve the user with the ID identifier
GET /practitioners Retrieves the list of medical practitioners 
POST /users Creates a user with the given information
POST /users/signin Signs in a user with the given credentials
POST /practitioners Creates a new medical practitioner
PUT /users/:id Updates a user
PUT /practitioners/:id Updates a practitioner
DELETE /users/:id Deletes an existing user
DELETE /practitioners/:id

Deletes an existing practitioner

Table 8.1: List of endpoints 
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset