Create a DEV-CMS connection


By following this guide it is possible to set up a custom endpoint route in addition to Fonto’s standard CMS contracts. This allows you to build custom integrations between Fonto and your CMS.


In this guide we’ll use a popover from an earlier guide as an example use case.

Step 1: Register a custom route to Fonto’s development CMS

  • Add a dev-cms/configureDevCms.js with the following code. This lets Fonto’s development server know that additional endpoints should be added to the Express server it runs.
configureDevCms.js
const routes = [require('./routes/getIngredientInformation')];

module.exports = (router) => {
	return {
		routes: routes.map(route => route(router))
	};
};


You can add as many routes as you like by adding more references into the
routes array.

Step 2: Add a custom route

  • Create file /dev-cms/routes/getIngredientInformation.js with the following code. Fonto will give it the Express router object, to which you can add endpoint routes as per their documentation. The req objects and res objects, too, are provided by Express and can be used in the way that they are documented there.
getIngredientInformation.js
module.exports = router => {
   router
       .route('/connectors/cms/standard/get-ingredient-information')
       .get((req, res) => {
           res.json('Hello world!').end();
       });
 
   return router;
};


Step 3: Restart the development server

After any change in the development server, you should restart it to put those changes into effect.

Step 4: Update  the Popover UI

In order to update the UI we’ve created in the Create a popover guide, we simply need to make an HTTP request. We could use fetch() to do so, but using the connectorsManager is recommended.

  • In the popover code we’ll add the following imports:
imports
import React, { useState, useEffect } from 'react';
import connectorsManager from 'fontoxml-configuration/src/connectorsManager.js';
import RequestData from 'fontoxml-connector/src/RequestData.js';
  • And in the RecipePopover component, the following state and effect hooks:
RecipePopover.js
const [ingredientInformation, setIngredientInformation] = useState(null);
 
useEffect(() => {
   const requestData = new RequestData();
   requestData.addQueryParameter('ingredient', ingredientTitle);
 
   connectorsManager
       .getCmsClient()
       .sendRequest('GET', '/get-ingredient-information', requestData)
       .then(response => setIngredientInformation(response.body))
       .catch(error => setIngredientInformation(error));
}, [ingredientTitle]);


Now, any time the popover is rendered we will have the
ingredientInformation variable available, which is either null, an Error or the response from your custom endpoint.

The full code for the popover created in the Create a popover guide has become:

RecipePopover.js
import React, { useState, useEffect } from 'react';
import connectorsManager from 'fontoxml-configuration/src/connectorsManager.js';
import RequestData from 'fontoxml-connector/src/RequestData.js';

import evaluateXPath from 'fontoxml-selectors/src/evaluateXPath.js';
import documentsManager from 'fontoxml-documents/src/documentsManager.js';
import useXPath from 'fontoxml-fx/src/useXPath.js';

import { Button, Flex, Popover, PopoverBody, PopoverFooter, PopoverHeader, Text, SpinnerIcon } from 'fds/components';

const RecipePopover = ( {togglePopover, data} ) => {
const [ingredientInformation, setIngredientInformation] = useState(null);

    const contextNode = documentsManager.getNodeById(data.contextNodeId);
    const ingredientTitle = useXPath("concat(upper-case(substring(.,1,1)),substring(., 2))", contextNode, { expectedResultType:evaluateXPath.STRING_TYPE });

useEffect(() => {
   const requestData = new RequestData();
   requestData.addQueryParameter('ingredient', ingredientTitle);
 
   connectorsManager
       .getCmsClient()
       .sendRequest('GET', '/get-ingredient-information', requestData)
       .then(response => setIngredientInformation(response.body))
       .catch(error => setIngredientInformation(error));
}, [ingredientTitle]);

    return (
        <Popover maxWidth="300px" minWidth="220px">
            <PopoverHeader title={ingredientTitle || "Ingredient name..."} />
            <PopoverBody>
                {
                    !!ingredientInformation ?
                        <Text>{ingredientInformation}</Text> :
                        <Flex alignItems="center" flexDirection="column" >
                            <SpinnerIcon size="m"/>
                        </Flex>
                }
            </PopoverBody>

            <PopoverFooter>
                <Button
                    type="primary"
                    label="Close"
                    onClick={togglePopover}/>
            </PopoverFooter>
        </Popover>
    );
};

export default RecipePopover;

Step 5: Customizing the custom endpoint further

So far our custom endpoint only returns a string “Hello world!”. In our popover code we’ve already queried the ingredient that was clicked, and that text is already included in the request as the “ingredient” query parameter.

Using Express we can read that request property and do a lookup from a database, object or other accordingly.

In this example we’ll hardcode a mapping of ingredients to descriptions, and perform a simple lookup:

getIngredientInformation.js
const exampleDatabase = {
	'skin of the guinea pig': 'The Skinny Pig is an almost hairless breed of Guinea pig. So, the skin of the skinny pig is not the same as a skinny guinea pig. Therefore, use the skin of the common quinea pig.',
	'cumin': 'Cumin sometimes spelled cummin is a flowering plant, native from the east Mediterranean to East India.',
	'salt': 'A white crystalline substance which gives seawater its characteristic taste and is used for seasoning or preserving food.',
	'pepper': 'A pungent hot-tasting powder prepared from dried and ground peppercorns, used as a spice or condiment to flavour food.',
	'oil': 'A viscous liquid derived from petroleum, especially for use as a fuel or lubricant.',
	'flour': 'A powder obtained by grinding grain, typically wheat, and used to make bread, cakes, and pastry.',
	'boiled potato': 'The potato is a root vegetable native to the Americas, a starchy tuber of the plant Solanum tuberosum, and the plant itself, a perennial in the family Solanaceae, in this case boiled.',
	'manioc root': 'Cassava with long tuberous edible roots and soft brittle stems; used especially to make cassiri (an intoxicating drink) and tapioca',
	'tomatoes': 'The tomato plant can have a fibrous root system or a taproot system depending on how the plant was grown. If the plant is grown from a seed, the plant will exhibit taproot organization.',
	'silver onions': 'Also known as Pearl onions, is a close relative of the leek, and may be distinguished from common onions by having only a single storage leaf, similar to cloves of garlic.',
	'lime juice': 'A lime is a citrus fruit, which is typically round, green in color, 3-6 centimetres (1.2-2.4 in) in diameter, and contains acidic juice vesicles, in this case pressed',
	'beer': 'An effervescent drink made from an extract of the roots and bark of certain plants.'
};

module.exports = (router) => {
	router
		.route('/connectors/cms/standard/get-ingredient-information')
		.get((req, res) => {
			const ingredientInformation = exampleDatabase[req.query.ingredient.toLowerCase()];

			res.json(ingredientInformation).end();
		});

	return router;
};


  • Restart your development server again to see these changes in effect.

Final result