Skip to main content

Command Palette

Search for a command to run...

The Shipping Log, Day One: Why I Built Two npm Packages for Nigerian Developers

Updated
4 min read
The Shipping Log, Day One: Why I Built Two npm Packages for Nigerian Developers
O
Business & Full Stack Developer

I'm Marcus. I write code in Ibadan, Nigeria, and I taught myself most of what I know. No bootcamp. No computer science degree. I'm currently finishing a Business Management degree at Miva Open University while building production software on the side.

This blog is where I'll document the actual work: the bugs, the builds, the lessons, and the occasional mistakes that only show up after you've deployed something to production.

I'm calling it The Shipping Log.

This is day one.

The itch that started it

I kept rewriting the same Nigerian-specific logic across projects. Regex for MTN phone numbers. NUBAN validation copied from one codebase into the next. VAT math typed out by hand, again, for the third project this quarter.

Copy-pasting that logic stopped being a shortcut and started being a liability. One typo in a VAT calculation, repeated across three apps, is three bugs instead of one. So I pulled the logic out of my utils folders and packaged it properly. That's how I ended up publishing my first two npm packages: naija-validators and naija-tax-utils.

The problem with global libraries

JavaScript's validation libraries are built for global problems. They check email formats and generic credit card numbers well. They don't know what a BVN is. They don't catch a malformed NIN. They have no idea whether a 10-digit string is a valid NUBAN.

If you're building a fintech app or an e-commerce platform for the Nigerian market, you hit that gap fast. I built these packages so the next Nigerian developer doesn't have to write a BVN regex from scratch.

naija-validators

A lightweight, zero-dependency library for the data formats Nigerian developers handle daily: BVN, NIN, phone numbers (with carrier detection), and NUBAN bank accounts.

import { validateBVN, validatePhone } from "naija-validators";

const isValidBVN = validateBVN("12345678901");

const phoneData = validatePhone("08031234567");
console.log(phoneData.carrier); // "MTN"

naija-tax-utils

Tax math hardcoded into an app is a bug waiting to happen. This package handles standard Nigerian tax calculations and returns a full breakdown instead of a single number.

import { calculateVAT } from "naija-tax-utils";

const vat = calculateVAT(100000);

console.log(vat);
/*
{
  effectiveRate: 7.5,
  gross: 107500,
  net: 100000,
  taxAmount: 7500
}
*/

It currently handles standard VAT. PAYE calculation is built into the architecture and coming next.

Where to find them

GitHub:

npm:

What the build taught me

Writing the validation and tax logic took maybe a fifth of the total time. The rest went into making the packages production-ready, and that's where I learned the most.

TypeScript 5.5's strictness clashed with my local build setup more than once. I split the build process: tsup compiles the CJS and ESM output fast, while the native tsc compiler handles the declaration files. That split fixed a string of deprecation errors I couldn't resolve any other way.

I wrote Jest test suites for both packages. Watching the VAT math and carrier detection pass, repeatedly, gave me a kind of confidence that manual testing never does.

Then I wired up GitHub Actions so every push triggers an Ubuntu runner, installs dependencies, and runs the full test suite. Getting that green CI badge on the repo felt like a real milestone, not a vanity one. It means the next person who opens an issue against my code gets a project that already proves itself on every commit.

What's next

A calculatePayroll() function is next on the roadmap for naija-tax-utils. Plate number validation is on the list for naija-validators, along with a few more utilities Nigerian developers keep asking for.

If you're building for the Nigerian market, try the packages, break them, open an issue. That's the whole point of putting them out there.

The goal of this blog isn't to document perfect outcomes. It's to document the process of shipping things, learning from them, and getting a little better with every release.

This is day one. Let's see where the log takes us.