# node-usb, a USB library for Node that speaks both the legacy API and WebUSB

> The usb package for Node.js is a native module that wraps libusb and exposes two APIs side by side: the original non-blocking legacy API and a WebUSB implementation that follows the browser specification. Its own README now points new work at the node-usb-rs repository, so the interesting question is when the TypeScript line is the right one to use.

**node-usb/node-usb** — Improved USB library for Node.js

- Repository: https://github.com/node-usb/node-usb
- Website: https://node-usb.github.io/node-usb/
- Stars: 1,655 · Forks: 284
- Language: TypeScript
- License: MIT
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/node-usb-node-usb

## Two APIs over one libusb, and a rewrite on the horizon

The usb package is described as a Node.JS library for communicating with USB devices, and it is a refactoring and rewrite of Christopher Klein's node-usb. Christopher Klein is listed as a contributor in package.json alongside Kevin Mehall, Tim Ryan and Rob Moran.

What makes the library distinctive is the pair of APIs it supports since v2.0.0. The WebUSB API follows the WebUSB Specification and is marked as recommended. The Legacy API retains the previous non-blocking API. So a project can move from the old shape to the specification shape without the underlying native layer changing, and the README notes that convenience methods exist to list or find devices and to convert between a legacy usb.Device and a WebUSB device.

The conversion is the useful part, and the examples make it concrete. A legacy device found by serial number can be turned into a WebUSB compatible one with WebUSBDevice.createInstance(device), and the example notes that the calls are blocking so the function is async. In the other direction, the webusb instance exposes requestDevice with a filters array, and a custom WebUSB can be constructed with a devicesFound function, which the README describes as able to return a promise so a UI can be displayed if required.

The authorisation model is also present in the examples, and it is the piece that most often surprises people. The default webusb instance follows the WebUSB spec and only returns authorised devices, so getDevices() on the default instance is not the same as a device list. The escape hatch is a WebUSB constructed with allowAllDevices set to true, which the README labels as bypassing the check for authorised devices.

And the one piece of news that matters most is a note at the very top of the README: development of this library has now moved to github.com/node-usb/node-usb-rs since v3.0.0. The TypeScript line is the 2.x series, and the current package version is 2.19.1.

## Installing it, and the platform specific parts

Native modules are bundled using prebuildify, so installation should be as simple as installing the package. With npm that is npm install usb, and with yarn it is yarn add usb.

```bash
npm install usb
```

```bash
yarn add usb
```

The library is now written in TypeScript, so the README notes that a separate types file is no longer required to be installed, and specifically that you should not install @types/usb. The package carries @types/w3c-web-usb as a runtime dependency, which is the type definition for the browser API it mirrors.

The platforms differ more than usual here, and the README covers them separately because the problems are different.

On Windows, if you get LIBUSB_ERROR_NOT_SUPPORTED when attempting to open your device, it is possible your device does not have a WinUSB driver for libusb to use. You can install one using Zadig, or the alternative is to use the UsbDK Backend of libusb by immediately calling usb.useUsbDkBackend(). The important constraint is that you cannot use multiple drivers on Windows, as they get exclusive access to the device, so switching between drivers means uninstalling and installing them as required.

On Linux, you need libudev to build libusb if a prebuild is not available, and on Ubuntu and Debian that is build-essential and libudev-dev. You may also need to modify your udev and permission rules in order to access your desired device, along the lines of a rule matching SUBSYSTEM usb, the idVendor and idProduct attributes, and a MODE of 0660 with a GROUP of the user you are in.

The Windows driver issue and the Linux udev rule are the two things that consume the time on a first install, and neither is a Node problem.

## The API surface, in the order the README presents it

The Getting Started section is a set of runnable examples, and reading them in order tells you the shape of the library.

The first is the migration note for v2.0.0. The legacy API exists on an object called usb on the main import, and the convenience functions exist as top level objects. So the old form was import * as usb from 'usb' followed by usb.getDeviceList(), and the new form is a named import of usb and getDeviceList from 'usb'.

Then the listing and finding helpers. getDeviceList returns a list of legacy Device objects. findByIds takes a vendor and product id, as in findByIds(0x59e3, 0x0a23), and returns a device if there is one. findBySerialNumber finds by serial number, and the example wraps it in an async function with the comment that it uses a blocking call.

The WebUSB examples follow. requestDevice with a filters array of an empty object returns the first matching device. The custom selection version builds a WebUSB with a devicesFound callback that filters on serialNumber, and getDevices lists authorised devices on the default instance, while a WebUSB with allowAllDevices set to true lists all of them.

The Electron section is the practical one for desktop applications. There is a maintained example repository, node-usb/node-usb-example-electron, and the README warns about a specific failure: if using a packaging system for Electron, ensure the node-usb library does not get recompiled, because the correct binaries are already shipped with the package. For electron-builder the settings given are buildDependenciesFromSource set to true, and nodeGypRebuild and npmRebuild both set to false.

That warning is a classic native module packaging problem and it is worth reading before you wire this into a desktop build, because a rebuild against a different Node ABI produces a module that fails to load at runtime.

## The limitations, and the shape of the package

The README lists what does not work, and the list is short enough to be worth quoting in full. It does not support configurations other than the default one, and it does not support isochronous transfers.

Those two omissions are the difference between this and a general purpose USB library. A device with multiple configurations, which is common in HID and composite devices, needs configuration handling that this does not do. Isochronous transfers, which is how audio and video streaming endpoints work, are simply absent, so a USB microphone or a webcam that streams frames is out of scope. For a HID reader or a serial adapter, the limitations rarely come up.

The repository layout shows how the native layer is assembled. There is a libusb/ directory that is a git submodule, a libusb.gypi file, a libusb_config/ directory, a binding.gyp for node-gyp, and src/ for the C++ source. The tsc/ directory is the TypeScript source, and typedoc.json and a docs/ pipeline generate the API documentation that is published at node-usb.github.io/node-usb.

The build tooling is prebuildify centric. The prebuild script is prebuildify --napi --strip --name node.napi, and there is a prebuild-cross variant using prebuildify-cross for the same NAPI name. The install script is node-gyp-build, which is what picks a prebuilt binary when one matches and falls back to a source build when it does not. The prepublishOnly and prebuild-download scripts use prebuildify-ci download, which is how the binaries get onto the registry in the first place.

The test script runs mocha with a ten second timeout over the test directory, and there is a valgrind target that runs the same suite under a leak check with the possibly-lost display disabled, which is the right way to test a native module for memory leaks.

One inconsistency to note for anyone pinning a version: the README's prerequisites section says Node.js 10.20.0 or higher, while the engines field in package.json is stricter, requiring 12.22.0 or higher but below 13.0, or 14.17.0 and above.

## Conclusion

node-usb suits a Node project that needs to talk to a USB device and wants the WebUSB shape of the API, particularly in Electron or in a pipeline that also has a browser front end, since the same device object works in both. It is the wrong choice for new work, because the README states that development moved to the node-usb-rs repository since v3.0.0, and it is the wrong choice if you need isochronous transfers or a non-default configuration, since both are listed as unsupported. Before pinning a version, check the node version range in package.json, because the engines field is narrow, and on Windows expect to install a WinUSB driver with Zadig before a device will open.

## FAQ

### Is node-usb still actively developed?

The README carries a note at the top saying that development of this library has now moved to github.com/node-usb/node-usb-rs since v3.0.0. The TypeScript line published here is the 2.x series, with version 2.19.1 the current release.

### How do I install the usb package for Node?

Run npm install usb or yarn add usb. Native modules are bundled with prebuildify so installation should be as simple as installing the package, and no separate types file is needed because the library is written in TypeScript, so do not install @types/usb.

### What should I do if I get LIBUSB_ERROR_NOT_SUPPORTED on Windows?

Your device probably has no WinUSB driver for libusb to use. Install one with Zadig, or use the UsbDK Backend by immediately calling usb.useUsbDkBackend(). Note that you cannot use multiple drivers on Windows since they get exclusive access to the device.

### What does node-usb not support?

The README states it does not support configurations other than the default one, and it does not support isochronous transfers. Devices with multiple configurations and streaming endpoints are therefore out of scope.

### How do I package node-usb in an Electron app?

Ensure the library is not recompiled, because the correct binaries ship with the package. The README gives electron-builder settings for this: buildDependenciesFromSource set to true, and nodeGypRebuild and npmRebuild both set to false. There is also a maintained example at node-usb/node-usb-example-electron.

## Sources

- [License: MIT](https://github.com/node-usb/node-usb/blob/main/LICENSE)
- [node-usb/node-usb on GitHub](https://github.com/node-usb/node-usb)
- [Project website](https://node-usb.github.io/node-usb/)
- [README](https://github.com/node-usb/node-usb/blob/main/README.md)
- [Releases](https://github.com/node-usb/node-usb/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/node-usb-node-usb
