onvif: a TypeScript ONVIF client that talks to real cameras rather than the specification
ONVIF client for all ONVIF devices with TypeScript types. Supports Profiles S/T/G/M/C/A, WS-Discovery, WS-Security/Digest, Events, Media/Media2, PTZ, Recording/Replay, and a 0.x compatibility layer.
At a glance
- What is it?
- onvif is a Node.js client for ONVIF devices with typed request and response interfaces, lazy-loaded service namespaces, WS-Discovery, digest and WS-Security authentication, events, media, PTZ and recording, and a compatibility layer for the older Cam API. The project says outright that it was built to work around real camera quirks across vendors.
- Who is it for?
- onvif fits a developer who has to talk to a specific camera and has found that a generic ONVIF library fails against that vendor's implementation, and who wants typed responses instead of parsed XML. It does not fit a team that wants one uniform API across many camera brands without testing each one, and the 1.0 release candidate means the API is still settling.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 3 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What the library covers
onvif is an ONVIF client for all ONVIF devices with TypeScript types. The profile list is the concrete scope: S, T, G, M, C and A, covering simple streaming, advanced streaming, generic device, media, continuous recording and analytics.
The feature list adds typed request and response interfaces from the latest ONVIF WSDL, generated by a separate onvif-generate-interfaces project. Events support pull-point, WS-BaseNotification, filters and an EventEmitter, with the details in the innerDocs events document. Services are lazy-loaded, and authentication covers WS-Security and Digest with MD5, SHA-1 or SHA-256, with Advanced Security marked experimental.
There is also vendor XML extension handling, with the xs:any and xs:anyAttribute cases documented separately, and low level SOAP XML helpers named build, parseSOAPString, request and requestContext. Those exist because the ONVIF specification does not describe every vendor's extension, and a client that cannot pass through unknown elements cannot talk to half the cameras in production.
WS-Discovery on the local network is there as well, exported as a Discovery value, and the package exposes a default Media Profile token bag through activeMediaProfile and setActiveMediaProfile.
The project is MIT licensed, written in TypeScript with a Promise API, and the last push was on 2026-10-22. The release history shows v1.0.0-rc.1 in September 2026, preceded by v0.8.3 and v0.8.2, and the v0.8.2 note says it is probably the last stable v0.x release unless proven otherwise, with v1.x coming.
Installing it and the shape of the API
The package needs Node.js 18 or newer, and the published package ships the compiled build directory in CommonJS with type declarations, the README, the licence and the changelog, so there is no need to clone the repository for normal use.
npm install onvifThe import is a named export of Onvif, and the README shows both module systems:
const { Onvif } = require('onvif'); // CommonJSimport { Onvif } from 'onvif'; // ESM / TypeScript (Node loads the CJS build)There are subpath exports for onvif/compatibility and onvif/compatibility/promises, which are the callback and Promise Cam wrappers, and the package exports map points all three entry points at the same compiled build with a require, import and default condition.
The usage shape is a constructor, a connect and a close:
import { Onvif } from 'onvif';
const onvif = new Onvif({
url: 'http://192.168.1.13:8000/onvif/device_service',
username: 'admin',
password: 'admin',
});
// or: { hostname, port, path?, useSecure? }
await onvif.connect();
const info = await onvif.device.getDeviceInformation();
console.log(info);
await onvif.close();The comment on the constructor line is worth noting, since it is the difference between one working and one broken setup: you can give a full url, or a hostname and port with an optional path and a useSecure flag.
After connect, the service namespaces are available as properties, onvif.media, onvif.ptz and so on, and the README notes that events is the eager exception. Call close when you no longer need the connection.
What connect actually does
connect() is not a trivial handshake, and the README describes its steps in detail because camera behaviour varies.
It synchronizes time, which matters for WS-Security since the timestamp is part of the digest calculation. It discovers service URLs via GetServices with IncludeCapability set to false, falling back to GetCapabilities if that fails. And when Media is available it loads profiles and video sources, preferring Media1, with the Media2 GetProfiles call running only if Media1 is missing or fails.
The media probe is the part that surprises people, because it can be turned off. Passing media: false skips media probing, which the README says is for door stations and reachability checks. Discovery.probe sets defaultMedia to false on access-only devices, meaning Profile C or A without Streaming or T in ProbeMatch, and connect honours that when media is omitted. You can override with media set to true or false explicitly, and for capability blocks from GetServices you call device.getServices with includeCapability set to true after connecting.
The error handling is also specific. On success connect emits connect and returns the instance, a failed connect rejects after clearing prior discovery state so it is safe to retry, and media probe failures emit warn but still complete the connection. That last behaviour is a considered choice: a camera that answers device queries but fails on media should still be usable for the parts that do work.
Migrating from the 0.x Cam API
The README carries a note for people arriving from the classic Cam API used in 0.x and 0.8, and it offers three routes.
You can stay on the old line with npm install onvif@previous, you can use the onvif/compatibility layer with this package, or you can browse the v0.x branch. The compatibility layer is what makes the second option practical, and the package exports it in two forms, the callback style and the Promise style, as onvif/compatibility and onvif/compatibility/promises.
That the project ships a compatibility layer inside the 1.x package is a deliberate decision about migration cost. A camera client library is embedded in whatever software you build on it, and the client API is usually spread across a codebase, so replacing it is not a dependency bump but a project. Giving people a shim inside the new major version means the upgrade is a change of import path rather than a rewrite.
The release notes reinforce the point. v0.8.2 is described as probably the last stable v0.x release, unless proven otherwise, with v1.x coming, and there is a v1.0.0-rc.1 release candidate. A release candidate means the API is close to final but not guaranteed, and the README's own advice to pin a specific version is the practical response to that.
The examples directory as a capability map
The examples directory is the most useful inventory of what the library can do, and the file names are self describing.
There are discovery.js and scan.ip.range.js for finding cameras, device.info.js and deviceio.relay.js for device queries, door.unlock.js for the relay output on a door station, analytics.rules.js for analytics, and osd.js for the on screen display.
The media examples cover media.snapshot.ts for a still image, media.stream.js and media2.profiles.js for the two media profiles, and imaging.settings.js for the imaging configuration. The PTZ example is ptz.keyboard.js, the replay example is replay.uri.js for recorded footage, and the events examples are events.motion.ts and events.with.filter.ts.
The networking examples show the range: https.digest.mjs for digest authentication over TLS, proxy.socks5.js for reaching a camera through a SOCKS proxy, and rtsp.socketio.ptz.js for the combination of a real time stream, a websocket bridge and pan tilt zoom control.
The compatibility examples are compatibility.cjs and compatibilityPromises.cjs, matching the two subpath exports, so the old API is exercised in the test suite rather than being a theoretical shim.
The test setup is worth noting as well. The test script starts and stops a happytime-onvif-server from the __tests__ directory, waits for it to become ready and hydrates profiles before running jest, which is how the library is tested against a real ONVIF server rather than only against mocks.
Where onvif is the wrong choice
The first limitation is the one the project is most honest about, which is that it is built to work around real camera quirks across vendors rather than to implement the specification cleanly. That is a strength for talking to one awkward camera and a liability for a fleet. Two cameras from different vendors will expose different subsets of the profile, and the library's workarounds are shaped by the devices its author happened to test against.
The second is the version state. The newest release is a 1.0.0 release candidate, which means the API you build against may still move before 1.0.0 final, and the compatibility layer exists precisely because the old API was widely used. Pinning a version is not optional advice here.
The third is the scope of the protocol. ONVIF covers device management, media, PTZ, events, recording and analytics, and a project that also needs to talk to a camera's proprietary vendor API is still writing that part by hand. There are vendor extension hooks in the library, and the innerDocs document on them is the way to find out how far that goes.
The comparison with the older Cam API is not really a comparison any more, since that line is closed to new work and the project has moved on. The honest comparison is with writing SOAP requests yourself against the WSDL, which is what the library is for, and with a vendor SDK, which is often better documented and much worse at talking to anything other than that vendor's own hardware.
Editorial conclusion
onvif fits a developer who has to talk to a specific camera and has found that a generic ONVIF library fails against that vendor's implementation, and who wants typed responses instead of parsed XML. It does not fit a team that wants one uniform API across many camera brands without testing each one, and the 1.0 release candidate means the API is still settling. Before adopting it, decide whether you need the 0.x Cam API or the new one, because the README points migrating users at onvif@previous, the compatibility layer or the v0.x branch, and read the connection documentation, because connect() probes media profiles and that behaviour is where access-only devices such as door stations break.
Frequently asked questions
What does the onvif package support?
It supports ONVIF profiles S, T, G, M, C and A, with typed request and response interfaces from the latest WSDL, WS-Discovery on the local network, WS-Security and Digest authentication with MD5, SHA-1 or SHA-256, events through pull-point and WS-BaseNotification, media and Media2, PTZ, recording and replay, and a compatibility layer for the older 0.x Cam API.
How do I install onvif and connect to a camera?
You need Node.js 18 or newer, install with `npm install onvif`, then create an Onvif instance with either a full url or a hostname and port, call `await onvif.connect()`, use the service namespaces such as onvif.device and onvif.media, and call `await onvif.close()` when finished. You can also pass autoConnect: true.
I am coming from the onvif 0.x Cam API, what should I do?
The README offers three routes: stay on the old line with npm install onvif@previous, use the onvif/compatibility layer with the new package, or browse the v0.x branch. The package exports the compatibility layer in both callback and Promise forms as onvif/compatibility and onvif/compatibility/promises.
What happens during connect() and why can it fail on a door station?
connect synchronizes time, discovers service URLs via GetServices and falls back to GetCapabilities, and probes media profiles, preferring Media1 and only calling Media2 GetProfiles if Media1 is missing or fails. Door stations and other access-only devices can fail the media probe, so you can pass media: false to skip it, and probe failures emit warn while still completing the connection.
Is onvif stable enough to depend on?
The newest release is v1.0.0-rc.1, a release candidate, and the v0.8.2 note says it is probably the last stable v0.x release unless proven otherwise. The README recommends pinning a specific version, and the project is MIT licensed with the last push on 2026-10-22.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/agsh-onvif)Was this useful?