Migrating from v1
Version 2 makes the validators follow SPEC.md, the official Spanish sources, by default. Four things change; everything else, every export name and alias included, stays as it was. The full list, with every detail, is MIGRATION.md.
Quick path: keep the v1 results
Section titled “Quick path: keep the v1 results”Every validator takes options as its second argument. These two give exactly the results of v1.0.11; a test compares both versions on about 490,000 inputs:
import { isValidNif } from "nif-dni-nie-cif-validation";
const V1_COMPATIBLE = { normalize: false, cifControl: "lenient" } as const;
isValidNif("G1234567D"); // falseisValidNif("G1234567D", V1_COMPATIBLE); // true, as in v1.0.11Use them as a stopgap for stored data, then move to the defaults.
What changes
Section titled “What changes”| # | Change | Who is affected | Keep the v1 behaviour |
|---|---|---|---|
| 1 | The CIF keys C, D, F, G, J, U and V need a digit control | CIFs such as G1234567D stop validating |
{ cifControl: "lenient" } |
| 2 | Input is normalized by default | Values with spaces, dots, hyphens or slashes, or a DNI without its leading zeros, start validating | { normalize: false } |
| 3 | TypeScript: the validators have a second parameter | ids.filter(isValidNif) no longer compiles |
Write ids.filter((id) => isValidNif(id)) |
| 4 | Only the package’s entry points can be imported | Imports of files inside dist/ |
Import from nif-dni-nie-cif-validation |
1. The CIF control follows CIF-3
Section titled “1. The CIF control follows CIF-3”The AEAT’s technical note gives a digit control to the keys A B C D E F G H J U V and a letter to N P Q R S W (CIF-3). Version 1 accepted either a letter or a digit for C, D, F, G, J, U and V, which has no official basis. It affects isValidNif, isValidCif (isValidLegalEntityNif) and isValidCifControlCode (isValidLegalEntityNifControlCode).
Before, in v1:
import { isValidCif } from "nif-dni-nie-cif-validation";
isValidCif("G1234567D"); // trueisValidCif("G12345674"); // trueAfter, in v2:
import { isValidCif } from "nif-dni-nie-cif-validation";
isValidCif("G1234567D"); // false: G takes a digitisValidCif("G12345674"); // trueisValidCif("G1234567D", { cifControl: "lenient" }); // true, as in v1Only C, D, F, G, J, U and V change: A, B, E and H always needed a digit, and N, P, Q, R, S and W a letter, in both versions.
2. The input is normalized
Section titled “2. The input is normalized”The validators now clean up the input before checking it: white space and dots (NORM-2), hyphens and slashes (NORM-3) are removed, and a DNI with fewer than 8 digits gets its leading zeros back (NORM-4). Cleaning up never makes an invalid document valid, so no true result of the full-document validators changes; some false results become true. The control-only helpers, isValidDniLetter and isValidCifControlCode, don’t check the format, so with separators their result can change in either direction: in v1, isValidCifControlCode("A 7727886") was true because the space counted as a 0 (see MIGRATION.md).
Before, in v1:
import { isValidCif, isValidDni, isValidNif } from "nif-dni-nie-cif-validation";
isValidNif(" 12.345.678-Z "); // falseisValidDni("1234567L"); // falseisValidCif("B-1234567-4"); // falseAfter, in v2:
import { isValidCif, isValidDni, isValidNif } from "nif-dni-nie-cif-validation";
isValidNif(" 12.345.678-Z "); // trueisValidDni("1234567L"); // trueisValidCif("B-1234567-4"); // trueisValidNif(" 12.345.678-Z ", { normalize: false }); // false, as in v1Lower case and the old 10-character NIE (X01234567L) were already accepted in v1, and still are. If you store the value, store its canonical form: normalize(), new in v2, returns it. See Normalizing and formatting input.
3. TypeScript: a second parameter
Section titled “3. TypeScript: a second parameter”Every validator is now (value: unknown, opts?: IsValidOptions) => boolean. The first parameter accepts more than before, which breaks nothing, but the second one means that TypeScript refuses a validator passed straight to an array method, whose second argument is the index:
import { isValidNif } from "nif-dni-nie-cif-validation";
const ids = ["12345678Z", "B12345675"];
// @ts-expect-error: the index (a number) is not an options objectids.filter(isValidNif);ids.filter((id) => isValidNif(id)); // ["12345678Z"]At runtime nothing changes: a number as options is ignored, so plain JavaScript keeps working.
4. Import from the package, not from dist/
Section titled “4. Import from the package, not from dist/”Version 2 ships ES modules and CommonJS and declares them in an exports map. Only the package itself, its subpaths (/locales/<code>, /generate, /zod, /valibot, /yup) and package.json can be imported, so a path into the build output fails with ERR_PACKAGE_PATH_NOT_EXPORTED:
const { isValidNif } = require("nif-dni-nie-cif-validation/dist/nif/nif");const { isValidNif } = require("nif-dni-nie-cif-validation");The validators and the other helpers are imported from nif-dni-nie-cif-validation; the locales, the generators and the schemas from their own entry points (/locales/<code>, /generate, /zod, /valibot, /yup). require() and import both work in Node.js 20 or newer and in every bundler.
New in v2
Section titled “New in v2”Nothing here breaks existing code:
validate(): the type, the canonical form and, for an invalid value, an error code, the SPEC rule and a message.normalize()andformat(),getNifType(),computeControlCharacter(),describeCifOrganisation()andisValidSpanishVat().- The options
rejectPlaceholders, and forvalidate()alsotypes,allowVatPrefixandlocale. - Messages in Spanish, Catalan, Basque and Galician.
- Test-data generators and Zod, Valibot and Yup schemas.
import { validate } from "nif-dni-nie-cif-validation";
validate("12345678A").error?.code; // "INVALID_CONTROL_CHARACTER"validate("12345678A").error?.rule; // "DNI-2"Checklist
Section titled “Checklist”-
Upgrade, and run your tests.
-
If stored CIFs with a letter control for C, D, F, G, J, U or V must stay valid, pass
{ cifControl: "lenient" }where you check them, and plan to fix the data. -
If some code relied on
falsefor values with separators, pass{ normalize: false }, or better, storenormalize(value). -
In TypeScript, replace
array.filter(isValidX)with an arrow function. -
Replace any import from
nif-dni-nie-cif-validation/dist/…with an import fromnif-dni-nie-cif-validation. -
For messages in another language, import its locale object and pass it as
locale.