Cloud based services and applications are used in just about every aspect of our lives today whether we are checking our email, using social media, tracking personal fitness, or even monitoring a security system. Each of these applications contain some cloud-based service that can be used for handling the creation, retrieval, and update of data. There are many different technology stacks that can be utilized to accomplish a service, one that has continued to gain traction is node.js with the Express framework. The Express Seed seeks to provide a strong foundation for any type of web service. The groundwork has been laid for security, authentication and authorization, modularity, and maintainability.
You can find the source for the Express Seed here!
What You Need
- Beginner knowledge of node.js
- Express: A node.js web application framework that provides a thin layer of features vital to development.
- Sequelize: A promise based ORM (Object Relational Mapping) providing dialect mappings for PostgreSQL, MySQL, SQLite and MSSQL.
- PostgreSQL: An open source object-relational database.
- Route: An initial entry to the web service corresponding to HTTP methods (POST, GET, PUT, etc.). The route listens for incoming request and routes them to the lower layers.
- Controller: Contains business logic to be performed to fulfill the given requests. An example might be calling various database tables and aggregating their data to be returned to the client.
- Repository: The sole purpose is to communicate with the database.
The project directories are divided into global directories and features. Grouping files according to features prevents the directories from growing extremely large as can be the case with grouping by type. Furthermore, when using require we can typically require from the same directory keeping the require paths short.
- App: General app configurations such as ports, versioning, API pathing, server address.
- Auth: authorization parameters such as life of cookies, bcrypt intensity, maximum number of sessions.
- Limiter: The rate limiter configurations. The rate limiter protects against DOS attacks by limiting the number of requests from a single IP. This is an extremely powerful protection for such little configuration.
- Db: Obviously the database connection information as well as an important sync Boolean which can enable or disable Sequelize sync. Sometimes you’d rather not have the database being created from source such as if it isn’t your database!
- sessionLife: Controls how long session history is kept for and how often they are cleaned up.
The route.js acts like the route index file and simply contains the express router declarations. In our simple application we direct the router to the routes contained in our UserRoute file.
As defined before a route is the entry point to our service and should always be protected with authorization middleware. It is generally a good idea to protect against invalid data entry as well. Consider the POST route below.
There are two middleware layers that are called prior to executing the body of the route. The first is the authentication piece which we will talk about later, the second middleware utilizes the express-validation module providing a schema. The UserValidation file contains schemas for user route validations. The schema is a JSON schema that utilizes the joi module providing type validation and validation response.
Note the type validation, required fields, and the specification between body and query parameter validation. The PostUser route expects body parameters while the GetUser expects a single URL parameter of ID.
Once the authentication middleware and the validation middleware pass the request is passed to the body of the route. In our POST example the body calls the accompanying business logic class UserController to process the request.
Following the user POST into the business logic UserController we see that the function is pretty boring. There isn’t a lot to do other than pass the data to the persistence layer UserRepository and then formulate a proper HTTP response. This will not always be the case and you’ll find in larger applications that far more processing, data aggregation, and algorithmic permutation may be involved in this layer.
There are some interesting concepts to point out here even with this simplistic example. Brush up on promises if you’re not familiar with them as they are used heavily here to perform synchronous actions. The first item of interest is a call to the EncryptionHelper which is a helper class that you can probably guess encrypts the given password. This utilizes the bcrypt module to perform a one-way hash. This ensures that even if somehow your database is leaked, that your stored passwords are not in jeopardy. The second is a call into the UserRepository which you’ll remember is where our database communications happen.
The last item of interest is the response construction. Throughout your services you’ll probably find that a response like created is used over and over. Therefore, we’ve placed our reusable response objects in a constants file for use throughout the application. This reuse keeps the code maintainable as changing the created response for 1 or n routes is as simple as changing the same line in constants. Lastly before the response is constructed we call into the UserRepository
The following snippet from the UserRepository shows how easy database communication can be with an ORM such as Sequelize. Simply call create and Sequelize does the heavy lifting to insert the new record in the database. Any errors are caught and logged accordingly.
There is substantial amount of configuration to get Sequelize working correctly but once you’ve got the hang of it the time savings can be immense. You’ll also be protected against SQL injection attacks by the ORM as one more layer of security for your system. A benefit of using the seed project is a lot of the configuration has been done for you. The models directory contains the models associated with your database tables and the Index.js file in that directory handles the configuration of Sequelize. When you want to add a new table and model simply add the new model to the directory and restart the server.
Starting Sequelize is easy once your index file is up, just a few lines in the server.js and you can have it communicate with your database and even build your database from the models defined in your code. No more creating tables by hand through insert scripts!
Not using PostgreSQL? The database is pretty easy to swap out by changing the supported dialect in the /models/index.js.
Basic authentication and authorization have been provided using the passport.js module. We’ve defined a single authorization strategy that accepts a username and password in the request body. Our password is checked against the password in the DB using the EncryptionHelper that you’ll remember uses bcrypt. Offenders who provide incorrect credentials have their IP logged for auditing purposes.
Once the credentials are verified and authentication passes the user needs to have a session created for the login. The expiration of the session is set, a session uuid is generated using a 32-byte string, and the new session is created in the DB and passed back on the response. Now you’ve got a session cookie for authorization on all of your requests!
I hope you hung in there for the long haul. Where do things go from here? Anywhere really. You can use this as a starting point for just about any RESTful service without sweating a lot of the upfront configurations. This architecture is purposely kept simple so that it applies to the wide variety of use cases out there.