How to migrate from Sails.js to Express.js (or how to finally reach land)
Tags: node, waterline, sailsjs, express
Published: 2019-11-05 12:51:54

---
title: How to migrate from Sails.js to Express.js (or how to finally reach land)
published: true
date: 2018-05-10 10:23:34 UTC
tags: nodejs,waterline,sailsjs,expressjs
canonical_url: https://medium.com/@Ecostack/how-to-migrate-from-sails-js-to-express-js-or-how-to-finally-reach-land-27972072c98f
---

![](https://cdn-images-1.medium.com/max/1024/1*RVia951vicq7LpQbISBlLw.jpeg)

At the company I am working for we had a pretty large project written in _Sails.js_. Now, after such a long time of development the project grew and grew and there was a need now for structural changes. Also it just became necessary to use a compiler for syntax checking.

So, we introduced _TypeScript_. This step was easy and could be quickly achieved with just a new _Grunt_ task and some modifications at the folder structure.

There is just one thing in _Sails.js_ which made _TypeScript_ less powerful_._ It makes all controllers, services and models available to the **GLOBAL** variable. This limits the possibilities of _TypeScript_ since _Sails.js_ always expects

```
module.exports = {
 ...
}
```

to be set.

With that size of a code base, it just becomes necessary to rely on features like type checking and code completion from _TypeScript_.

For these features we needed to implement classes. Now, having classes and the _Node.js_ standard export **module.exports** is not such an ideal combination.

A class looked like this:

{% gist https://gist.github.com/Ecostack/b877adf87deee9c5a1bb06f7df434f6f %}

So, after some try and error, it seemed like the keyword **export** would work for _Sails.js_ and we could use our required features from _TypeScript_.

This concept worked for some time, but in the end we faced sometimes issues with functions not being defined, depending on how the instance was accessed, via **GLOBAL** or via an import of the file.

This brought me then to the idea of the removal of _Sails.js_ and the implementation of _Express.js_ in combination with a full class driven approach for our code base.

It had also another huge benefit.

We could finally group files into sub folders. This was not possible, since _Sails.JS_ just reads the first layer of the folders it works with (services, controllers, models).

### The migration guide

So, how did we migrate in the end?

After some research of what modules are needed, it was more a task of **try and error** to see if the application boots and the unit tests are still running. :)

#### Custom app.js and server.ts(js) file

So, first step is to create a custom entry file for the application.

First, we created a new **app.js** and a **server.ts** file. The **server.ts** was created somewhere in the source directory and being a TypeScript file it has the benefits of being checked by the compiler.

The **app.js** file in the root folder would just call the compiled version of the **server.ts** file to start the application.

The **server.ts** file would look like your average _Express.js_ file except that you would add some extra in there, to make it work like _Sails.js_ in the beginning.

Constructing the server file was the major part of the migration, in the end.

There are a couple of things that need to be done:

#### Global Sails.js object

_Sails.js_ makes an object globally available which contains features like logging, config object, i18n.

To get the code up and running, it was the easiest to just simulate this behaviour:

{% gist https://gist.github.com/Ecostack/a3dbbd830a668394ed1e8c7550edb39d %}

Setup all the middleware

- CSRF
- CORS
- Locals (translations)
- wantsJSON (which has the same behaviour as Sails.js)
- Skipper (file uploads)
- Default response methods (res.ok() / res.serverError()/ …)

#### Routing and policies

In _Sails.js_ the routing and the policies are both set up with files, not in the code itself. This makes the migration quite time intensive, if you need to rewrite every route and its policies into code for the _Express.js_ router setup.

If the application is small, this would be not a huge problem. Our application though contains 700 REST routes and the equivalent amount of policies.

In the end, I ended up writing two parsers. One for the route setup, which would parse the **routes.js** and one for the policies, which would parse the **policies.js** file.

This had also the huge benefit of other developers could just proceed with their day to day development and extend these files all while I was modifying the core parts of the application. Business as usual could proceed.

#### Template engine

_Sails.js_ uses by default the **EJS** template engine.

This gave me a bit trouble, since the default setup of **EJS** did not work from the beginning with the setup of our **EJS** templates. There was an issue with how we use sub templates.

After some experimentation I found that it works properly with the package **express-ejs-layouts**.

This was the setup in the server file:

{% gist https://gist.github.com/Ecostack/fffdfb578585029818ec912b982b3c0b %}

Also the render methods needed to be changed.

_Sails.js_ implements a behaviour which, based on the controller, detects the correct template file.

The migration would go from:

{% gist https://gist.github.com/Ecostack/da2aa4b1d6b18681c9f21d166e3a8847 %}

to:

{% gist https://gist.github.com/Ecostack/8f776abf6e1c88435a1aeec580688dad %}

#### **What about the database layer?**

_Sails.js_ uses its own written database connector, **Waterline**.

This made it a bit more complex since **Waterline** is made to run inside of _Sails.js_. Now that there is no _Sails.js_ anymore, how would you trigger the startup? The documentation of the Github page does not give a lot of information on how to work with **Waterline** in your own project.

After some debugging of the documentation I came up with a class which replicates the behaviour of _Sails.js_.

The startup method would be called during the _Express.js_ startup.

I came up with this:

{% gist https://gist.github.com/Ecostack/bdbee47520d0bd48cd9efb37802063ab %}

Also I made it possible to access the model via the import and have the functions of **Waterline** available (find()/remove()/save()/…).

A model can now look like this:

{% gist https://gist.github.com/Ecostack/7b40252e28a978b42248e57a753ab584 %}

#### Socket.IO

Since we rely heavily on the socket implementation, we were in need to reimplement in nearly the same way.

To init the socket.io we first init the express server. The instance we get from the express server is then used to initiate an instance of socket.io.

{% gist https://gist.github.com/Ecostack/5974dc53f34c77f32ca18108abefd16e %}

Here we use the Redis adapter to keep multiple instances of our application synchronised.

The cookie parser is being used to, as it says, parse the cookies on the first connection from a browser.

After that the Socket.io instance gets started and as the final stage, there is some middleware applied to the Socket.io instance.

{% gist https://gist.github.com/Ecostack/0f7a7f97181ea7e2e00908066e445f44 %}

In the monitor you can listen on events which are coming in.

As you can see, this methods differs from the controller mapping approach of the _Sails.js_ _Socket.io_ implementation. It should not be too difficult to adapt to the _Socket.io_ event listening approach.

#### Last words

I am very happy with how everything turned out and how it works.

The next step for the future would be the migrate away from Waterline and towards Mongoose.

I hope you had the patience to read until this point and it might be helpful for you.