I am mobile app developer. I need some backend service to manage user data in remote databases quite frequently. Of course, I could use some BaaS (Parse, Backendless, etc…). But good own solution is always a more convenient and practical choice.
I decided to explore completely unknown technologies, which are now very popular and are positioned as easily assimilated by newcomers and do not require in-depth knowledge and experience to implement large-scale projects.
You can grab a final project from GitHub. Run npm install in projects folder for installation of all required modules.
1. Node.js + Express.js, simple web-server
Node.js has a non-blocking i/o. That’s great for API services which will be accessed by many clients. Express.js is an advanced, lightweight framework that allows us to quickly describe all the needed API endpoints. It also supports many useful modules.
Let’s create a new project with a single file server.js. Since the application will rely on Express.js, we’ll install it. Installing third-party modules through Node Package Manager is simple: npm install modulename in the project folder.
Express will be installed in node_modules folder. Now connect it to the application:
Run the application through the IDE or console (node server.js). This code will create a web server on localhost:1337. It now displays a message Cannot GET /. This is because we haven’t configured any routes yet. Next, let’s create some routes and configure basic settings of Express.
Now localhost:1337/api returns our message (handled by app.router). localhost:1337 displays index.html (handled by express.static()).
Next step is error handling.
2. Error handling
First connect a cool logging module Winston. We will make a wrapper for it. Run npm i winston in project root, then create a folder named libs/ with log.js there.
We created one transport for logging – console. You can separately sort and store logs in different transports, such as a database or file. Connect the logger to server.js.
Info message now passes through Winston to its transport – the console.
Next – 404 and 500 error handling.
Now, if there are no suitable routes, Express will return our message. When an internal error occurs, it will be passed to the handler, you can check it on localhost:1337/ErrorExample.
3. RESTful API endpoints, CRUD
Let’s add a way to handle some “articles”. Implementation will be empty for now, it will be fixed in the next step, after connecting to a database.
To test post/put/delete I am advising a wonderful wrapper over cURL - httpie. I will give examples of requests by using this tool.
4. MongoDB & Mongoose.js
Choosing a database, I was guided by the desire to once again explore something new. MongoDB - the most popular NoSQL document-oriented database. Mongoose.js - wrapper, allowing to create comfortable and functional schema documents.
Download and install MongoDB. Than, install Mongoose: npm i mongoose. I will put database interaction in separate module: libs/mongoose.js.
In this file, connection to the database is implemented and object scheme are declared. Articles will contain picture objects. A variety of complex validation can be implemented here as well.
I will use nconf module to store there database path. Also, let’s move a server port number there. The module is installed by npm i nconf. Custom wrapper will be libs/config.js.
All the settings will be stored in config.json at the project’s root.
Let’s add CRUD actions in existing routes.
All operations are very clear, thanks to Mongoose and self-explanatory scheme. Now, before running our node.js, we need to run MongoDB server: mongod. mongo - is a client utility for working with the database, the service itself is mongod.
Request examples using httpie:
You can checkout the project at this stage from Github.
5. Access control — OAuth 2.0, Passport.js
We will use OAuth 2. Perhaps this is redundant, but in the future, this approach facilitates an integration with other OAuth-providers.
Module Passport.js will be responsible for access control. For OAuth2 server, I will use handy solution from the same author - OAuth2orize. Access tokens will be stored in MongoDB.
First you need to install all the required modules:
Then, you need to add mongoose.js scheme for users and tokens:
Virtual property password is an example of how mongoose model can embed convenient logic. Hashing algorithms and salt is not in this article’s scope, so we won’t dig into the details of the implementation.
User – a user who has a name, password hash and a salt.
Client – a client application which requests access on behalf of a user, has a name and a secret code.
AccessToken – token (type of bearer), issued to the client application, limited by time.
RefreshToken – another type of token allows you to request a new bearer-token without re-request a password from the user.
Add token lifetime to config.json:
I implemented OAuth2 server and authorization logic in separate modules. In auth.js passport.js strategies are described. We connect 3 strategies – 2 for verifying Client credentials in OAuth2 username-password flow and one to check the token.
BasicStrategy and ClientPasswordStrategy are responsible for Client credentials verification. They share similar code because they are interchangeable, we can use one or another. From OAuth 2.0 spec we can see that “HTTP Basic authentication” is required and “client credentials in the request body” is optional (for clients who don’t support HTTP Basic auth). We implemented both.
oauth2.js is responsible for the issuance and renewal of the token. One token exchange strategy is for username-password flow, another is to refresh tokens.
Connect these modules with server.js:
For example, the access is restricted on localhost:1337/api/userInfo.
To check the auth logic, we should create a user and a client in our database. Use this node application, which will create the necessary objects and remove redundant from collections. It helps quickly clean the tokens and users for testing.
If you used dataGen.js following commands to test authorization will fit you well. Let me remind you that I am using httpie.
Attention! On production always use HTTPS, it is implicit in OAuth 2 specification. And do not forget to do correct password hashing.
Let me remind that you can find the working example at the repository on GitHub.
To start example project, you should run npm install in project root, then run mongod, node dataGen.js (wait for completion), and then node server.js.
If any part of the article is worth to be described more clearly, please contact me by email or twitter.
To summarize, I want to say that node.js is a great, convenient server solution. MongoDB document-oriented approach is a very unusual, but certainly a useful tool. It also has a lot of features that I have not used yet. Node.js has a very large community and there are many open-source projects that come along. For example, the creator of the oauth2orize and passport.js, Jared Hanson makes wonderful projects that facilitate the implementation of the most well-protected systems.