Advanced Topics

Routing

Routing essentially means how URLs are mapped to actions and variables.

When a URL is accessed on your domain, a request is sent to the router that is available in ‘app.js’ file or a plugin. This router is responsible for finding a matching page and, accordingly, routing the request to the page. contentstack-express allows you to define these routing rules.

Example:

Let’s define a rule where the router will return ‘hello world’ when a GET request is made to the home page.

// respond with "hello world" when a GET request is made to the homepage
app.get('/', function(req, res) {
  res.send('hello world');
});

Similarly, you can various define custom routes. contentstack-express supports all the route methods, paths, and handlers that are provided by Node.js Express.

Querying

Queries are requests that help you fetch published content from your stack in Contentstack. You can apply filters or perform custom search to get the required content for your website using these queries. Let’s look at at some of the basic queries and advanced queries.

Basic Queries

lessThan

This method provides only the entries with values less than the specified value for a field.

Parameters

key uid of the field that is to be taken into consideration
value The value used to match or compare

Example

blogQuery.lessThan('created_at','2015-06-22')

includeCount

This method also includes the total number of entries returned in the response.

Example

blogQuery.includeCount()

regex

This method provides only the entries matching the regular expression for the specified field.

Parameters

key Uid of the field that is to be taken into consideration
value The value used to match or compare
options <optional> match or compare value in entry

Examples

.regex without options

blogQuery.regex('title','/^Demo/')

.regex with options

blogQuery.regex('title','/^Demo/', 'i')

Contentstack provides several other queries for fetching content in customized manner. We have covered all of them in the following section.

Advanced Queries

.find() [mandatory]

This method fetches a list of entries that satisfy the specified query. Filters such as .query() can be applied to limit the response.

app.extends().get('/find', function(req, res, next) {
    return Stack.ContentType('demo').Query().language('en-us').find().then(response => {
        res.json(response);
    }).catch(error => {
        res.json(error);
    });
});

.toJSON() [optional]

This method fetches plain JavaScript object. If not specified, a wrapper is run around each object that is returned.

app.extends().get('/find', function(req, res, next) {
    return Stack.ContentType('demo').Query().language('en-us').toJSON().then(response => {
        res.json(response);
    }).catch(error => {
        res.json(error);
    });
});

.fetch() [mandatory]

This method fetches a single entry. Specify either the entry’s UID or use .where() to filter out a single entry.

app.extends().get('/fetch', function(req, res, next) {
    return Stack.ContentType('demo').Entry('').language('en-us').fetch().then(response => {
        res.json(response);
    }).catch(error => {
        res.json(error);
    });
});

.language('code') [optional]

This method is used to set the language code. The entries of the set language are retrieved. If not specified, the entries are fetched from the master language.

app.extends().get('/fetch', function(req, res, next) {
    return Stack.ContentType('demo').Entry('').language('en-us').fetch().then(response => {
        res.json(response);
    }).catch(error => {
        res.json(error);
    });
});

.lessThan('key', 'value') [optional]

This method retrieves entries where a field value is less than the specified value.

app.extends().get('/lessThan', function(req, res, next) {
    return Stack.ContentType('demo').Query().lessThan('number', 2).find().then(response => {
        res.json(response);
    }).catch(error => {
        res.json(error);
    });
});

.greaterThan('key', 'value') [optional]

This method retrieves entries where a specific field value is greater than the specified value.

app.extends().get('/greaterThan', function(req, res, next) {
    return Stack.ContentType('demo').Query().greaterThan('number', 2).find().then(response => {
        res.json(response);
    }).catch(error => {
        res.json(error);
    });
});

.notEqualTo('key', 'value') [optional]

This method retrieves entries where a specific field value is not equal to a specified value.

app.extends().get('/notEqualTo', function(req, res, next) {
    return Stack.ContentType('demo').Query().notEqualTo('number', 2).find().then(response => {
        res.json(response);
    }).catch(error => {
        res.json(error);
    });
});

.containedIn('key', 'values') [optional]

This method retrieves entries with fields having values that match to the specified set of values.

app.extends().get('/containedIn', function(req, res, next) {
    return Stack.ContentType('demo').Query().containedIn('number', [2]).find().then(response => {
        res.json(response);
    }).catch(error => {
        res.json(error);
    });
});

.notContainedIn('key', 'values') [optional]

This method retrieves entries with fields having values that do not match to the specified set of values.

app.extends().get('/notContainedIn', function(req, res, next) {
    return Stack.ContentType('demo').Query().notContainedIn('number', [2]).find().then(response => {
        res.json(response);
    }).catch(error => {
        res.json(error);
    });
});

.exists('key') [optional]

This method provides only those entries that contains a field matching the specified UID.

app.extends().get('/exists', function(req, res, next) {
    return Stack.ContentType('demo').Query().exists('boolean').find().then(response => {
        res.json(response);
    }).catch(error => {
        res.json(error);
    });
});

.notExists('key') [optional]

This method provides only those entries that do not contain the field matching the specified UID.

app.extends().get('/notExists', function(req, res, next) {
    return Stack.ContentType('demo').Query().notExists('boolean').find().then(response => {
        res.json(response);
    }).catch(error => {
        res.json(error);
    });
});

.ascending('key') [optional]

This parameter sorts the retrieved entries in the ascending order on the basis of a specific field.

app.extends().get('/ascending', function(req, res, next) {
    return Stack.ContentType('demo').Query().ascending('number').find().then(response => {
        res.json(response);
    }).catch(error => {
        res.json(error);
    });
});

.descending('key') [optional]

This parameter sorts the retrieved entries in the descending order on the basis of a specific field.

app.extends().get('/descending', function(req, res, next) {
    return Stack.ContentType('demo').Query().descending('number', [2]).find().then(response => {
        res.json(response);
    }).catch(error => {
        res.json(error);
    });
});

.skip(value) [optional]

This method skips the specified number of entries.

app.extends().get('/skip', function(req, res, next) {
    return Stack.ContentType('demo').Query().skip(1).find().then(response => {
        res.json(response);
    }).catch(error => {
        res.json(error);
    });
});

.limit(value) [optional]

This method limits the response by providing only the specified number of entries.

app.extends().get('/limit', function(req, res, next) {
    return Stack.ContentType('demo').Query().limit(1).find().then(response => {
        res.json(response);
    }).catch(error => {
        res.json(error);
    });
});

.or(query1, query2) [optional]

This method performs the ‘OR’ operation on the specified query objects and provides only the matching entries.

app.extends().get('/or', function(req, res, next) {
    return Stack.ContentType('demo').Query().or({
        '_data.title': 'Demo 4'
    }, {
        '_data.title': 'B5'
    }).find().then(response => {
        res.json(response);
    }).catch(error => {
        res.json(error);
    });
});

.and(query1, query2) [optional]

This method performs the ‘AND’ operation on the specified query objects and provides only the matching entries.

app.extends().get('/and', function(req, res, next) {
    return Stack.ContentType('demo').Query().and({
        '_data.title': 'Demo 4'
    }, {
        '_data.number': 12
    }).find().then(response => {
        res.json(response);
    }).catch(error => {
        res.json(error);
    });
});

.where(key, value) [optional]

This method provides only the entries matching the specified value for a field. Using ‘where’, you can query referenced fields.

Note: You need to edit the config file and add ‘indexes’. Refer the example below:

/**
 * Note
 * Use 'where' to query on 'reference' fields
 * In order to query on 'reference' fields, kindly add the referenced content type's uid in the config's 'indexes'
 * example - config/all.js
 * 'indexes': {
 *   content_type_uid: [''] // Pass content type's fields that you'd want to query on
 *                          // leave it empty, if you'd like to query on any of the content type's fields
 *   ...
 * }
 */
app.extends().get('/where', function(req, res, next) {
    return Stack.ContentType('demo').Query().where('reference.uid', {
        '$in': ['blt4129f010963bee1b', 'bltf0ae2fe2a3a6a8e8']
    }).find().then(response => {
        res.json(response);
    }).catch(error => {
        res.json(error);
    });
});

.count() [mandatory]

This method provides only the number of entries matching the specified filters.

app.extends().get('/count', function(req, res, next) {
    return Stack.ContentType('demo').Query().count().then(response => {
        res.json(response);
    }).catch(error => {
        res.json(error);
    });
});

.query(queryObject) [optional]

This method is used to set raw queries on Query instances. Prefix each of the fields (to be filtered) with ‘_data.’

app.extends().get('/query', function(req, res, next) {
    return Stack.ContentType('demo').Query().query({
        '_data.boolean': true
    }).find().then(response => {
        res.json(response);
    }).catch(error => {
        res.json(error);
    });
});

.tags(['value1', 'value2']) [optional]

This parameter allows you to specify an array of tags to search objects.

app.extends().get('/tags', function(req, res, next) {
    return Stack.ContentType('demo').Query().tags(['one']).find().then(response => {
        res.json(response);
    }).catch(error => {
        res.json(error);
    });
});

.includeCount() [optional]

This method also includes the total number of entries returned in the response.

app.extends().get('/includeCount', function(req, res, next) {
    return Stack.ContentType('demo').Query().includeCount().find().then(response => {
        res.json(response);
    }).catch(error => {
        res.json(error);
    });
});

.regex(key, value, options) [optional]

This method provides only the entries matching the regular expression for the specified field.

app.extends().get('/regex', function(req, res, next) {
    return Stack.ContentType('demo').Query().regex('title', new RegExp('Demo', 'ig')).find().then(response => {
        res.json(response);
    }).catch(error => {
        res.json(error);
    });
});

contentstack-express specific methods

.excludeReference()

This method will exclude all references of an entry from the response. It accepts no parameter.

app.extends().get('/excludeReference', function(req, res, next) {
    return Stack.ContentType('demo').Query().excludeReference().find().then(response => {
        res.json(response);
    }).catch(error => {
        res.json(error);
    });
});

.referenceDepth(value)

This method sets the depth to which an entry’s reference will be fetched. It accepts a numeric value as parameter. In the below example, the reference depth is set to 1, i.e., it will fetch only the entries and its immediate referred entries. However, if the referred entries have references, those will be ignored.

app.extends().get('/referenceDepth', function(req, res, next) {
    return Stack.ContentType('demo').Query().referenceDepth(1).find().then(response => {
        res.json(response);
    }).catch(error => {
        res.json(error);
    });
});

.lessThanEqualTo('field', number)

This method retrieves entries where a field value is less than or equal to the specified value.

app.extends().get('/lessThanOrEqualTo', function(req, res, next) {
    return Stack.ContentType('demo').Query().lessThanEqualTo('number', 2).find().then(response => {
        res.json(response);
    }).catch(error => {
        res.json(error);
    });
});

.greaterThanEqualTo('field', number)

This method retrieves entries where a field value is greater than or equal to the specified value.

app.extends().get('/greaterThanOrEqualTo', function(req, res, next) {
    return Stack.ContentType('demo').Query().greaterThanEqualTo('number', 2).find().then(response => {
        res.json(response);
    }).catch(error => {
        res.json(error);
    });
});

contentstack-express methods return values

Here are the return values of the contentstack-express methods.

Fetching a single entry

To fetch a single entry, you can use the code given below:

Stack.ContentType(‘demo’).Entry(‘bltsomething123’).fetch().then(entry => {
    /**
     *  The structure of result would be
     *  {
     *     title: ‘entry_demo_1’
     *     ..
     *  }
     **/
}).catch(error => ..);

The above code returns the details in JSON.

Fetching multiple entries

Using the following code you can fetch multiple entries of a particular content type. Here, each method is wrapped around getter-setters.

Stack.ContentType(‘demo’).Query().find().then(result => {
    /**
     *  The structure of result would be
     *  [
     *     [
     *       { entry_title_1: “Demo title 1”, … },
     *       { entry_title_2: “Demo title 2”, … },
     *       ...
     *     ]
     *  ]
     *
     *  And the following operation can be done on those objects
     *
     *  result[0].forEach(entry => console.log(entry.get(‘title’));
     **/
}).catch(error => ..);

Fetching multiple entries using ‘toJSON()’

Unlike the above method, the ‘toJSON()’ method returns your response consisting the resultant entries in JSON.

Stack.ContentType(‘demo’).Query().toJSON().find().then(result => {
    /**
     *  The structure of result would be
     *  [
     *       { entry_title_1: “Demo title 1”, … },
     *       { entry_title_2: “Demo title 2”, … },
     *       ...
     *  ]
     **/
}).catch(error => ..);

Fetching the total number of entries using ‘count()’

The ‘count()’ method returns a numeric value in the output. Here’s how your code looks like:

Stack.ContentType(‘demo’).Query().count().then(result => {
    // the result will be a numeric value
}).catch(error => ..);

Content caching

contentstack-express stores the entire content of the rendered page. The visitors are then served the cached version of the content, instead of rendering pages every time. This decreases the number of read requests sent to the database, as well as improves the overall performance of the website.

To enable content caching, set cache to ‘true’ in config.

Configuring multiple servers

To enable horizontal scaling of content, you can add multiple servers for any environment and set up an internal process to connect each server.

Add multiple servers within the environment

First, you need to add your servers in the environment. To do so, click on ‘Settings’ and select ‘Environments’.

Note: You can either add a new environment or update an existing one

Now, click on the ‘Advanced’ option of the environment. Under the ‘Server(s)’ section, add the names of servers that you wish to add for this environment.

Add servers.png

Finally, after adding the servers in an environment, you need to run your app in the servers.

Start the app in individual servers

To start your app in all the servers, run the following command in all of them, along with the corresponding server details.

For Windows:

set NODE_ENV=<<YOUR_ENV_NAME>>&& set SERVER=<<YOUR_SERVER_NAME>>&& npm run start

To run ‘Server 1’ and ‘Server 2’ in Windows, run the following commands in the respective servers:

  • Server 1:
  • set NODE_ENV=production&& set SERVER=Server 1&& npm run start
        
  • Server 2:
  • set NODE_ENV=production&& set SERVER=Server 2&& npm run start
        

For Linux:

NODE_ENV=<<YOUR_ENV_NAME>> SERVER=<<YOUR_SERVER_NAME>> npm run start

To run ‘Server 1’ and ‘Server 2’ in Linux, run the following commands in the respective servers:

  • Server 1:
  • set NODE_ENV=production SERVER=Server 1 npm run start
        
  • Server 2:
  • set NODE_ENV=production SERVER=Server 2 npm run start
        

Publish content in the environment

Once you perform the above steps, publish the entries in the environment. The published content will reflect in all the servers.


Was this article helpful?
top-arrow