crypto.hash()
Hashes a single value in one call and returns the digest directly, without a Hash object.
crypto.hash(algorithm, data[, options])- Added in Node.js v21.7.0, v20.12.0
Parameters
| Parameter | Type | Description |
|---|---|---|
$algorithm | string | undefined | The digest algorithm name, such as 'sha256' or 'sha512'. |
$data | string | Buffer | TypedArray | DataView | The value to hash. A string is encoded as UTF-8 before hashing; to use a different input encoding, convert it yourself with Buffer.from() first. |
$optionsoptional | Object | string= 'hex' | Either an options object, or a string used directly as the output encoding. |
$options.outputEncodingoptional | string= 'hex' | Encoding used for the returned digest. Use 'buffer' to receive raw bytes. |
$options.outputLengthoptional | number | For XOF hash functions such as 'shake256', the desired output length in bytes. Required for XOF functions with no default output length. |
Return value
string | BufferThe digest, hex-encoded by default, or a Buffer when the output encoding is 'buffer'.
Errors
Throws ERR_CRYPTO_UNKNOWN_DIGEST if the algorithm is unsupported, or ERR_INVALID_ARG_TYPE if data is not a string or byte source.
Changelog
- PHP v25.5.0, v24.13.1The API is no longer experimental.
- PHP v24.4.0The outputLength option was added for XOF hash functions.
crypto.hash() collapses the three-call ritual createHash().update().digest() into one expression. It is both shorter to write and genuinely faster for small inputs, because Node performs the whole operation in C++ without constructing a JavaScript Hash object or its stream machinery.
When to reach for it
Use it whenever the data is already in memory and fits comfortably in a single allocation — a request body, a config blob, a cache key, a column value. That covers the large majority of hashing in application code.
Keep crypto.createHash() for the cases crypto.hash() cannot serve: data arriving in chunks, a file you want to stream rather than read, or any input large enough that materialising it as one buffer is a problem. The official docs are explicit that for large data createHash() is the better choice.
What trips people up
The default output differs from digest(). crypto.hash() returns hex by default, whereas hash.digest() with no argument returns a Buffer. If you want bytes, ask for 'buffer' explicitly.
Availability is the other catch: this landed in Node 20.12.0 and 21.7.0, so it is missing on Node 18 and earlier. It also has no string inputEncoding parameter — strings are always UTF-8, and hashing hex or base64 input means calling Buffer.from(value, 'hex') yourself.
Example
When to use it
- Fingerprinting a request body to build a cache key in one line
- Hashing a config object to detect whether it changed since last boot
- Deriving a deterministic shard key from a user identifier
- Computing an ETag for a small in-memory response payload
Frequently asked questions
Is crypto.hash() faster than crypto.createHash()?
Yes, for data small enough to fit in a single memory allocation, because it skips constructing the JavaScript Hash object and its stream plumbing. For large data the official docs recommend createHash() instead, so you can stream rather than buffer the whole input.
What Node version added crypto.hash()?
It was added in Node 21.7.0 and backported to 20.12.0, and stopped being marked experimental in 24.13.1 and 25.5.0. It does not exist on Node 18 or earlier, so guard with typeof crypto.hash === 'function' if you support those.
Why does crypto.hash() return a string when digest() returns a Buffer?
Their defaults differ: crypto.hash() defaults to 'hex' output while hash.digest() with no argument returns a Buffer. Pass 'buffer' as the third argument to crypto.hash() to get raw bytes.
More examples
A cache key from a request body
Canonicalising before hashing is what makes the key stable — without the sorted replacer the two calls would hash different JSON strings and produce two cache entries for one logical query. Truncating to 32 hex characters keeps 128 bits, which is plenty for a cache key.
const crypto = require('node:crypto');
const cache = new Map();
function cacheKey(endpoint, params) {
// Sort keys so {a:1,b:2} and {b:2,a:1} produce the same key.
const canonical = JSON.stringify(params, Object.keys(params).sort());
return endpoint + ':' + crypto.hash('sha256', canonical).slice(0, 32);
}
function lookup(endpoint, params) {
const key = cacheKey(endpoint, params);
if (!cache.has(key)) cache.set(key, { computedAt: 'once' });
return key;
}
const k1 = lookup('/search', { q: 'node', page: 1 });
const k2 = lookup('/search', { page: 1, q: 'node' });
console.log('key:', k1);
console.log('property order ignored:', k1 === k2);
console.log('cache entries:', cache.size);When to fall back to createHash()
Both paths yield the same digest; the difference is peak memory. readFileSync loads the entire file, which is fine for kilobytes and disastrous for gigabytes, whereas the streaming form holds one chunk at a time.
import crypto from 'node:crypto';
import fs from 'node:fs';
import { pipeline } from 'node:stream/promises';
fs.writeFileSync('hash-compare.txt', 'hello world');
// Small and already in memory: crypto.hash() is the faster, shorter path.
const small = crypto.hash('sha256', fs.readFileSync('hash-compare.txt'));
// Large or streamed: createHash() avoids holding the whole file at once.
const streaming = crypto.createHash('sha256');
await pipeline(fs.createReadStream('hash-compare.txt'), streaming);
console.log('one-shot :', small);
console.log('streamed :', streaming.digest('hex'));
console.log('identical:', small === 'b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9');
fs.unlinkSync('hash-compare.txt');Guarding for older Node versions
The feature check keeps a library working on Node 18 while using the faster path where it exists. The last two lines show the missing inputEncoding: passing the string 'deadbeef' hashes eight ASCII characters, not the four bytes you probably meant.
const crypto = require('node:crypto');
// crypto.hash() only exists on Node >= 20.12.0 / 21.7.0.
const digest = typeof crypto.hash === 'function'
? (algorithm, data) => crypto.hash(algorithm, data)
: (algorithm, data) => crypto.createHash(algorithm).update(data).digest('hex');
console.log('available here:', typeof crypto.hash === 'function');
console.log('sha256:', digest('sha256', 'hello world'));
// There is no inputEncoding parameter — decode non-UTF-8 strings yourself.
console.log('hex input decoded:', crypto.hash('sha256', Buffer.from('deadbeef', 'hex')));
console.log('hex input as text:', crypto.hash('sha256', 'deadbeef'));
Discussion