This shows how to use DeviceDetectionPipelineBuilder instead of specific DeviceDetectionCloudPipelineBuilder.
This shows how to use DeviceDetectionPipelineBuilder instead of specific DeviceDetectionCloudPipelineBuilder. This example is available in full on GitHub.
Expected output:
Is user agent 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_2 like Mac OS X) AppleWebKit/604.4.7 (KHTML, like Gecko) Mobile/15C114' a mobile?
true
Is user agent 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36' a mobile?
false
/* *********************************************************************
* This Original Work is copyright of 51 Degrees Mobile Experts Limited.
* Copyright 2026 51 Degrees Mobile Experts Limited, Davidson House,
* Forbury Square, Reading, Berkshire, United Kingdom RG1 3EU.
*
* This Original Work is licensed under the European Union Public Licence
* (EUPL) v.1.2 and is subject to its terms as set out below.
*
* If a copy of the EUPL was not distributed with this file, You can obtain
* one at https://opensource.org/licenses/EUPL-1.2.
*
* The 'Compatible Licences' set out in the Appendix to the EUPL (as may be
* amended by the European Commission) shall be deemed incompatible for
* the purposes of the Work and the provisions of the compatibility
* clause in Article 5 of the EUPL shall not apply.
*
* If using the Work as, or as part of, a network application, by
* including the attribution notice(s) required under Article 5 of the EUPL
* in the end user terms of the application under an appropriate heading,
* such notice(s) shall fulfill the requirements of that article.
* ********************************************************************* */
const path = require('path');
const require51 = (requestedPackage) => {
try {
return require(path.join(__dirname, '/../../../node_modules/', requestedPackage));
} catch (e) {
return require(path.join(__dirname, '/../../../../', requestedPackage));
}
};
const fs = require('fs');
const http = require('http');
const pug = require('pug');
const compiledFunction =
pug.compileFile(path.join(__dirname, '/index.pug'));
// Directory holding the shared pattern-library web-example assets
// (examples-main.min.css and examples.min.js). These are served as static
// files so the page can reference them with /css and /js URLs, in the same way
// express.static or an ASP.NET wwwroot folder would expose them.
const publicDir = path.join(__dirname, '/public');
// Map of file extensions to the content types used when serving static assets.
const staticContentTypes = {
'.css': 'text/css',
'.js': 'text/javascript'
};
// Serve a file from the public directory. Returns true if the request was a
// static asset request (whether or not the file was found) so the caller can
// stop processing it as a detection request.
const tryServeStatic = (req, res) => {
const urlPath = req.url.split('?')[0];
if (!urlPath.startsWith('/css/') && !urlPath.startsWith('/js/')) {
return false;
}
// Resolve the requested path within the public directory and make sure it
// cannot escape it.
const filePath = path.join(publicDir, urlPath);
if (!filePath.startsWith(publicDir)) {
res.statusCode = 403;
res.end();
return true;
}
fs.readFile(filePath, function (err, content) {
if (err) {
res.statusCode = 404;
res.end();
return;
}
res.statusCode = 200;
res.setHeader('Content-Type',
staticContentTypes[path.extname(filePath)] || 'application/octet-stream');
res.end(content);
});
return true;
};
const core = require51('fiftyone.pipeline.core');
const optionsExtension =
require51('fiftyone.devicedetection.shared').optionsExtension;
const dataExtension =
require51('fiftyone.devicedetection.shared').dataExtension;
require(path.join(__dirname, '/../exampleUtils'));
// Pipeline variable to be used
let pipeline;
const setPipeline = (options) => {
// An explicit data file path supplied in the '_51DEGREES_DD_PATH'
// environment variable takes precedence over the value in the
// configuration file.
const envDataFilePath = process.env[DATA_FILE_PATH_ENV_VAR];
if (envDataFilePath) {
optionsExtension.setDataFilePath(options, envDataFilePath);
}
const dataFilePath = optionsExtension.getDataFilePath(options);
if (!dataFilePath) {
throw 'A data file must be specified in the 51d.json file or the ' +
`'${DATA_FILE_PATH_ENV_VAR}' environment variable.`;
}
if (!fs.existsSync(dataFilePath)) {
if (newDataFilePath !== undefined) {
optionsExtension.setDataFilePath(options, newDataFilePath);
console.warn('Failed to find a device detection data file at ' +
`at the specified path '${dataFilePath}'. Use '${newDataFilePath}' ` +
'instead.');
} else {
throw 'Failed to find a device detection data file at ' +
`'${dataFilePath}'. If using the lite file, then make sure the ` +
'device-detection-data submodule has been updated by running ' +
'\'git submodule update --recursive\'. Otherwise, ensure that the ' +
'filename is correct in 51d.json.';
}
}
// 'suppressProcessExceptions' is set to true in 51d.json
// (PipelineOptions.BuildParameters) so a device-detection failure degrades
// gracefully instead of failing the request. Use false while developing to
// surface mistakes loudly. Errors are still logged via the 'error' handler.
pipeline = new core.PipelineBuilder({
// Enable custom javascript builder
addJavaScriptBuilder: true,
// Settings for javascript builder
javascriptBuilderSettings: {
// Custom endpoint to report client-side evidence
endPoint: '/json'
}
}).buildFromConfiguration(options);
// Logging of errors and other messages.
// Valid logs types are info, debug, warn, error
pipeline.on('error', console.error);
};
const server = http.createServer((req, res) => {
// Serve the shared CSS/JS assets from the public directory. If this was a
// static asset request then there is nothing more to do.
if (tryServeStatic(req, res)) {
return;
}
// FlowData is a data structure that is used to convey
// information required for detection and the results of the
// detection through the pipeline.
// Information required for detection is called "evidence"
// and usually consists of a number of HTTP Header field
// values, in this case represented by a
// Object of header name/value entries.
const flowData = pipeline.createFlowData();
// Extract required evidence from the Http request.
flowData.evidence.addFromRequest(req);
// Handle report from client side separately
if (req.url.startsWith('/json')) {
flowData.process().then(function () {
res.statusCode = 200;
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify(flowData.jsonbundler.json));
});
} else {
flowData.process().then(function () {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/html');
// Some browsers require that extra HTTP headers are explicitly
// requested. So set whatever headers are required by the browser in
// order to return the evidence needed by the pipeline.
// More info on this can be found at
// /blog/user-agent-client-hints?utm_source=code&utm_medium=example&utm_campaign=device-detection-node&utm_content=fiftyone.devicedetection.onpremise-examples-onpremise-gettingstarted-web-gettingstarted.js&utm_term=server
core.Helpers.setResponseHeaders(res, flowData);
// Obtain all used evidence
const allEvidence = flowData.evidence.getAll();
const evidences =
pipeline.getElement('device').evidenceKeyFilter.filterEvidence(
allEvidence);
// Compile a response
res.end(compiledFunction(
{
dataFilePublishedTime: new Date().getTime(),
dataFileAgeWarning: DATA_FILE_AGE_WARNING,
responseHeaders: res.getHeaders(),
evidenceUsed: evidences,
allEvidence,
dataSourceTier: ExampleUtils.getDataTier(pipeline),
fiftyOneJs: flowData.javascriptbuilder.javascript,
hardwareVendor: dataExtension.getValueHelper(flowData.device, 'hardwarevendor'),
hardwareName: dataExtension.getValueHelper(flowData.device, 'hardwarename'),
deviceType: dataExtension.getValueHelper(flowData.device, 'devicetype'),
platformVendor: dataExtension.getValueHelper(flowData.device, 'platformvendor'),
platformName: dataExtension.getValueHelper(flowData.device, 'platformname'),
platformVersion: dataExtension.getValueHelper(flowData.device, 'platformversion'),
browserVendor: dataExtension.getValueHelper(flowData.device, 'browservendor'),
browserName: dataExtension.getValueHelper(flowData.device, 'browsername'),
browserVersion: dataExtension.getValueHelper(flowData.device, 'browserversion'),
screenWidth: dataExtension.getValueHelper(flowData.device, 'screenpixelswidth'),
screenHeight: dataExtension.getValueHelper(flowData.device, 'screenpixelsheight')
})
);
});
}
});
// Don't run the server if under TEST
if (process.env.JEST_WORKER_ID === undefined) {
// Load the configuration options from config file.
const options = JSON.parse(fs.readFileSync(path.join(__dirname, '/51d.json')));
setPipeline(options);
const port = 3001;
const hostname = 'localhost';
server.listen(port, hostname);
console.log(`Server listening on: http://${hostname}:${port}`);
};
// Export server object and set pipeline.
module.exports = {
server,
setPipeline
};
Definition fiftyone.devicedetection.onpremise/examples/onpremise/exampleUtils.js:41
