Skip to content

Commit

Permalink
npi validator
Browse files Browse the repository at this point in the history
  • Loading branch information
coryasilva committed Feb 3, 2024
1 parent f63b76c commit 4f471e2
Show file tree
Hide file tree
Showing 5 changed files with 179 additions and 1 deletion.
37 changes: 36 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,37 @@
# npi-validator
Validate NPI numbers for healthcare physicians or organizations

Validation function for National Provider Identifier (NPI) numbers. Works for healthcare physicians and organizations in the United States or other countries that use the NPI standard.

This is an implementation of the Luhn formula (mod 10 double add double) check digit, see the [CMS.gov specification](https://www.cms.gov/Regulations-and-Guidance/Administrative-Simplification/NationalProvIdentStand/Downloads/NPIcheckdigit.pdf) for more information.

## Install

```sh
npm install npi-validator
```

## Usage

```js
import npiValid from 'npi-validator';

const npi = 1234567893;
const valid = npiValid(npi);

console.log(valid);
//=> true
```

## API

### `function npiValid(id: string | number, prefix?: string | number): boolean`

```js
import npiValid from 'npi-validator';

const npi = 1234567893;
const valid = npiValid(npi);

console.log(valid);
//=> true
```
18 changes: 18 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
Validates NPI numbers. See the [CMS.gov specification](https://www.cms.gov/Regulations-and-Guidance/Administrative-Simplification/NationalProvIdentStand/Downloads/NPIcheckdigit.pdf) for more information.
@example
```
import npiValid from 'npi-validator';
const npi = 1234567893;
const valid = npiValid(npi);
console.log(valid);
//=> true
```
@param id - npi number; must be 10 digits
@param prefix - prefix number; defaults to 80840; must start with 80,the health prefix, and be followed by the 3 digit country code
*/
export function npiValid(id: string | number, prefix?: string | number): boolean;
47 changes: 47 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
const {ceil, floor} = Math;
const toInt = s => Number.parseInt(s, 10);

const sumDigits = int => {
let sum = 0;
while (int) {
sum += int % 10;
int = floor(int / 10);
}

return sum;
};

const doubleAlternate = (value, index) => {
let pos = index ?? value.length - 1;
let sum = 0;
let alt = true;
for (; pos >= 0; pos--) {
const cur = toInt(value.charAt(pos));
sum += alt ? sumDigits(cur * 2) : cur;
alt = !alt;
}

return sum;
};

export function npiValid(id, prefix = '80840') {
const prefixRegex = /^80\d{3}$/;
const npiRegex = /^\d{10}$/;
const usPrefixSum = 24; // Precalculated value for 80840 (United States)
const npi = id?.toString() ?? '';
const pre = prefix?.toString() ?? '';

if (!npiRegex.test(npi) || !prefixRegex.test(pre)) {
return false;
}

const check = toInt(npi.charAt(9));
const npiSum = doubleAlternate(npi, 8);
const preSum = pre === '80840' ? usPrefixSum : doubleAlternate(pre, 3);
const total = npiSum + preSum;
const totalCeil = ceil(total / 10) * 10;

return totalCeil - total === check;
}

export default npiValid;
40 changes: 40 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"name": "npi-validator",
"version": "1.0.0",
"description": "Validate NPI numbers for healthcare physicians or organizations.",
"homepage": "https://github.com/coryasilva/npi-validator#readme",
"bugs": {
"url": "https://github.com/coryasilva/npi-validator/issues"
},
"repository": {
"type": "git",
"url": "git+https://github.com/coryasilva/npi-validator.git"
},
"author": {
"name": "Cory Silva",
"url": "https://corysilva.com"
},
"keywords": [
"healthcare",
"npi",
"validation"
],
"engines": {
"node": ">=18"
},
"type": "module",
"sideEffects": false,
"main": "index.js",
"exports": {
"types": "./index.d.ts",
"default": "./index.js"
},
"scripts": {
"test": "xo && ava && tsc index.d.ts"
},
"devDependencies": {
"ava": "^6.0.1",
"typescript": "^5.3.3",
"xo": "^0.56.0"
}
}
38 changes: 38 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/* eslint-disable unicorn/numeric-separators-style */
import test from 'ava';
import npiValid from './index.js';

test('NPI validation with default United States prefix', t => {
t.is(npiValid(1234567893), true);
t.is(npiValid('1234567893'), true);
t.is(npiValid(1234567890), false);

// Bad inputs
t.is(npiValid(''), false);
t.is(npiValid(' '), false);
t.is(npiValid(undefined), false);
t.is(npiValid(null), false);
t.is(npiValid({}), false);
t.is(npiValid([]), false);
t.is(npiValid(true), false);
t.is(npiValid(false), false);
t.is(npiValid(), false);
});

test('NPI validation with custom prefix', t => {
t.is(npiValid(1234567894, 80123), true);
t.is(npiValid(1234567894, '80123'), true);
t.is(npiValid(1234567894, 8012), false);
t.is(npiValid(1234567894, 81840), false);

// Bad inputs
t.is(npiValid(1234567893, ''), false);
t.is(npiValid(1234567893, ' '), false);
t.is(npiValid(1234567893, '80 '), false);
t.is(npiValid(1234567893, undefined), true);
t.is(npiValid(1234567893, null), false);
t.is(npiValid(1234567893, {}), false);
t.is(npiValid(1234567893, []), false);
t.is(npiValid(1234567893, true), false);
t.is(npiValid(1234567893, false), false);
});

0 comments on commit 4f471e2

Please sign in to comment.