From the RoR world to the kingdom of express.js. Part 1.
I have been writing API-applications with Ruby on Rails for years and i am pretty satisfied with it. Usual instruments that provide the desired usability are:
- Rails::API is a bit more lightweight and faster Rails
- PostgreSQL as a relational database
- Active Model Serializers for configuration the response’s appearance
This scope is a good fit for me. However, in these few-post series, I’ll look at the basics of the obtaining rails-like node.js
API instead. There are several reasons for that.
Firstly, last year we saw a huge rise of microservices popularity. Separating the large monolithic applications into microservices allows us to replace rails applications with something else.
Moreover, in some cases it would be better to avoid such heavyweight framework as rails.
Plus, when you write a node.js
application, you’re writing and learning Javascript. And the lifelong practice of learning is what makes us humans and our lives worthwhile.
So I have decided to make a try and to “translate” the familiar stack of technologies into Javascript.
In this part I am going to describe how to setup project that is based on the features of express.js. As the first step I am going to create a simple CRUD logic for models that I gave the name article
.
1. Create new express application
Let’s get it going by fetching express-generator
(it installs express
as an ensemble):
npm install -g express-generator
Once we’re done, we can generate new application and set dependencies up:
express example-express-api
cd example-express-api
npm install
The server could be already fired by typing npm start
, but for the better developing experience we need to install some tool for the monitoring code changes. Without it the server has to be restarted manually each time when the code is modificated. There is nodemon that will restart the node application automatically.
npm install -g nodemon
And then switch the lines in package.json
to:
"scripts": {
"start": "nodemon ./bin/www"
}
Type npm start
again and navigate to http://localhost:3000/
in the browser. We should see the “Welcome to Express” text. Voila, preparatory work is complete and we are ready to begin the actual development.
2. Using ES2015
Before we move on to the database details, let’s add an ability to support the modern standard of Javascript — ES2015. It runs by transpiling new syntax that ES2015 brings into the one supported by the browser. To put it into the action let’s install babel and preset for it.
npm install --save-dev babel-cli babel-preset-es2015
Then update the start
script in the package.json
file:
"scripts": {
"start": "nodemon ./bin/www --exec babel-node --presets es2015"
}
Because babel-cli
has been applied earlier, we have access to the babel-node executable, which transforms all of the code before running it through nodemon.
Optionally, for the finishing touch we could update generated by the express
files. Basically, we need to accomplish the following three things:
- substitute the things like
var routes = require('./routes/index')
toimport routes from './routes/index'
- use
let
instead ofvar
- introduce arrow functions. For example:
function(req, res, next) {...}
transforms to(req, res, next) => {...}
3. Connect PostgreSQL to application
Rails has a really awesome migration system. It allows us to evolve database schema overtime. I want to achieve this mechanism in my js-app too. As with many things in JS, there is a package that helps us to do this called pg-migrate.
npm install -g node-pg-migrate --save
And:
pg-migrate create create_articles
It generates new migration inside migrations
folder. Fill it out with the following code:
exports.up = (pgm) => {
pgm.createTable('articles', {
id: { type: 'serial', primaryKey: true },
title: { type: 'varchar(140)' },
body: { type: 'text' },
created_at: { type: 'timestamp' }
});
};
exports.down = (pgm) => {
pgm.drop('articles');
};
According to the documentaion for the successful migration we have to add .env
in the root directory and include require('dotenv').load();
at the top of app.js
file. In .env
I have to specify the database connection url by setting the environment variable DATABASE_URL
. For instance:
DATABASE_URL=postgresql://localhost:5432/my_blog
Here I set up a simple structure of the articles table. The particular article consists of two text fields (title and body) and one timestamp. It is time to create database with name my_blog
in psql
console and run the migration command by entering pg-migrate up
.
4. First couple of endpoints
Let’s keep it simple by adding new route file articles.js
inside routes
directory. Two things to note inside app.js
: new routes and specify base route. This is what the app.js
should contain:
import articles from './routes/articles';
app.use('/articles', articles);
Now, let’s build the first couple of endpoints: index and create. To make a request to database the connection pool should be initialized. It takes one argument presented url to database.
import express from 'express';
import pg from 'pg';
let router = express.Router();
router.get('/', (req, res, next) => {
pg.connect(process.env.DATABASE_URL, (err, client, done) => {
let results = [];
if (err) {
done();
return res.status(500).json({ success: false, data: err });
}
const query = client.query("SELECT * from articles ORDER BY id ASC");
query.on('row', (row) => {
results.push(row);
});
query.on('end', () => {
done();
return res.json(results);
});
});
});
module.exports = router;
For testing POST action I am using the POSTman extension for google chrome. We should send response via x-www-form-urlencoded
format. Data is available through res.body
attribute.
router.post('/', (req, res, next) => {
let data = { title: req.body.title, body: req.body.body };
pg.connect(process.env.DATABASE_URL, (err, client, done) => {
if (err) {
done();
return res.status(500).json({ success: false, data: err });
}
client.query("INSERT INTO articles(title, body, created_at) values($1, $2, $3)", [data.title, data.body, new Date()]);
res.send("OK");
});
});
In-between conclusion
It seems to take much more time to string everything together in express.js
. I guess the main reason is the lack of opionated structure. In next post I’m going to build remained endpoints to obtain the full CRUD interface and also polish routes by introducing controllers.