This example is available in full on GitHub.
This example is available in full on GitHub. Expected output:
User Agent Client Hints Example
Hardware Vendor: [...]
Hardware Name: [...]
Device Type: [...]
...
/* *********************************************************************
* 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 core = require51('fiftyone.pipeline.core');
require51('fiftyone.devicedetection.onpremise').DeviceDetectionOnPremisePipelineBuilder;
const fs = require('fs');
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;
};
// Load in a datafile
const datafile = (process.env.directory || __dirname) + '/../../../device-detection-cxx/device-detection-data/51Degrees-LiteV4.1.hash';
if (!fs.existsSync(datafile)) {
console.error("The datafile required by this example is not present. Please ensure that the 'device-detection-data' submodule has been fetched.");
throw ("No data file at '" + datafile + "'");
}
// Helper function to read property values from flowData
const getValueHelper = (flowData, propertyKey) => {
const device = flowData.device;
try {
const property = device[propertyKey];
if (property.hasValue && property) {
return property.value;
} else {
return property.noValueMessage;
}
} catch (error) {
return 'Not found in datafile';
}
};
// Pipeline variable to be used
let pipeline;
const setPipeline = (properties) => {
pipeline = new DeviceDetectionOnPremisePipelineBuilder({
performanceProfile: 'MaxPerformance',
dataFile: datafile,
autoUpdate: false,
restrictedProperties: properties,
// Set to `true` 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 pipeline 'error' handler below.
suppressProcessExceptions: true
}).build();
// Logging of errors and other messages.
// Valid logs types are info, debug, warn, error
pipeline.on('error', console.error);
};
const http = require('http');
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;
}
const flowData = pipeline.createFlowData();
// Add any information from the request
// (headers, cookies and additional client side provided information)
flowData.evidence.addFromRequest(req);
flowData.process().then(function () {
res.statusCode = 200;
// 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-useragentclienthints-web-useragentclienthintsweb.js&utm_term=server
core.Helpers.setResponseHeaders(res, flowData);
res.setHeader('Content-Type', 'text/html');
// Obtain the client-hint evidence that was used for detection so it can be
// shown in the page.
const evidenceUsed =
pipeline.getElement('device').evidenceKeyFilter.filterEvidence(
flowData.evidence.getAll());
// Compile a response
res.end(compiledFunction(
{
evidenceUsed,
dataSourceTier: ExampleUtils.getDataTier(pipeline),
hardwareVendor: getValueHelper(flowData, 'hardwarevendor'),
hardwareName: getValueHelper(flowData, 'hardwarename'),
deviceType: getValueHelper(flowData, 'devicetype'),
platformVendor: getValueHelper(flowData, 'platformvendor'),
platformName: getValueHelper(flowData, 'platformname'),
platformVersion: getValueHelper(flowData, 'platformversion'),
browserVendor: getValueHelper(flowData, 'browservendor'),
browserName: getValueHelper(flowData, 'browsername'),
browserVersion: getValueHelper(flowData, 'browserversion')
})
);
});
});
// Don't run the server if under TEST
if (process.env.JEST_WORKER_ID === undefined) {
setPipeline(null);
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 deviceDetectionOnPremisePipelineBuilder.js:35
Definition fiftyone.devicedetection.onpremise/examples/onpremise/exampleUtils.js:41
