Tips to power up your Express.js backend
Mohit Sehgal
Building custom eCommerce Solutions | Full Stack Developer - Node.js PostgreSQL
The backend is the most important part of your web app. These tips help you make it stronger in terms of features, performance, and scalability.
The backend is one of the most important parts of any modern web app. There are many awesome frameworks but Express.js is my favorite.
In this write up I'll present some very important tips to power up your Express.js backend. These tips work for all other backend's also just the implementation changes.
Express.js is one of the most popular JavaScript Frameworks for the backend. It is very flexible but you need to set up a few things for any non-trivial web app. Here is the list.
Environment variables
Express.js doesn't provide support for environment variables out of the box. I recommend using?`dotenv`?package for this purpose. Simply include the following 2 lines before initializing the express.js server.
const dotenv = require('dotenv')
dotenv.config()
Next, create?a .env?file at the root of the project. e.g. We want to add a database URL to .env
DATABASE_URL=mongodb://localhost/ecommerce
This can be accessed from the code using `process.env` object.
const dbURL = process.env.DATABASE_URL
Request Validator
Input validation is one of the most common requirements for any web backend. For ExpressJS, you can use?`joi`?package.
`joi`?package allows validating an object against `Joi` schema using?`Joi.validate()`?function. The target object for our case is Express.js?`req.body`?or?`req.query`.
const schema = Joi.object().keys({
marks: Joi.number().min(0).max(100),
name: Joi.string()
});
const requestBody = req.body
const validationResult = schema.validate(requestBody)
if (validationResult.error){
//validation failed
}else{
//input data is valid
}
You can easily refactor this code to separate middleware.
Access Control
Access control system is very important in multi-role system. You may need to allow REST API access to users based on roles.
For example,
This can be implemented using?`accesscontrol`?npm package. Here is how you do it.
const AccessControl = require('accesscontrol')
const accessControl = new AccessControl()
//Creation of access control policy
accessControl
.grant('user')
.createOwn('profile')
.updateOwn('profile')
.readOwn('profile')
.deleteOwn('profile')
// middleware to check if given role has permission to access given endpoint
function(req,res,next) {
const permission = accessControl.permission({
role: req.user.role,
resource, //profile
action, //read:own
})
if (permission.granted) {
next() //access granted
} else {
//return error
res.status(403).json({
error: true,
message: 'Unauthorized Access',
})
}
}
Again this can be refactored to common middleware which can be used in all the REST Endpoints of your Express.js App.
Bcrypt the passwords and important information
Never store sensitive information like passwords as plain text in your database. Node.js provides?`bcrypt`?package to encrypt and verify information like passwords.
Here is a way to encrypt the password (or other sensitive information) in your database
const password = req.body.password
const saltingRounds = 10
const bcrypt = require('bcrypt')
let hashedPassword = bcrypt.hashSync(password, saltingRounds)
// save the hashedPassword
Verify if the password is correct
const isCorrectPassword = bcrypt.compareSync(inputPassword, storedHashedPassword)
if (isCorrectPassword){
//correct password
}
You can also use this for other use-cases like storing digital PINs.
JSON Web Tokens
JSON Web Tokens aka JWT is a secure way of transferring the information between parties (generally server and client)
Let me explain in an easier language.
Here is a quick illustration.
Here is how you do it in the express.js app.
In order to generate token.
const jwt = require('jsonwebtoken')
generateToken(user) {
let opts = {}
//options can identify the issuer and expiry time for the token
opts.issuer = config.jwt.issuer
opts.audience = config.jwt.audience
opts.expiresIn = 60 * 60;
return jwt.sign(user, config.jwt.secret, opts) //information in user object is encrypted in the token which can be used later
}
Important?JWT Secret should never be made public or committed to any VCS like GitHub, Bitbucket, or similar.
To Decode the information back, use?`jwt.decode`?function.
const decoded = jwt.verify(token, config.jwt.secret);
It will throw an error if?the token?is invalid or the secret is wrong.
CORS
Not everybody in the world should be able to access your REST endpoints. You need to limit them by origins.?cors?package allows you to define which origins can access your Express.js endpoints.
领英推荐
Here is how you allow only limited origins to access express.js routes.
const cors = require('cors')
const whitelist = [
'https://appsyoda.com',
'https://www.appsyoda.dev'
]
let corsOptionsDelegate = function (req, callback) {
let corsOptions
if (whitelist.indexOf(req.header('Origin')) !== -1) {
corsOptions = {
origin: true,
} // reflect (enable) the requested origin in the CORS response
} else {
corsOptions = {
origin: false,
} // disable CORS for this request
}
callback(null, corsOptions) // callback expects two parameters: error and options
}
app.use(cors(corsOptionsDelegate)) // app is express app object
Now if any other service or client tries to access your app, other than whitelisted ones, they will get the CORS error.
Helmet
`Helmet`?is a security package that can be used as a middleware in your express app. It sets various HTTP headers on each request.
It's very simple to use.
const helmet = require('helmet')
app.use(helmet())
It helps but it is not the silver bullet for your app security.
File upload to Amazon S3
Any non-trivial web app would need file upload functionality. Amazon S3 is among the best cloud services for managing files on the cloud.
For the Express.js app, you need 3 packages?`multer`,?`multer-s3`?and?`amazon-sdk`
Here is the snippet for That
const config = require('../../config'),
multer = require('multer'),
multerS3 = require('multer-s3'),
AWS = require('aws-sdk')
AWS.config.update({
accessKeyId: config.s3.accessKey,
secretAccessKey: config.s3.secretKey,
})
const s3 = new AWS.S3({
region: config.s3.region,
s3_signature_version: config.s3.s3_signature_version,
bucket: config.s3.bucketName,
})
// file upload code using s3 and multer
let uploadFile = multer({
storage: multerS3({
s3: s3,
bucket: config.s3.bucketName,
key: function (req, file, cb) {
cb(null, Date.now() + '-' + file.originalname)
},
}),
}).array('file', 1)
//uploadFile can be now used as middleware in any of the Express Routes.
router.post('/profile-pic', authenticate, uploadFile, UserController.updateProfilePic)
It can be modified to accept multiple files as well.
NodeMailer
Sending emails is a very basic function of any backend server. You may need to send transactional emails to your clients or you may need to send operational emails to your system admin.
`nodemailer`?allows you to send emails from your Node.js server. You can configure it using SMTP credentials
First, you need to configure the?transporter?object using SMTP credentials like this.
const nodemailer = require('nodemailer')
const config = require('../config') //setup using .env (dotenv)
const fromEmail = config.email.defaultFrom
let transporter = nodemailer.createTransport({
host: config.email.host,
port: config.email.port,
secure: true, // secure:true for port 465, secure:false for port 587
auth: {
user: config.email.username,
pass: config.email.password,
},
})
Then this transporter object can be used to send emails. Here is an example
async sendEmail(toEmail, emailSubject, emailText) {
let info = await transporter.sendMail({
from: fromEmail, // sender address
to: toEmail, // list of receivers
subject: emailSubject, // Subject line
html: emailText, // text body
})
return info
}
This function can be exported from a common service file and used anywhere in your app.
TDD Setup
This is very important. You need a way to automatically test important parts of your application with some testing framework.
Generally, there are 2 parts to it.
A testing framework like?mocha
As the name suggests, you need a framework/library to be able to write tests in your backend app.?mocha?is a very versatile yet easy-to-use framework which is widely used with Node.js apps for writing the specs.
Mocha provides?describe?and?it.?describe?is the keyword to signify which module is under test.?it?tells the mocha framework that it is an individual test.
Here is a quick example
describe("Product Management", function () {
describe("Create Product", function () {
it("creates product with valid data", function () {
});
it("returns error with invalid data", function () {
});
});
it("Delete Product", function () {
});
});
It also provides reporting options and parallelization of tests.
Assertion Library like?chai
It is a library that allows us to write assert statements. These are elemental to any testing framework. I recommend?the `chai`?assertion library to be used with Express.js. Refer?official documentation?for learning more about it.
Here is the simplest example of a chai test
const expect = require('chai').expect
const request = require('request')
const baseURL = 'https://api.appsyoda.com/' // change it to your actual base URL
describe('Check App Status', function () {
it('Returns Status 200', function (done) {
let url = baseURL
request(url, function (error, response, body) {
expect(response.statusCode).to.equal(200) //assert statement
done()
})
})
})
The above snippet makes an HTTP request to?baseURL?and asserts a 200 status code using?chai.
Faker
Faker is the library that allows you to create contextual fake data. Here is an example to generate a user name, email, and number.
const firstName = faker.name.firstName()
const lastName = faker.name.lastName()
const email = faker.internet.email(firstName, lastName)
const phone = faker.phone.phoneNumberFormat()
//use this to test sign-up endpoint
You can use it in combination with test frameworks.
Conclusion
There are a lot of other things which you can do. I have touched on the basics and most important aspects. For sure, we can dig deep into each of the aspects as well. Write me back for feedback.
If you find it helpful. Please share it and follow me on LinkedIn.
Original Post at https://www.appsyoda.com/blog/expressjs-tips/
Consultant, Full Stack Developer at Avanade
2 年??