The joi npm package is a mature, actively maintained JavaScript schema-description and data-validation library, and the correct package to install today is plain joi — the older @hapi/joi package is deprecated and should be migrated away from. If you validate request bodies, config files, or any untrusted input in a Node.js service, joi is a solid choice, and validation itself is a security control. This review covers where the package stands, the naming history that trips people up, and how to use npm joi in a way that actually reduces risk.
Where joi stands today
Joi is one of the most established validation libraries in the Node.js ecosystem, with a very large install base — tens of thousands of dependent projects on the npm registry. It is maintained by Sideway Inc. and continues to see releases; the current major line is 18.x. For a library this central to input handling, active maintenance is exactly what you want, because validation bugs are security bugs.
You install it the obvious way:
npm install joi
And use it to describe and enforce a schema:
const Joi = require("joi");
const schema = Joi.object({
username: Joi.string().alphanum().min(3).max(30).required(),
email: Joi.string().email().required(),
age: Joi.number().integer().min(0).max(120),
}).unknown(false); // reject unexpected keys
const { error, value } = schema.validate(req.body);
if (error) {
return res.status(400).json({ message: error.details[0].message });
}
The @hapi/joi naming history
The single most common source of confusion around npm joi is the package name, and it has real security consequences. Joi originated inside the hapi framework project and for a period was published as @hapi/joi. After a change of leadership in the hapi project, joi moved to Sideway and was published again under the plain joi name on npm.
Today:
joiis the actively maintained package. Install this.@hapi/joiis deprecated. Its final release (17.1.1) was published years ago, and the package carries a deprecation notice telling users to runnpm install joiinstead.
If your project still depends on @hapi/joi, migrate. In most codebases the change is mechanical: swap the dependency and update your imports.
// Old
const Joi = require("@hapi/joi");
// New
const Joi = require("joi");
Do a proper migration rather than a blind find-and-replace, because there were breaking changes across major versions. Read the changelog for the versions you are crossing and run your test suite. Staying on a deprecated package means you stop receiving fixes, which is a slow-motion security problem.
Why validation is a security control
Treating joi as merely a convenience misses the point. Schema validation at the boundary is a defense-in-depth layer against several attack classes:
It constrains injection surface. By rejecting inputs that do not match an expected shape — a number field that must be an integer in a range, a string that must match a strict pattern — you cut off malformed payloads before they reach a database query or a shell call. Validation is not a substitute for parameterized queries, but it narrows what an attacker can even attempt.
It prevents mass assignment. Calling .unknown(false) (or the equivalent stripping behavior) rejects properties you did not define, so a client cannot smuggle in an unexpected field like isAdmin and have it flow into an object update.
It enforces type safety at runtime. JavaScript will happily coerce and mishandle unexpected types. A validated schema guarantees downstream code receives the shape it assumes, which eliminates a class of logic bugs that turn into vulnerabilities.
Use an allowlist mindset: define exactly what is acceptable and reject everything else. That is precisely what a joi schema expresses, and it is the opposite of trying to blocklist bad values.
Dependency hygiene for joi and its tree
Even a well-maintained library sits in a dependency graph, and that graph is your real attack surface. A few habits keep npm joi usage safe over time:
- Pin and audit. Commit your lockfile and run
npm auditin CI so a newly disclosed advisory in joi or any transitive dependency surfaces on the next build. - Keep current. Track new joi releases and apply patches promptly rather than pinning to an old major indefinitely. The
@hapi/joisituation is the cautionary tale — a deprecated package is a dependency that will never get its next security fix. - Watch the whole tree, not just direct deps. Most real risk comes from packages you never chose to install directly. Software composition analysis such as Safeguard's SCA inventories every direct and transitive dependency, flags known CVEs, and can catch a deprecated or vulnerable package before it ships. It is the automated version of the discipline above.
For a broader look at defending against the bugs validation helps prevent, our code injection attack guide covers the source-to-sink pattern that strong input validation interrupts.
FAQ
Should I use joi or @hapi/joi?
Use joi. The @hapi/joi package is deprecated and its final release is years old; it explicitly directs users to install joi instead. Migrate any project still on @hapi/joi by swapping the dependency and updating imports, checking the changelog for breaking changes between versions.
Is joi still maintained?
Yes. Joi is maintained by Sideway Inc. and continues to receive releases, with the current major line at 18.x. It has a very large dependent-project base on npm, which for a validation library is important because validation flaws are security flaws.
Does using joi make my app secure?
Joi is a strong input-validation layer and a real defense-in-depth control, but it is not a complete security solution. You still need parameterized database queries, proper authentication and authorization, output encoding, and dependency scanning. Validation narrows the attack surface; it does not replace the other controls.
How do I migrate from @hapi/joi to joi?
Install joi, remove @hapi/joi, and change your require/import statements from @hapi/joi to joi. Because there were breaking changes across major versions, review the changelog for the versions you are crossing and run your full test suite rather than doing a blind find-and-replace.