Thai National ID Card Reader

A TypeScript library for reading Thai National ID Cards using PC/SC compatible smartcard readers. Built with event-driven architecture, full type safety, and automatic data extraction capabilities.
Features
- β
Full TypeScript Support: Complete type safety and IntelliSense support
- β
Dual Module Support: Both CommonJS (
require()) and ESM (import) compatibility
- β
Event-Driven Architecture: Clean EventEmitter-based integration
- β
Automatic Data Extraction: Reads all available card data by default
- β
Smart Event Management: Intelligent card state tracking with no spurious events
- β
Comprehensive Documentation: Extensive JSDoc coverage and examples
- β
Error Handling: Robust error handling with detailed status codes
- β
Modern Architecture: Built with current JavaScript/TypeScript best practices
Installation
npm install thai-national-id-card-reader
git clone https://github.com/your-username/thai-national-id-card-reader.git
cd thai-national-id-card-reader
npm install
npm run build
Module Formats
The library is built to support both CommonJS and ESM module systems:
- CommonJS:
dist/index.cjs - For use with require()
- ESM:
dist/index.js - For use with import
- TypeScript:
dist/index.d.ts - Type definitions for both formats
The package.json uses conditional exports to automatically serve the correct format based on how you import it.
Prerequisites
Windows
- Install Node.js (recommended: nvm-windows)
- Install Windows build tools:
npm install --global windows-build-tools
Ubuntu & Raspberry Pi
sudo apt-get update
sudo apt-get install libpcsclite1 libpcsclite-dev pcscd
sudo systemctl start pcscd
sudo systemctl enable pcscd
macOS
- Install Xcode Command Line Tools:
xcode-select --install
- PC/SC framework is included with macOS
Quick Start
CommonJS Usage
const { ThaiIdCardReader } = require('thai-national-id-card-reader');
const cardReader = new ThaiIdCardReader({
debug: false,
exitOnReadError: false
});
cardReader.on('device-connected', (event) => {
console.log('β
Card reader connected:', event.data.message);
});
cardReader.on('card-inserted', (event) => {
console.log('π³ Card detected:', event.data.message);
});
cardReader.on('card-data', (event) => {
const data = event.data;
console.log('π Thai National ID Card Data:');
console.log('ββ Citizen ID:', data.cid);
console.log('ββ Thai Name:', data.name);
console.log('ββ English Name:', data.nameEn);
console.log('ββ Date of Birth:', data.dob);
console.log('ββ Gender:', data.gender);
console.log('ββ Address:', data.address);
console.log('ββ Issue Date:', data.issueDate);
console.log('ββ Expire Date:', data.expireDate);
console.log('ββ Issuer:', data.issuer);
console.log('ββ NHSO Data:', data.nhso ? JSON.stringify(data.nhso, null, 2) : 'Not Available');
console.log('ββ Laser ID:', data.laserId || 'Not Available');
console.log('ββ Photo:', data.photo ? 'Available (Base64)' : 'Not Available');
});
cardReader.on('card-removed', (event) => {
console.log('π€ Card removed:', event.data.message);
});
cardReader.on('card-error', (event) => {
console.error('β Error:', event.data.message);
});
cardReader.init();
console.log('π Thai National ID Card Reader initialized. Insert a card...');
process.on('SIGINT', () => {
console.log('\\nπ Shutting down...');
cardReader.destroy();
process.exit(0);
});
ESM Usage
import { ThaiIdCardReader } from 'thai-national-id-card-reader';
const cardReader = new ThaiIdCardReader({
debug: false,
exitOnReadError: false
});
cardReader.on('device-connected', (event) => {
console.log('β
Card reader connected:', event.data.message);
});
cardReader.on('card-data', (event) => {
const data = event.data;
console.log('π Thai National ID Card Data:');
console.log('ββ Citizen ID:', data.cid);
console.log('ββ Thai Name:', data.name);
});
cardReader.init();
console.log('π Thai National ID Card Reader initialized. Insert a card...');
process.on('SIGINT', () => {
console.log('\nπ Shutting down...');
cardReader.destroy();
process.exit(0);
});
TypeScript Usage
import { ThaiIdCardReader, ThaiIdCardData, CardDataEvent } from 'thai-national-id-card-reader';
const cardReader = new ThaiIdCardReader({
debug: true,
exitOnReadError: false
});
cardReader.on('card-data', (event: CardDataEvent) => {
const data: ThaiIdCardData = event.data;
if (data.cid) {
console.log(`Thai Citizen ID: ${data.cid}`);
}
if (data.photo) {
console.log('Photo data received:', data.photo.substring(0, 50) + '...');
}
if (data.nhso) {
console.log('NHSO Health Insurance Data:');
console.log('- Main Hospital:', data.nhso.mainHospitalName);
console.log('- Insurance Level:', data.nhso.mainInscl);
console.log('- Valid Until:', data.nhso.expireDate);
}
});
cardReader.init();
Available Events
device-connected | Card reader connected | When PC/SC detects a compatible reader |
card-inserted | Card physically inserted | When a Thai National ID card is inserted |
card-data | Data successfully extracted | After complete data reading process |
card-removed | Card physically removed | When card is removed (only if previously inserted) |
card-error | Error during operations | When card reading or communication fails |
card-incorrect | Invalid card type | When non-Thai ID card is inserted |
device-disconnected | Card reader disconnected | When reader is unplugged or becomes unavailable |
Module Formats
This library supports both CommonJS and ESM module formats, ensuring compatibility with different Node.js environments and bundlers.
CommonJS (require)
const { ThaiIdCardReader, ErrorCodes } = require('thai-national-id-card-reader');
const ThaiIdCardReader = require('thai-national-id-card-reader').default;
ESM (import)
import { ThaiIdCardReader, ErrorCodes } from 'thai-national-id-card-reader';
import ThaiIdCardReader from 'thai-national-id-card-reader';
TypeScript
import { ThaiIdCardReader, ThaiIdCardData, CardDataEvent } from 'thai-national-id-card-reader';
import ThaiIdCardReader = require('thai-national-id-card-reader');
Build Outputs
When you install the package, you get:
dist/index.cjs - CommonJS build with proper exports object
dist/index.js - ESM build with standard ES module exports
dist/index.d.ts - TypeScript type definitions for both formats
The package.json uses conditional exports to automatically serve the correct format:
{
"main": "dist/index.cjs",
"module": "dist/index.js",
"types": "dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js",
"require": "./dist/index.cjs",
"default": "./dist/index.js"
}
}
}
Card Data Structure
The library automatically extracts all available data from Thai National ID Cards:
interface ThaiIdCardData {
cid?: string;
name?: string;
nameEn?: string;
dob?: string;
gender?: string;
issuer?: string;
issueDate?: string;
expireDate?: string;
address?: string;
photo?: string;
nhso?: NhsoData;
laserId?: string;
}
interface NhsoData {
mainInscl?: string;
subInscl?: string;
mainHospitalName?: string;
subHospitalName?: string;
paidType?: string;
issueDate?: string;
expireDate?: string;
updateDate?: string;
changeHospitalAmount?: string;
}
Configuration Options
interface CardReaderOptions {
exitOnReadError?: boolean;
debug?: boolean;
}
Running the Example
For development and testing purposes, you can run the included example:
npm run build
npm run example
node example.js
Note: This is for testing the library during development. When using the library in your own project, import it as shown in the Quick Start section.
API Reference
ThaiIdCardReader Class
Constructor
new ThaiIdCardReader(options?: CardReaderOptions)
Methods
init(): void - Initialize card reader and start device detection
destroy(): void - Clean up resources and stop the reader
isReady(): boolean - Check if reader is initialized and ready
hasCardInserted(): boolean - Check if a card is currently inserted
Events
The reader extends EventEmitter with fully typed events. All events include:
status: number - HTTP-style status code
description: string - Human-readable description
data: object - Event-specific data payload
Advanced Usage
Error Handling
cardReader.on('card-error', (event) => {
console.error(`Error (${event.status}): ${event.description}`);
console.error('Details:', event.data.message);
switch (event.status) {
case 400:
console.log('Please insert a valid Thai National ID Card');
break;
case 404:
console.log('Card reader not found - check connections');
break;
case 500:
console.log('Card reading failed:', event.data.error);
break;
}
});
Promise-Based Usage
function readCardData() {
return new Promise((resolve, reject) => {
const reader = new ThaiIdCardReader({ exitOnReadError: false });
reader.once('card-data', (event) => {
reader.destroy();
resolve(event.data);
});
reader.once('card-error', (event) => {
reader.destroy();
reject(new Error(event.data.message));
});
reader.init();
});
}
readCardData()
.then(data => console.log('Card data:', data))
.catch(error => console.error('Failed to read card:', error));
Troubleshooting
Card Reader Not Detected
sudo systemctl status pcscd
pcsc_scan
lsusb
system_profiler SPUSBDataType
Build Issues
- Ensure you have the correct build tools for your platform
- Install Python 2.7.x for native module compilation
- On Windows, try
npm install --global --production windows-build-tools
Card Reading Errors
- Clean the card contacts with a soft, dry cloth
- Ensure the card is properly inserted (gold contacts down)
- Try with multiple cards to rule out card-specific issues
- Enable debug mode:
new ThaiIdCardReader({ debug: true })
Development
npm install
npm run dev
npm run lint
npm run lint:fix
npm run format
npm run typecheck
npm run build && npm run example
Architecture
The library uses a modular architecture with clear separation of concerns:
- ThaiIdCardReader - Main class handling device/card events and orchestration
- PersonalApplet - Handles extraction of personal information and photo data
- NhsoApplet - Handles health insurance data (placeholder implementation)
- CardReaderEventEmitter - Type-safe event system with full TypeScript support
- APDU Commands - Low-level smartcard communication protocols
Error Codes
The library uses HTTP-style status codes for consistent error handling:
200 - Success (data read successfully)
201 - Device activated (reader connected)
202 - Card inserted
205 - Card removed
400 - Incorrect card type
404 - Device not found
500 - Internal error (card read failure, communication error)
Contributing
- Fork the repository
- Create a feature branch:
git checkout -b feature/amazing-feature
- Make your changes with proper tests
- Ensure code quality:
npm run lint && npm run typecheck
- Test with actual hardware:
npm run build && npm run example
- Commit with clear messages:
git commit -m 'Add amazing feature'
- Push to branch:
git push origin feature/amazing-feature
- Open a Pull Request
License
MIT License - see LICENSE file for details.
Requirements
- Hardware: PC/SC compatible smartcard reader
- Cards: Thai National ID Cards (Government issued)
- Node.js: Version 14.x or higher
- Operating System: Windows 10+, macOS 10.14+, Ubuntu 18.04+
β οΈ Important: This library requires physical smartcard reader hardware and valid Thai National ID Cards to function. It is intended for legitimate applications that require identity verification or data extraction from Thai government-issued identification cards.