index.js in RESTApps
index.js is the entry point and route configuration for a RESTApp. This is where the RESTApp's endpoints are created. The Sitevision Public API is accessible.
The URL to a RESTApp looks like this: <domain>/rest-api/<addonName>/<restAppRoute>
A simple index.js:
(function() {
'use strict';
var router = require('router');
router.get('/', function(req, res) {
res.json({message: 'Hello World'});
});
}());
Routing
Routes handle how a RESTApp responds requests. The router object provides methods to respond to the following request methods:
- GET
- PUT
- POST
- DELETE
- PATCH (@since 2023.02.1)
A route definition has the following structure:
router.method(path, handler);
Basic examples:
var router = require('router'); // retrieve an instance of the router object
// GET [domain]/rest-api/[headlessCustomModuleName]/user
router.get('/user', function(req, res) {
res.json({user: 'Foo'});
});
A RESTApp also has implicit support for the HEAD method.
HEAD requests will trigger the corresponding GET route, but the response will be stripped from body payload (headers only) [@since 10.1]
Middleware [@since 5.1]
A middleware function is a function that have access to the request object (req
), response object (res
) and a function (next
) triggering the next function in the request-response chain. The next function will either be another middleware or the actual route function. Middleware functions are executed every time the app receives a request matching a route.
Middleware functions are registered using the router.use
-method:
var router = require('router');
// register a middleware that will be executed every time the app recives a request
router.use((req, res, next) => {
if (req.method === 'PUT') {
// do something
}
// trigger next function in the stack (next middleware or route function)
next();
});
router.get('/', (req, res) => {
res.send('Hello');
});
Requests may be terminated and altered within a middleware. Keep in mind that the next()
-function must be called in order to pass over control to the next function in the stack.
Multiple middleware functions may be registered and they will be executed in the order they are registered, i.e. top-down.
const router = require('router'),
logUtil = require('LogUtil');
router.use((req, res, next) => {
logUtil.info('Hello from first');
next();
});
router.use((req, res, next) => {
logUtil.info('Hello from second');
next();
});
router.get('/', (req, res) => {
logUtil.info('Hello from route');
res.send('Hello');
});
// => 'Hello from first'
// => 'Hello from second'
// => 'Hello from route'
Route parameters
Route parameters are used to capture a value in the URL. Below is an example of a path that will capture userId on the /user
route.
// GET [domain]/rest-api/[headlessCustomModuleName]/user/123
router.get('/user/:id', function(req, res) {
// route is matched and {id: 123} will be populated in the req.params object
});
Request object (req
)
The req
object is a representation of the HTTP-request.
Properties
req.params
Object that contains parameters passed to a route. The object contains route parameters ('/user/:userId'
), query string parameters and values in the request body from POST, PUT and DELETE.
// GET [domain]/rest-api/[headlessCustomModuleName]/user/456
router.get('/user/:userId', function(req, res) {
logger.info(req.params.userId); // 456
});
// GET [domain]/rest-api/[headlessCustomModuleName]/user?foo=bar
router.get('/user', function(req, res) {
logger.info(req.params.foo); // bar
});
// POST [domain]/rest-api/[headlessCustomModuleName]/addToBasket (productId=1)
router.post('/addToBasket', function(req, res) {
logger.info(req.params.productId); // 1
});
req.files
Object containing files from a multipart request. Files are exposed as sv:temporaryFile-nodes (since 4.5.4).
FileUtil and ImageUtil provide useful methods when working with temporary file nodes (since 4.5.4).
router.post('/', function(req, res) {
var myFile = req.files.name;
});
req.cookies
Object that contains cookies from the request.
// Cookie basketId=789
router.get('/user', function(req, res){
logger.info(req.cookies.basketId); // 789
});
req.xhr
Boolean indicating whether or not the request is an XHR (ajax) request.
router.get('/', function(req, res) {
if (req.xhr) {
res.json({foo: 'ajax-bar'});
} else {
res.json({foo: 'bar'});
}
});
req.session
Session data is stored and accessed through the req.session
property. Session attributes are namespaced for WebApps/RESTapps, i.e. "global" session attributes cannot be accessed. Note that session data must be JSON serializable.
// WebApp/RESTApp x
router.get('/', function(req, res) {
req.session.name = 'John Doe';
});
// WebApp/RESTApp y
router.get('/', function(req, res) {
logger.info(req.session.name); // 'John Doe';
});
req.hostname [@since 4.5.3]
Hostname from the HTTP request.
router.post('/', function(req, res) {
logger.info(req.hostname); //'developer.sitevision.se'
});
req.protocol [@since 5.0]
Protocol from the request (http or https).
router.get('/', (req, res) => {
logger.info(req.protocol); //'https'
});
req.secure [@since 5.0]
Boolean shorthand checking req.protocol === 'https'
.
router.get('/', (req, res) => {
if (req.secure) {
// ...
}
});
If a load balancer or proxy is used, make sure that the client request protocol is forwarded to Sitevision.
req.uri [@since 2023.04.1]
The client request uri (the uri that triggered rendering of this app). Note! The uri is not always equal to "the uri of current page".
// example.com/anything/something
router.get('/', (req, res) => {
logger.info(req.uri); // '/anything/something'
});
req.serverside [@since 2023.08.1]
Whether the request is a server-side one or not. State is true when the RestApp is invoked via RestAppInvoker
.
router.get('/', (req, res) => {
if (req.serverside) {
// ...
}
});
Methods
req.invalidateSession()
Invalidates current session.
req.updateSession() [@since 2023.03.1]
Re-synchronizes local req.session
state with the global/shared App session state. Typically needed when multiple apps are executed intertwined
req.header(name) [@since 4.5.2]
Request headers from the HTTP-request
router.post('/', function(req, res) {
logger.info(req.header('user-agent')); //mozilla....
});
req.method [@since 5.1]
HTTP method of the request.
router.use((req, res, next) => {
if (req.method === 'PUT') {
return res.status(403);
}
next();
});
Response object (res
)
The res
object is a representation of the HTTP-response.
Methods
res.set(name, value)
Sets a HTTP-header in the response. Returns this
for chaining purposes.
res.set('X-Content-Type-Options', 'nosniff')
.set('Cache-Control', 'no-cache');
res.type(type) [@since 4.5.2]
Sets the response Content-Type
header. If type contains the “/” character, then it will be used as-is. Returns this
for chaining purposes.
res.type('.html'); // => 'text/html'
res.type('html'); // => 'text/html'
res.type('text/html'); // => 'text/html'
res.type('svg'); // => 'image/svg+xml'
res.type('txt'); // => 'text/plain'
res.send(response)
Sends a character response. Type will be text/html
if not explicitly set.
res.send('<p>Hello from RESTApp!</p>');
res.type('svg')
.send('<svg version="1.1" ... </svg>');
res.json(response)
Sends a application/json-
response.
res.json({
id: '123',
foo: 'bar'
});
res.sendFile(file)
Sends a file as response. Uses best effort for content type matching if no type is explicitly set. File argument must be a Sitevision JCR-Node of type sv:file
, sv:image
or sv:temporaryFile
. Byte array (byte[]
) is also valid as of Sitevision 5.0.1 (java.io.File is also allowed for legacy reasons).
res.sendFile(file);
res.type('application/xml; charset=ISO-8859-1')
.sendFile(file);
res.status(code)
Sets the HTTP-status for the response. Returns this
for chaining purposes.
res.status(404).send('Not found');
res.cookie(cookie)
Adds a cookie. The cookie
parameter is an object that can have the following properties.
Property | Type | Description | Default | Since |
---|---|---|---|---|
| String | The name of the cookie | ||
| String | The value of the cookie | ||
| Boolean | Flag to prevent client side scripts from accessing the cookie | false | 5.0 |
| Boolean | Sets the cookie to be used with HTTPS only | false | 5.0 |
| Number | The maximum age of the cookie in seconds.
| -1 | 5.0 |
| String | Determines in what contexts this cookie will be available/sent. Valid values (case-insensitive) are:
Invalid values will be treated as "no value is set by the app" (i.e. no same site attribute will be added to the cookie). | 7.1 |
res.cookie({
name: 'basketId',
value: '123',
httpOnly: true,
secure: true,
maxAge: 3600,
sameSite: 'Strict'
});
res.clearCookie(name [, path])
Clears a cookie given a name. If path is missing, it defaults to '/'.
res.clearCookie('basketId');