How to Integrate Face Recognition in Node.js (Express Example)
Step-by-step tutorial: add face registration, recognition, and liveness detection to a Node.js app with the ARSA Face Recognition API and native fetch.
What We're Building
In this tutorial you'll add three face recognition capabilities to a Node.js backend:
We'll use the ARSA Face Recognition API — a REST API, so there is no ML runtime to install, no model files to download, and no GPU required. Everything runs over HTTPS with multipart/form-data uploads. If you prefer Python, see our Python integration guide; for browser-side capture, see real-time face detection in JavaScript.
Prerequisites
fetch and FormData — no axios needed)Store the key in an environment variable, never in code:
export ARSA_API_KEY="your_api_key_here"
A Minimal API Client
Every ARSA endpoint authenticates with the x-key-secret header. Here's a small client module:
// arsaClient.js
const BASE_URL = "https://faceapi.arsa.technology/api/v1";
const API_KEY = process.env.ARSA_API_KEY;
async function arsaFetch(path, { method = "POST", headers = {}, body } = {}) {
const res = await fetch(`${BASE_URL}${path}`, {
method,
headers: { "x-key-secret": API_KEY, ...headers },
body,
});
const data = await res.json();
if (!res.ok) throw new Error(data.message || `HTTP ${res.status}`);
return data;
}
module.exports = { arsaFetch };
Step 1: Register a Face
Registration converts one or more photos of a person into a face embedding stored in your isolated database, keyed by a face_uid you choose (an employee ID, user UUID, etc. — alphanumeric, hyphens, underscores, dots, max 255 chars).
// register.js
const fs = require("fs");
const { arsaFetch } = require("./arsaClient");
async function registerFace(faceUid, imagePath) {
const form = new FormData();
const buffer = fs.readFileSync(imagePath);
form.append("face_image", new Blob([buffer], { type: "image/jpeg" }), "face.jpg");
return arsaFetch("/face_recognition/register_face", {
headers: { "x-face-uid": faceUid },
body: form,
});
}
registerFace("employee-001", "./alice.jpg").then(console.log);
// { status: 'success', message: 'employee-001 registered successfully', latency_ms: ... }
Tips for good registrations:
additional_face_image_0..2) of the same person to improve matching across angles and lightingStep 2: Recognize a Face (1:N Search)
Recognition takes a new photo and searches your whole registered database for the best match — this is 1:N face recognition, the operation behind attendance systems and face login.
// recognize.js
const fs = require("fs");
const { arsaFetch } = require("./arsaClient");
async function recognizeFace(imagePath) {
const form = new FormData();
const buffer = fs.readFileSync(imagePath);
form.append("face_image", new Blob([buffer], { type: "image/jpeg" }), "query.jpg");
return arsaFetch("/face_recognition/recognize_face", { body: form });
}
recognizeFace("./checkin-photo.jpg").then(result => {
for (const face of result.faces) {
console.log(face);
}
});
The response includes the matched face_uid and a similarity score for each detected face. How you threshold that score matters — read our guide to similarity thresholds and match scores before shipping.
Step 3: Add Liveness Detection
Recognition alone can be fooled by a printed photo or a phone screen. Passive liveness detection analyzes a single image for spoof artifacts — no user interaction required:
// liveness.js
const fs = require("fs");
const { arsaFetch } = require("./arsaClient");
async function checkLiveness(imagePath) {
const form = new FormData();
const buffer = fs.readFileSync(imagePath);
form.append("face_image", new Blob([buffer], { type: "image/jpeg" }), "selfie.jpg");
return arsaFetch("/face_liveness", { body: form });
}
For high-security flows such as KYC onboarding, chain the calls: liveness first, then recognition or 1:1 validation only if the image is live.
Wiring It Into Express
Here's the pattern for an Express route that accepts an upload from your frontend and proxies it to ARSA — your API key stays on the server, which is rule #1 of API key security:
// server.js
const express = require("express");
const multer = require("multer");
const { arsaFetch } = require("./arsaClient");
const app = express();
const upload = multer({ storage: multer.memoryStorage(), limits: { fileSize: 10 * 1024 * 1024 } });
app.post("/api/checkin", upload.single("photo"), async (req, res) => {
try {
const form = new FormData();
form.append("face_image", new Blob([req.file.buffer], { type: req.file.mimetype }), "photo.jpg");
const liveness = await arsaFetch("/face_liveness", { body: form });
if (liveness.faces?.some(f => f.liveness === "fake")) {
return res.status(403).json({ error: "Spoof detected" });
}
const form2 = new FormData();
form2.append("face_image", new Blob([req.file.buffer], { type: req.file.mimetype }), "photo.jpg");
const match = await arsaFetch("/face_recognition/recognize_face", { body: form2 });
res.json(match);
} catch (err) {
res.status(502).json({ error: err.message });
}
});
app.listen(3000);
Handling Errors and Limits
latency_ms to monitor performance from your regionNext Steps
The whole integration above fits in under 100 lines of Node.js. Grab a free API key and try it against your own photos.