Develop Rest API and server-side rendering simultaneously in SailsJs

I am looking towards the SailsJs framework for nodeJs to build an app.

Initially I planned to build just a REST API in sails and manage the Front-End using AngularJs, the API would also be useful for other non-browser apps. But then I realized since angularJs is client side data binding , My site might not be SEO friendly and IE-8 compatible.

So what I am trying to find out now is that :

Is it possible to develop a site which uses server-side(ejs) rendering and together with that, develop a REST API simultaneously with the least efforts. and if I go this way, What things one should keep in mind.

Thanks.

This is totally possible! Take a look at req.wantsJSON.

A flag indicating whether the requesting client would prefer a JSON response (as opposed to some other format, like XML or HTML.)

The best point about this is that:

req.wantsJSON is used by all of the built-in custom responses in Sails.

It means that you have nothing to do server-side for it to work!

You only have to create the view corresponding to the action (usually find or findOne) and watch out for the headers you send to your Sails server.


For example, let's generate an "api" (controller/model) with Sails Command Line Interface

$ sails generate api product

Now let's create some products with the Sails Console

$ sails console

sails> Product.create({name: 'Blue chair'}).exec(console.log)
undefined
sails> null { name: 'Blue chair',
 createdAt: "2014-12-30T04:29:15.447Z",
 updatedAt: "2014-12-30T04:29:15.447Z",
 id: 1 }

sails> Product.create({name: 'Red table'}).exec(console.log)
undefined
sails> null { name: 'Red table',
 createdAt: "2014-12-30T04:29:25.447Z",
 updatedAt: "2014-12-30T04:29:25.447Z",
 id: 2 }

sails> Product.create({name: 'Yellow hammer'}).exec(console.log)
undefined
sails> null { name: 'Yellow hammer',
 createdAt: "2014-12-30T04:29:35.447Z",
 updatedAt: "2014-12-30T04:29:35.447Z",
 id: 3 }

If you lift your sails app right now and access http: //localhost:1337/product, you should have something like

 [
  {
   name: 'Blue chair',
   createdAt: "2014-12-30T04:29:15.447Z",
   updatedAt: "2014-12-30T04:29:15.447Z",
   id: 1
  },
  {
   name: 'Red table',
   createdAt: "2014-12-30T04:29:25.447Z",
   updatedAt: "2014-12-30T04:29:25.447Z",
   id: 2
  },
  {
   name: 'Yellow hammer',
   createdAt: "2014-12-30T04:29:35.447Z",
   updatedAt: "2014-12-30T04:29:35.447Z",
   id: 3
  }
 ]

Which is JSON (you can check the Content-Type header value in the Response Headers of the Network section in your browser Developer Tools).

To associate a view to your resource, you'll need to create a folder named with the name of your resource in your views folder, and create file named with the name of the action (find or findOne usually).

If you refresh the page in your browser, you should see what's inside your view (nothing basically). So let's create the "find" view which contains the list of products

<ul>
    <% _.each(data, function (product) { %>
    <li><%= product.name %></li>
    <% }) %>
</ul>

In your browser, you should have something like :

  • Blue chair
  • Red table
  • Yellow hammer

Now, how can you get the JSON data for your other client app? Just by giving the correct HTTP Headers.

As said in the req.wantsJSON doc

if this request has a "json" content-type AND ALSO has its "Accept" header set

You have to set both the "Content-Type" and the "Accept" headers in your HTTP request. By testing it, I found out that the "Accept" header is sufficient. So you only have to set one these values to this header :

  • "application/json" if you want JSON
  • "text/html" if you want your view

And that's it!


I hope I didn't forget anything. Do not hesitate if you need more precision!