pify for Wrapping Node.js Callback APIs
Promisify a callback-style function
At a glance
- What is it?
- pify converts callback-style JavaScript functions into Promise-based APIs, with optional support for multiple callback values and whole-module wrapping. It is most useful for Node.js developers adapting existing callback libraries without rewriting their call sites.
- Who is it for?
- Adopt pify when you control callback-style JavaScript and want predictable Promise adapters for individual functions or module objects. Do not choose it for functions that already return Promises, rely on unusual callback layouts without configuring `errorFirst`, or need Node.js-specific custom promisification behavior.
- 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 35 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
pify Targets Final-Argument Callback-Style JavaScript
pify solves a narrow interoperability problem: JavaScript libraries often accept input arguments and then report their result through a callback, while application code may prefer `async` and `await`. Rather than requiring a rewrite of the callback library, pify returns a wrapped function that exposes the result through a `Promise`. The intended users are JavaScript and Node.js developers integrating libraries such as callback-based filesystem or HTTP APIs.
The default assumption is conventional Node.js error-first behavior. A successful callback resolves the Promise with its second argument, while a callback that receives an error rejects it. This is a sensible fit for APIs such as `fs.readFile`, but it is not universal. The `errorFirst` option exists for functions such as `fs.exists()` and browser or Chrome Extension APIs whose callbacks do not place an error first.
The package is ESM and supports Node.js `>=14.16`. Its published files are limited to `index.js` and `index.d.ts`, while `sideEffects` is set to `false`. That makes pify a small library dependency, but it does mean the project is specifically oriented toward modern JavaScript and Node.js rather than older module systems.
Installing pify and Reading package.json with await
The README gives one installation command. Run it in the directory containing the application that will consume the Promise wrapper:
npm install pifyAfter installation, import both the package and the callback-based Node.js module. This first use wraps `fs.readFile`, passes the ordinary method arguments, and awaits the resulting value before parsing it as JSON:
import fs from 'node:fs';
import pify from 'pify';
const data = await pify(fs.readFile)('package.json', 'utf8');
console.log(JSON.parse(data).name);
//=> 'pify'The callback passed internally by the wrapper is not visible at the call site. The wrapped function receives the same `'package.json'` and `'utf8'` arguments and returns a `Promise` for the file contents. If the underlying operation reports an error through the error-first callback, the wrapper rejects that Promise rather than returning the callback's value.
This example only works at the top level of an ES module that supports `await`. A CommonJS module or a JavaScript file that does not permit top-level `await` would need its own module structure, but the README does not document a separate pify setup for those cases.
Whole-Module Wrapping Changes Every Selected Method
pify accepts either one function or an object containing methods. When passed an object, it returns an object whose selected methods are Promise wrappers. This is more than a convenience for a large Node.js module: it avoids creating a separate adapter for each method.
The README demonstrates wrapping `node:fs` and then calling the wrapped method with its normal arguments:
import fs from 'node:fs';
import pify from 'pify';
const data2 = await pify(fs).readFile('package.json', 'utf8');
console.log(JSON.parse(data2).name);
//=> 'pify'Selection is controlled by `include` and `exclude`. `include` accepts strings or regular expressions and identifies methods to wrap. `exclude` also accepts strings or regular expressions, and methods not excluded are wrapped. Its default expression is `[/.+(?:Sync|Stream)$/]`, which leaves methods whose names end in `'Sync'` or `'Stream'` untouched.
That default is a design choice with consequences. Synchronous methods do not fit the asynchronous callback contract that pify expects. Stream-related names may represent a different interface, but the documented rule remains name-based exclusion rather than a documented capability check. Applications with unusual method names should set `exclude` explicitly instead of assuming every returned property behaves like a promisifiable function. The README does not specify behavior for nested objects or inherited properties, so the safest scope is a flat module of documented methods.
multiArgs Preserves HTTP-Style Multi-Value Responses
Most error-first callbacks provide one useful value, so pify resolves with the callback's second argument by default. Some libraries provide more than one. The README uses `request` as the example: its caller may need both an HTTP response and a response body.
Set `multiArgs` to `true` when those values matter:
import request from 'request';
import pify from 'pify';
const pRequest = pify(request, {multiArgs: true});
const [httpResponse, body] = await pRequest('https://sindresorhus.com');With this option, successful resolution produces an array containing all callback arguments except the error. Rejection produces an array containing all callback arguments, including the error. That rejection detail matters to consumers: a catch handler expecting only an `Error` value must account for the documented multi-argument rejection shape.
The feature is useful when an API has a stable multiple-value callback contract. It is not a general response normalization layer. pify does not merge callback values into an object, name the positions, or verify their types. The array order is the callback's order. If a dependency later changes the number or order of callback arguments, the wrapper does not shield callers from that change.
Function Modules and Class Methods Need Context Preservation
A function can also be an object with attached methods. By default, pify treats a function input as the main callable and promisifies that function too. `excludeMain: true` changes that choice so attached methods are wrapped while the original function itself remains callable.
This matters because the README's class-method example identifies a common failure mode. An unbound class method has no access to its instance. Calling `pify(someInstance.someFunction)` by itself does not supply the instance context. Binding the method before wrapping it does:
import pify from 'pify';
import SomeClass from './some-class.js';
const someInstance = new SomeClass();
const someBoundFunction = pify(someInstance.someFunction.bind(someInstance));The README also shows wrapping the whole instance, which preserves method calls through the wrapped object. The README also shows wrapping the whole instance and calling its method through the wrapped object.
That final code uses a generic method name rather than reproducing the README's `someFunction` example. For production code, use the actual method name and argument list from the class. The underlying point remains specific: pify converts callback invocation but cannot invent missing receiver context. TypeScript users also face a separate limitation with overloaded functions. The README says only the final overload is selected, and it provides a type assertion as the documented way to select a different overload.
pify Differs from Node.js util.promisify
Node.js includes `util.promisify`, so adding pify is not automatically the best decision. The README presents several concrete differences. pify can wrap a complete module or object, whereas the Node.js utility is described as wrapping a specific method. pify also supports `multiArgs`, and it deliberately avoids the special "magic behavior" used for certain Node.js methods.
That last distinction represents a choice about predictability. A wrapper with fewer API-specific exceptions is easier to reason about when adapting third-party libraries, particularly when a method looks callback-based but is not one of Node.js's specially handled functions. The cost is that users do not receive Node.js-specific custom promisified behavior through pify. Choosing Node.js's built-in utility can avoid an external dependency when its method-level model already matches the application.
The README claims pify is faster and links that claim to an issue discussion, but it does not provide a benchmark in the project documentation. This article therefore does not treat that claim as a reason to adopt it. The observable design differences are sufficient: use pify for whole-object wrapping or multi-value callbacks, and consider `util.promisify` when a standard Node.js method and built-in implementation are enough.
pify Maintenance, Compatibility, and Upgrade Boundaries
The repository is not archived, and its last push was on 2026-09-18. That is recent repository activity, but the listed package releases are older: `v6.1.0` was released on 2022-09-02, `v6.0.0` on 2022-06-06, and `v5.0.0` on 2020-02-14. The repository's current maintenance activity therefore should not be confused with a stream of recent npm releases.
The MIT license permits use, modification, distribution, and sublicensing under its conditions, including preservation of the copyright and license notice. That is a factual summary of the license identifier, not legal advice. Consumers still need to review the repository's `license` file for the exact text when redistribution terms matter.
Compatibility creates a clear upgrade boundary. `package.json` declares `"type": "module"`, exports the default implementation from `./index.js`, and provides types from `./index.d.ts`. It requires Node.js `>=14.16`. A CommonJS-only consumer or a runtime below that version is outside the documented package configuration. The project also uses TypeScript declaration tests and type tests, but the overload issue in the README means declaration inference still needs review when adapting overloaded functions.
Editorial conclusion
Adopt pify when you control callback-style JavaScript and want predictable Promise adapters for individual functions or module objects. Do not choose it for functions that already return Promises, rely on unusual callback layouts without configuring `errorFirst`, or need Node.js-specific custom promisification behavior. Verify first that the target callback follows the expected final-argument convention, especially because the README does not document a custom callback-position option. On current evidence, pify remains the more suitable choice when whole-module wrapping or `multiArgs` is required, while `util.promisify` is the lower-dependency option for ordinary Node.js error-first callbacks.
Frequently asked questions
How do I install pify in a Node.js project?
Run `npm install pify`. The package declares Node.js `>=14.16` and uses ES modules through `"type": "module"`.
How does pify differ from Node.js util.promisify?
The README says pify can wrap a whole module or object and can preserve multiple callback arguments with `multiArgs`. It also says pify avoids the special custom promisification behavior provided for certain Node.js methods.
How do I promisify a class method without losing its context?
Bind the method to its instance before passing it to pify, or wrap the instance and call the method through the wrapped object. An unbound method has no receiver context, according to the README.
How do I wrap only selected methods from a module?
Use the `include` option with strings or regular expressions. Alternatively, use `exclude` to identify methods that should remain untouched; the default excludes names ending in `'Sync'` or `'Stream'`.
What happens when a pify callback returns multiple values?
With `{multiArgs: true}`, successful calls resolve to an array containing all callback arguments except the error. Rejections contain an array of all callback arguments, including the error.
Does pify support TypeScript function overloads?
The README states that pify selects only the last overload. It documents a type assertion as the way to expose a different overload when necessary.
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/sindresorhus-pify)Was this useful?