ખીલો ખોદતા ખાણ જો મળે ઝવેરાતની, તો નિરાંતે તારી ભક્તિ કરું શામળા | ભલું થયું ભાંગી જંજાળ, નવી નક્કોર દેશે શ્રીગોપાળ | જે તુજથી ના થઈ શકે એ જ કરાવ પ્રભુ પાસે, પાણિયારું પ્રભુ ભરશે નહિ ભરશે નદી તળાવ

Kiran Chauhan

Cogito, ergo sum // I think, therefore I am.

Chapter 02: Customizing Sails Apps

—

In the previous chapter, we created a small Sails application and explored the basics of its MVC functionality. We saw how to define routes, create controllers and models, and render a view.

In this chapter, we’ll explore some ways to customize a Sails application. We’ll start with simple changes, such as changing the port, and gradually move towards customizing the functionalities that Sails provides.

This chapter is part of my book, Understanding and Using Sails.js, which I'm writing and publishing chapter by chapter.

You can read the first few chapters for free on this blog. Visit the book's dedicated page for more information, or head over to Gumroad to purchase a copy and/or support the ongoing writing of this book.

The idea is to understand how much control Sails gives us over the application without requiring us to modify the framework itself.

---

Let’s start by changing the port. By default, Sails starts the server on port 1337. But what if we want to run our application on a different port? We can pass configuration options to the sails.lift() method in the app.js file. Let’s change the port to 3000.

const sails = require('sails');

sails.lift({
    port: 3000,
});

That’s it! Restart the application, and Sails will start the server on port 3000 instead of the default 1337 port. You can now visit http://localhost:3000 in your browser to access the application. This is a simple example of how we can customize a Sails application by passing configuration options when lifting the server.

---

If you’ve run a Sails application, you’ve probably noticed the ASCII ship displayed in the terminal when the application starts. It’s a nice touch, but we might not always want to see it. For example, we may prefer cleaner console output when running our application. Sails provides the log.noShip option for this purpose. Let’s update the code in our app.js file.

const sails = require('sails');

sails.lift({
    port: 3000,
    log: {
        noShip: true,
    },
});

We’ve added the log configuration object and set noShip to true. Now, Sails won’t display the ship during startup. Notice that we’re still using the same sails.lift() method. We’re simply passing an additional configuration option. Restart the application, and the ship should disappear from the console!

---

When starting the application, you might notice some messages related to sessions in the console. If our sandbox application doesn’t need session support, we can disable the session hook.

When starting the application, you might notice following messages related to sessions in the console. If our sandbox application doesn’t need session support, we can disable the session hook.

debug: Warning: no session secret was set!  In development mode, we'll set this for you,
debug: but session secret must be manually specified in production.
debug: To set up a session secret, add or update it in `config/session.js`:
debug: module.exports.session = { secret: 'extremely-secure-keyboard-cat' }
debug: 
debug: (Or if you don't need sessions enabled, disable the hook.)

Sails uses hooks to organize and extend its functionality. A hook can provide a particular capability to the application, and Sails loads its built-in hooks during startup. Let’s disable the session hook by setting hooks.session to false in the app.js file.

const sails = require('sails');

sails.lift({
    port: 3000,
    log: {
        noShip: true,
    },
    hooks: {
        session: false,
    },
});

We’ve now disabled the session hook in addition to hiding the ship. Keep in mind that disabling the session hook is not merely a way to hide console messages. It also disables session functionality, which may be required by parts of your application. For our experimental sandbox, we can disable it if we don’t need sessions for now. With these options in place, the startup output should be much quieter.

---

Now that we’ve reduced the default startup output, let’s print a message of our own. The sails.lift() method accepts a callback that runs after the application has finished lifting. We can use this callback to print a message when the application is ready.

const sails = require('sails');

sails.lift(
    {
        port: 3000,
        log: {
            noShip: true,
        },
        hooks: {
            session: false,
        },
    },
    () => {
        console.log('Application is up and running on port 3000');
    }
);

We’ve moved the configuration options into the first argument and added a callback as the second argument. Once Sails has finished starting the application, the callback prints our message to the console. If you’re thinking about Express’s app.listen() callback function, you’re right!

---

In the previous example, we used Node’s built-in console.log() method to print our message. However, Sails provides its own logger through sails.log. It supports different log levels, such as info, warn, error, and debug.

Let’s use the info method instead.

const sails = require('sails');

sails.lift(
    {
        port: 3000,
        log: {
            noShip: true,
        },
        hooks: {
            session: false,
        },
    },
    () => {
        sails.log.info();
        sails.log.info('Application is up and running on port 3000');
        sails.log.info();
    }
);

Using the Sails logger keeps our messages within the application’s logging system. This also means that the logger’s configuration, including its log level, can affect which messages appear in the console. You may notice that we’re calling sails.log.info() without any arguments before and after our message. These calls are intended to add some visual spacing around the message.

---

We can make our startup message more informative by including the version of Sails we’re running. Sails exposes its version through sails.version.

Let’s add it to our startup output.

const sails = require('sails');

sails.lift(
    {
        port: 3000,
        log: {
            noShip: true,
        },
        hooks: {
            session: false,
        },
    },
    () => {
        sails.log.info();
        sails.log.info('Application is up and running on port 3000');
        sails.log.info(`Sails v${sails.version}`);
        sails.log.info();
    }
);

Running the application should print the version of Sails we’re using in our application.

---

So far, we’ve been passing all our configuration options directly to sails.lift(). This works, but imagine that we have many more options to configure. Our app.js file could become cluttered with configuration details. Fortunately, Sails supports a .sailsrc file for application configuration. We can move our configuration options into this file and keep app.js clean.

Create a .sailsrc file in the root of the contacts application and add the following configuration:

{
    "port": 3000,
    "log": {
        "noShip": true
    },
    "hooks": {
        "session": false
    }
}

Now we can remove the configuration options from the sails.lift() method, keeping only the callback function in app.js. To load the configurations from the .sailsrc file, we need to import rc from sails/accessible/rc and then call the rc() function with sails as the argument.

All in all, we need to write the following code:

const sails = require('sails');
const rc = require('sails/accessible/rc');

sails.lift(rc("sails"), () => {
    sails.log.info('Application is up and running on port 3000');
    sails.log.info(`Sails.js v${sails.version}`);
});

Go ahead and restart the application. You should see the same output in the console.

---

We can rearrange the locations of models, views, and controllers. Instead of keeping these folders in their current locations, we can have a root-level app folder and put all three folders inside it:

  • app/models
  • app/views
  • app/controllers

To do this, we need to set the values of paths.models, paths.views, and paths.controllers in the .sailsrc file.

Let’s update the configuration:

{
    "port": 3000,
    "log": {
        "noShip": true
    },
    "hooks": {
        "session": false
    },
    "paths": {
        "controllers": "app/controllers",
        "models": "app/models",
        "views": "app/views"
    }
}

Now, move the controllers, models, and views into the app folder. In our existing project, this means moving the relevant folders out of api and into app. After that, rerun the application. You shouldn’t notice any difference in the result. You’ll still see the same output, but with a different folder structure.

Better? :)

---

Let’s shift our focus and customize the Sails application beyond its configuration options. For example, what if I want to use Pug as the template engine instead of EJS? First, we need to install the consolidate package.

npm i -E consolidate

As we’re planning to use Pug, don’t forget to install that as well.

npm i -E pug

View configuration is defined in the config/views.js file. We don’t have this file yet, so let’s create it.

touch config/views.js

Open config/views.js and add the following code:

module.exports.views = {
    extension: 'pug',

    getRenderFn: function () {
        const consolidate = require('consolidate');
        return consolidate.pug;
    },

    layout: false,
};

First, I specified the template extension so that I don’t have to write the extension when using the template with the res.render() method.

Second, I need to tell Sails which render function to use for the given template. If the template engine is supported by Consolidate, it’s quite easy to do: we can return the appropriate render function from getRenderFn(), as Sails suggests.

Finally, Pug has its own way of handling layouts and related functionality. We can explicitly configure the layout behavior, so I’ve set layout to false.

---

Let’s replace the disk-based database with PostgreSQL. For this, you should have PostgreSQL installed on your computer.

First, install the PostgreSQL adapter:

npm i -E sails-postgresql

Next, create the config/datastores.js file.

module.exports.datastores = {
    default: {
        adapter: require('sails-postgresql'),
        url: 'postgres://postgres:password@localhost:5432/contacts',
    },
};

The database URL follows this format:

protocol://user:password@host:port/database

Create a database named contacts in PostgreSQL and then rerun the application. With an appropriate migration strategy configured, Sails can create the contacts table in the contacts database. I know, this looks a bit odd: we have a database named contacts and a table named contacts inside it!

---

Interestingly enough, we can use multiple databases in a single application. For example, we can use MongoDB alongside PostgreSQL.

Let’s install the MongoDB adapter:

npm i -E sails-mongo

Next, add a mongo datastore to config/datastores.js.

module.exports.datastores = {
    default: {
        adapter: require('sails-postgresql'),
        url: 'postgres://postgres:password@localhost:5432/contacts',
    },
    mongo: {
        adapter: require('sails-mongo'),
        url: 'mongodb://localhost:27017/logs',
    },
};

We’ve now configured two datastores: default, which uses PostgreSQL, and mongo, which uses MongoDB.

Let’s create a Log model to work with the MongoDB datastore in Log.js file within app/models.

touch app/models/Logs.js

Open Logs.js and add the following code.

module.exports = {
    datastore: 'mongo',
    attributes: {
        id: {
            columnName: '_id',
            type: 'string',
        },
        action: { 
            type: 'string', 
            required: true,
        },
  },
};

We’ve specified mongo as the datastore for this model. This tells Waterline to use our MongoDB datastore when working with Log.

Let’s use both models in index function of HomeController.js file.

module.exports = {
    index: async (req, res, next) => {
        const contacts = await Contact.find();
        console.log('contacts:', contacts);

        const logs = await Log.find();
        console.log('logs:', logs);

        res.render('home');
  },
};

Here, we’re querying the Contact and Log models separately. The Contact model uses the default PostgreSQL datastore, while the Log model uses MongoDB. This is a simple example of how we can work with multiple databases in the same Sails application.

---

Customization is endless! But let’s stop here. In the next chapter, we’ll take a look at some extra functionalities that Sails provides. I said extra because I personally think these are nice-to-have features that don’t necessarily need to be part of the framework. Again, that’s just my personal opinion, and you might disagree. That’s totally fine with me! :)


સાહિત્યિક કાર્યક્રમો

  • તા. ૨૧-૨૫ ઓક્ટોબર ૨૦૨૬ - સંસ્કૃત વાગ્માધુરી ૨૦૨૬, રા. વિ. પાઠક સભાગૃહ, ગુજરાતી સાહિત્ય પરિસદ, આશ્રમ રોડ, અમદાવાદ.
  • તા. ૨૪–૨૫ ઓક્ટોબર ૨૦૨૬ - અમદાવાદ ઇન્ટરનેશનલ લિટરેચર ફેસ્ટિવલ
  • વધુ માહિતી માટે...

વર્ગીકૃત

કડીઓ

સૂચિત લેખો

ચોપડીઓ

જોયેલ