[{"data":1,"prerenderedAt":2107},["ShallowReactive",2],{"navigation":3,"article-\u002Farticles\u002Fbuilding-on-nigerias-postcode-api":150,"article-surround-\u002Farticles\u002Fbuilding-on-nigerias-postcode-api":1755,"tags-list":1760,"content-tags:articles":2038},[4],{"title":5,"path":6,"stem":7,"children":8,"page":149},"Articles","\u002Farticles","articles",[9,13,17,21,25,29,33,37,41,45,49,53,57,61,65,69,73,77,81,85,89,93,97,101,105,109,113,117,121,125,129,133,137,141,145],{"title":10,"path":11,"stem":12},"Building a Claude Code Plugin with Hooks","\u002Farticles\u002F2026-01-04-building-claude-code-plugin-with-hooks","articles\u002F2026-01-04-building-claude-code-plugin-with-hooks",{"title":14,"path":15,"stem":16},"Where should I validate? Everywhere, and for different reasons","\u002Farticles\u002F2026-04-18-validation-across-the-stack","articles\u002F2026-04-18-validation-across-the-stack",{"title":18,"path":19,"stem":20},"Error handling across the stack","\u002Farticles\u002F2026-04-23-error-handling-across-the-stack","articles\u002F2026-04-23-error-handling-across-the-stack",{"title":22,"path":23,"stem":24},"3 Ways to Run NestJS Cron Jobs When Running Multiple Instances","\u002Farticles\u002F3-ways-to-run-nestjs-cron-jobs-when-running-multiple-instances","articles\u002F3-ways-to-run-nestjs-cron-jobs-when-running-multiple-instances",{"title":26,"path":27,"stem":28},"Building a Custom Content Slider (Carousel) in Angular","\u002Farticles\u002Fbuilding-a-custom-content-slider-carousel-in-angular","articles\u002Fbuilding-a-custom-content-slider-carousel-in-angular",{"title":30,"path":31,"stem":32},"Building Jobven: Designing a Developer-Friendly API","\u002Farticles\u002Fbuilding-jobven-designing-a-developer-friendly-api","articles\u002Fbuilding-jobven-designing-a-developer-friendly-api",{"title":34,"path":35,"stem":36},"Building MapleStack: AWS S3 for Data Storage","\u002Farticles\u002Fbuilding-maplestack-aws-s3-for-data-storage","articles\u002Fbuilding-maplestack-aws-s3-for-data-storage",{"title":38,"path":39,"stem":40},"Building MapleStack: Enhancing Email Capabilities with Mailgun","\u002Farticles\u002Fbuilding-maplestack-enhancing-email-capabilities-with-mailgun","articles\u002Fbuilding-maplestack-enhancing-email-capabilities-with-mailgun",{"title":42,"path":43,"stem":44},"Building MapleStack: NestJS for Server-Side Operations","\u002Farticles\u002Fbuilding-maplestack-nestjs-for-server-side-operations","articles\u002Fbuilding-maplestack-nestjs-for-server-side-operations",{"title":46,"path":47,"stem":48},"Building MapleStack: PostgreSQL for Data Storage","\u002Farticles\u002Fbuilding-maplestack-postgresql-for-data-storage","articles\u002Fbuilding-maplestack-postgresql-for-data-storage",{"title":50,"path":51,"stem":52},"Building MapleStack: React for an interactive user interface","\u002Farticles\u002Fbuilding-maplestack-react-for-an-interactive-user-interface","articles\u002Fbuilding-maplestack-react-for-an-interactive-user-interface",{"title":54,"path":55,"stem":56},"Building MapleStack: Securing Connections with Let's Encrypt","\u002Farticles\u002Fbuilding-maplestack-securing-connections-with-lets-encrypt","articles\u002Fbuilding-maplestack-securing-connections-with-lets-encrypt",{"title":58,"path":59,"stem":60},"Building MapleStack: Simplifying User Authorization with AWS Cognito","\u002Farticles\u002Fbuilding-maplestack-simplifying-user-authorization-with-aws-cognito","articles\u002Fbuilding-maplestack-simplifying-user-authorization-with-aws-cognito",{"title":62,"path":63,"stem":64},"Building MapleStack: Tailwind CSS for Streamlined Styling","\u002Farticles\u002Fbuilding-maplestack-tailwind-css-for-streamlined-styling","articles\u002Fbuilding-maplestack-tailwind-css-for-streamlined-styling",{"title":66,"path":67,"stem":68},"Nigeria's Postcode API: How It Works and How to Test It","\u002Farticles\u002Fbuilding-on-nigerias-postcode-api","articles\u002Fbuilding-on-nigerias-postcode-api",{"title":70,"path":71,"stem":72},"Debugging Multiple NestJS Applications in VSCode","\u002Farticles\u002Fdebugging-multiple-nestjs-applications-in-vscode","articles\u002Fdebugging-multiple-nestjs-applications-in-vscode",{"title":74,"path":75,"stem":76},"Enums as arrays in PostgreSQL - Updated 2024","\u002Farticles\u002Fenums-as-arrays-in-postgresql","articles\u002Fenums-as-arrays-in-postgresql",{"title":78,"path":79,"stem":80},"FindChildcare.ca","\u002Farticles\u002Ffindchildcare-ca","articles\u002Ffindchildcare-ca",{"title":82,"path":83,"stem":84},"GraphQL Server - Apollo, KoaJS and Typescript implementation","\u002Farticles\u002Fgraphql-server-apollo-koajs-and-typescript-implementation","articles\u002Fgraphql-server-apollo-koajs-and-typescript-implementation",{"title":86,"path":87,"stem":88},"How I built MapleStack's waitlist with AI: ChatGPT vs Bard","\u002Farticles\u002Fhow-i-built-maplestacks-waitlist-with-ai-chatgpt-vs-bard","articles\u002Fhow-i-built-maplestacks-waitlist-with-ai-chatgpt-vs-bard",{"title":90,"path":91,"stem":92},"How to Integrate Google reCAPTCHA v3 with NestJS in 3 Easy Steps","\u002Farticles\u002Fhow-to-integrate-google-recaptcha-v3-with-nestjs-in-3-easy-steps","articles\u002Fhow-to-integrate-google-recaptcha-v3-with-nestjs-in-3-easy-steps",{"title":94,"path":95,"stem":96},"How to resolve a blacklisted domain","\u002Farticles\u002Fhow-to-resolve-a-blacklisted-domain","articles\u002Fhow-to-resolve-a-blacklisted-domain",{"title":98,"path":99,"stem":100},"Implementing WordPress-like Tags in Nuxt Content","\u002Farticles\u002Fimplementing-wordpress-like-tags-in-nuxt","articles\u002Fimplementing-wordpress-like-tags-in-nuxt",{"title":102,"path":103,"stem":104},"Jobven","\u002Farticles\u002Fjobven","articles\u002Fjobven",{"title":106,"path":107,"stem":108},"KidzLog","\u002Farticles\u002Fkidzlog","articles\u002Fkidzlog",{"title":110,"path":111,"stem":112},"MapleStack","\u002Farticles\u002Fmaplestack","articles\u002Fmaplestack",{"title":114,"path":115,"stem":116},"Migrating from WordPress to Nuxt","\u002Farticles\u002Fmigrating-from-wordpress-to-nuxt","articles\u002Fmigrating-from-wordpress-to-nuxt",{"title":118,"path":119,"stem":120},"My Initial Server Setup Checklist","\u002Farticles\u002Fmy-initial-server-setup-checklist","articles\u002Fmy-initial-server-setup-checklist",{"title":122,"path":123,"stem":124},"Pre-authorized transaction on the Stellar network","\u002Farticles\u002Fpre-authorized-transaction-on-the-stellar-network","articles\u002Fpre-authorized-transaction-on-the-stellar-network",{"title":126,"path":127,"stem":128},"Saza: Open Source mobile and desktop Stellar wallet release.","\u002Farticles\u002Fsaza-open-source-mobile-and-desktop-stellar-wallet-release","articles\u002Fsaza-open-source-mobile-and-desktop-stellar-wallet-release",{"title":130,"path":131,"stem":132},"SpellCheckMySite.com","\u002Farticles\u002Fspellcheckmysite-com","articles\u002Fspellcheckmysite-com",{"title":134,"path":135,"stem":136},"Using Check Constraints in PostgreSQL for Value Validation","\u002Farticles\u002Fusing-check-constraints-in-postgresql-for-value-validation","articles\u002Fusing-check-constraints-in-postgresql-for-value-validation",{"title":138,"path":139,"stem":140},"Using DataLoader in GraphQL","\u002Farticles\u002Fusing-dataloader-in-graphql","articles\u002Fusing-dataloader-in-graphql",{"title":142,"path":143,"stem":144},"Using Typescript in NodeJS development","\u002Farticles\u002Fusing-typescript-in-nodejs-development","articles\u002Fusing-typescript-in-nodejs-development",{"title":146,"path":147,"stem":148},"Welcome!","\u002Farticles\u002Fwelcome","articles\u002Fwelcome",false,{"id":151,"title":66,"author":152,"body":156,"categories":1738,"date":1739,"description":1740,"extension":1741,"head":1742,"image":1743,"meta":1744,"minRead":768,"navigation":642,"ogImage":1742,"path":67,"robots":1742,"schemaOrg":1742,"seo":1746,"sitemap":1747,"stem":68,"tags":1748,"__hash__":1754},"articles\u002Farticles\u002Fbuilding-on-nigerias-postcode-api.md",{"name":153,"avatar":154},"Peter Oliha",{"src":155,"alt":153},"\u002Fassets\u002Fimages\u002Fauthors\u002Fpeter-oliha.jpg",{"type":157,"value":158,"toc":1724},"minimark",[159,168,171,182,187,198,256,267,278,282,285,291,294,299,310,315,318,322,329,426,451,457,461,468,540,543,546,550,556,581,840,843,847,850,856,1079,1091,1201,1205,1208,1421,1431,1435,1444,1562,1573,1577,1580,1586,1606,1618,1622,1631,1643,1646,1654,1662,1664,1672,1677,1681,1707,1711,1714,1717,1720],[160,161,162,163,167],"p",{},"On 1 October 2026, Nigeria launched its National Digital Alphanumeric Postcode System, with an API for developers to build on. I wanted to see what it returns before committing to anything, so I built an open source mock of the API with the same paths and response shapes, and an npm package, ",[164,165,166],"code",{},"ng-postcode",", that handles the postcode format offline.",[160,169,170],{},"In this article I'll walk through how the postcodes and the API work, and how to build and test an integration using NIPOST's sandbox and the mock together.",[160,172,173,174,181],{},"This isn't a guide to the official onboarding. For that, the ",[175,176,180],"a",{"href":177,"rel":178},"https:\u002F\u002Fdocs.postcode.gov.ng",[179],"nofollow","NIPOST docs"," are the source.",[183,184,186],"h2",{"id":185},"getting-access","Getting access",[160,188,189,190,193,194,197],{},"Every endpoint except ",[164,191,192],{},"\u002Fhealthz"," needs an API key, sent in the ",[164,195,196],{},"X-API-Key"," header. \"Free\" in the docs means a call consumes no credits. NIPOST issues two kinds of secret key:",[199,200,201,220],"table",{},[202,203,204],"thead",{},[205,206,207,211,214,217],"tr",{},[208,209,210],"th",{},"Key",[208,212,213],{},"Prefix",[208,215,216],{},"What it reads",[208,218,219],{},"Needs",[221,222,223,240],"tbody",{},[205,224,225,229,234,237],{},[226,227,228],"td",{},"Test",[226,230,231],{},[164,232,233],{},"nipost_test_",[226,235,236],{},"A sandbox of public places, free and unmetered at every lookup level",[226,238,239],{},"A developer account; one test key per organisation",[205,241,242,245,250,253],{},[226,243,244],{},"Live",[226,246,247],{},[164,248,249],{},"nipost_live_",[226,251,252],{},"The live postcode database, with credits for Lookup L2 and above",[226,254,255],{},"Verified KYB and a granted access level",[160,257,258,259,262,263,266],{},"There are also publishable keys (",[164,260,261],{},"nipost_pk_test_"," and ",[164,264,265],{},"nipost_pk_live_",") for browser and mobile code. They are capped at L3 and locked to the origins you allow.",[160,268,269,270,273,274,277],{},"The two datasets don't overlap. The sandbox codes NIPOST publishes, such as ",[164,271,272],{},"FC-01-A01-KP-27",", only resolve under a test key, and the 20 sample postcodes on the Lookup levels page, such as ",[164,275,276],{},"LA-11-W06-TC-10",", only resolve under a live key.",[183,279,281],{"id":280},"where-the-mock-fits","Where the mock fits",[160,283,284],{},"The sandbox is the right place to check your integration against NIPOST's own gateway. The mock covers the parts it doesn't:",[160,286,287],{},[288,289,290],"strong",{},"1. No account at all",[160,292,293],{},"The hosted mock answers with no sign-up and no key, so you can open a lookup in a browser and see the response before deciding anything.",[160,295,296],{},[288,297,298],{},"2. Errors on demand",[160,300,301,302,305,306,309],{},"Error paths like ",[164,303,304],{},"402 insufficient_credits"," or ",[164,307,308],{},"403 level_not_granted"," are hard to trigger on purpose against a real account, and they're exactly what your code needs to handle. The mock returns them when you send a reserved key.",[160,311,312],{},[288,313,314],{},"3. Tests without a network",[160,316,317],{},"Your test suite shouldn't depend on a remote service or a key in CI. The package ships request handlers that answer calls to the real gateway URL inside your tests.",[183,319,321],{"id":320},"how-a-nigerian-postcode-works","How a Nigerian postcode works",[160,323,324,325,328],{},"A postcode has five segments, written ",[164,326,327],{},"AA-99-H77-BB-55",":",[199,330,331,347],{},[202,332,333],{},[205,334,335,338,341,344],{},[208,336,337],{},"Segment",[208,339,340],{},"Example",[208,342,343],{},"Meaning",[208,345,346],{},"Format",[221,348,349,365,381,397,412],{},[205,350,351,354,359,362],{},[226,352,353],{},"State",[226,355,356],{},[164,357,358],{},"EK",[226,360,361],{},"Ekiti",[226,363,364],{},"2 letters",[205,366,367,370,375,378],{},[226,368,369],{},"LGA",[226,371,372],{},[164,373,374],{},"01",[226,376,377],{},"LGA within the state",[226,379,380],{},"2 digits, 01 to 99",[205,382,383,386,391,394],{},[226,384,385],{},"District",[226,387,388],{},[164,389,390],{},"A03",[226,392,393],{},"District within the LGA",[226,395,396],{},"3 letters or digits",[205,398,399,402,407,410],{},[226,400,401],{},"Area",[226,403,404],{},[164,405,406],{},"FK",[226,408,409],{},"Area within the district",[226,411,364],{},[205,413,414,417,421,424],{},[226,415,416],{},"Unit",[226,418,419],{},[164,420,374],{},[226,422,423],{},"Building unit within the area",[226,425,380],{},[160,427,428,429,432,433,305,436,439,440,443,444,446,447,450],{},"The same code can be written three ways, ",[164,430,431],{},"EK-01-A03-FK-01",", ",[164,434,435],{},"EK 01 A03 FK 01",[164,437,438],{},"EK01A03FK01",", and the API accepts any of them, in any case. Numeric segments are zero-filled, so an LGA of ",[164,441,442],{},"1"," becomes ",[164,445,374],{},", and ",[164,448,449],{},"00"," is never valid.",[160,452,453,454,456],{},"One small thing to know: the docs' format page describes the code as 12 characters, while NIPOST's own site and FAQ say 11, which is what the segments add up to (",[164,455,438],{},"). Validate on the segments.",[183,458,460],{"id":459},"lookup-levels","Lookup levels",[160,462,463,464,467],{},"A lookup takes a ",[164,465,466],{},"level"," parameter, and each level adds fields on top of the one below:",[199,469,470,483],{},[202,471,472],{},[205,473,474,477,480],{},[208,475,476],{},"Level",[208,478,479],{},"Adds",[208,481,482],{},"Cost",[221,484,485,498,509,519,529],{},[205,486,487,490,495],{},[226,488,489],{},"L1",[226,491,492],{},[164,493,494],{},"valid",[226,496,497],{},"Free",[205,499,500,503,506],{},[226,501,502],{},"L2",[226,504,505],{},"Administrative address and recent house address",[226,507,508],{},"Credits",[205,510,511,514,517],{},[226,512,513],{},"L3",[226,515,516],{},"Building use",[226,518,508],{},[205,520,521,524,527],{},[226,522,523],{},"L4",[226,525,526],{},"Other building info",[226,528,508],{},[205,530,531,534,537],{},[226,532,533],{},"L5",[226,535,536],{},"Point geometry",[226,538,539],{},"Restricted",[160,541,542],{},"Your organisation is granted a maximum level, and the response is capped at it. Test keys read every level without spending credits.",[160,544,545],{},"NIPOST also has free reference endpoints that list states, the LGAs in a state, and the district and area codes below them, which are handy for building address pickers.",[183,547,549],{"id":548},"step-1-validate-and-format-postcodes-offline","Step 1: Validate and format postcodes offline",[160,551,552,553,555],{},"Most of the format rules don't need the network at all. The ",[164,554,166],{}," package implements them with no runtime dependencies:",[557,558,563],"pre",{"className":559,"code":560,"language":561,"meta":562,"style":562},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","npm install ng-postcode\n","bash","",[164,564,565],{"__ignoreMap":562},[566,567,570,574,578],"span",{"class":568,"line":569},"line",1,[566,571,573],{"class":572},"sBMFI","npm",[566,575,577],{"class":576},"sfazB"," install",[566,579,580],{"class":576}," ng-postcode\n",[557,582,586],{"className":583,"code":584,"language":585,"meta":562,"style":562},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import { assemble, disassemble, format, isValidFormat, parse } from 'ng-postcode'\n\nformat('ek01a03fk01')\n\u002F\u002F { postcode: 'EK-01-A03-FK-01', display: 'EK 01 A03 FK 01', compact: 'EK01A03FK01' }\n\nassemble({ state: 'ek', lga: 1, district: 'a03', area: 'fk', unit: 1 }).postcode\n\u002F\u002F 'EK-01-A03-FK-01'\n\ndisassemble('EK 01 A03 FK 01')\n\u002F\u002F { state: 'EK', lga: '01', district: 'A03', area: 'FK', unit: '01' }\n\nisValidFormat('EK-00-A03-FK-01') \u002F\u002F false: numeric segments run 01 to 99\nparse('not a postcode')          \u002F\u002F null\n","ts",[164,587,588,637,644,665,672,677,760,766,771,787,793,798,819],{"__ignoreMap":562},[566,589,590,594,598,602,605,608,610,613,615,618,620,623,626,629,632,634],{"class":568,"line":569},[566,591,593],{"class":592},"s7zQu","import",[566,595,597],{"class":596},"sMK4o"," {",[566,599,601],{"class":600},"sTEyZ"," assemble",[566,603,604],{"class":596},",",[566,606,607],{"class":600}," disassemble",[566,609,604],{"class":596},[566,611,612],{"class":600}," format",[566,614,604],{"class":596},[566,616,617],{"class":600}," isValidFormat",[566,619,604],{"class":596},[566,621,622],{"class":600}," parse",[566,624,625],{"class":596}," }",[566,627,628],{"class":592}," from",[566,630,631],{"class":596}," '",[566,633,166],{"class":576},[566,635,636],{"class":596},"'\n",[566,638,640],{"class":568,"line":639},2,[566,641,643],{"emptyLinePlaceholder":642},true,"\n",[566,645,647,651,654,657,660,662],{"class":568,"line":646},3,[566,648,650],{"class":649},"s2Zo4","format",[566,652,653],{"class":600},"(",[566,655,656],{"class":596},"'",[566,658,659],{"class":576},"ek01a03fk01",[566,661,656],{"class":596},[566,663,664],{"class":600},")\n",[566,666,668],{"class":568,"line":667},4,[566,669,671],{"class":670},"sHwdD","\u002F\u002F { postcode: 'EK-01-A03-FK-01', display: 'EK 01 A03 FK 01', compact: 'EK01A03FK01' }\n",[566,673,675],{"class":568,"line":674},5,[566,676,643],{"emptyLinePlaceholder":642},[566,678,680,683,685,688,692,694,696,699,701,703,706,708,712,714,717,719,721,724,726,728,731,733,735,738,740,742,745,747,749,751,754,757],{"class":568,"line":679},6,[566,681,682],{"class":649},"assemble",[566,684,653],{"class":600},[566,686,687],{"class":596},"{",[566,689,691],{"class":690},"swJcz"," state",[566,693,328],{"class":596},[566,695,631],{"class":596},[566,697,698],{"class":576},"ek",[566,700,656],{"class":596},[566,702,604],{"class":596},[566,704,705],{"class":690}," lga",[566,707,328],{"class":596},[566,709,711],{"class":710},"sbssI"," 1",[566,713,604],{"class":596},[566,715,716],{"class":690}," district",[566,718,328],{"class":596},[566,720,631],{"class":596},[566,722,723],{"class":576},"a03",[566,725,656],{"class":596},[566,727,604],{"class":596},[566,729,730],{"class":690}," area",[566,732,328],{"class":596},[566,734,631],{"class":596},[566,736,737],{"class":576},"fk",[566,739,656],{"class":596},[566,741,604],{"class":596},[566,743,744],{"class":690}," unit",[566,746,328],{"class":596},[566,748,711],{"class":710},[566,750,625],{"class":596},[566,752,753],{"class":600},")",[566,755,756],{"class":596},".",[566,758,759],{"class":600},"postcode\n",[566,761,763],{"class":568,"line":762},7,[566,764,765],{"class":670},"\u002F\u002F 'EK-01-A03-FK-01'\n",[566,767,769],{"class":568,"line":768},8,[566,770,643],{"emptyLinePlaceholder":642},[566,772,774,777,779,781,783,785],{"class":568,"line":773},9,[566,775,776],{"class":649},"disassemble",[566,778,653],{"class":600},[566,780,656],{"class":596},[566,782,435],{"class":576},[566,784,656],{"class":596},[566,786,664],{"class":600},[566,788,790],{"class":568,"line":789},10,[566,791,792],{"class":670},"\u002F\u002F { state: 'EK', lga: '01', district: 'A03', area: 'FK', unit: '01' }\n",[566,794,796],{"class":568,"line":795},11,[566,797,643],{"emptyLinePlaceholder":642},[566,799,801,804,806,808,811,813,816],{"class":568,"line":800},12,[566,802,803],{"class":649},"isValidFormat",[566,805,653],{"class":600},[566,807,656],{"class":596},[566,809,810],{"class":576},"EK-00-A03-FK-01",[566,812,656],{"class":596},[566,814,815],{"class":600},") ",[566,817,818],{"class":670},"\u002F\u002F false: numeric segments run 01 to 99\n",[566,820,822,825,827,829,832,834,837],{"class":568,"line":821},13,[566,823,824],{"class":649},"parse",[566,826,653],{"class":600},[566,828,656],{"class":596},[566,830,831],{"class":576},"not a postcode",[566,833,656],{"class":596},[566,835,836],{"class":600},")          ",[566,838,839],{"class":670},"\u002F\u002F null\n",[160,841,842],{},"A well-formed postcode isn't necessarily one that exists. Only the API can tell you that, but checking the format first saves a call, and a credit, on input that could never match.",[183,844,846],{"id":845},"step-2-try-the-api-in-a-browser","Step 2: Try the API in a browser",[160,848,849],{},"The hosted mock answers with no account and no key. Open this in a browser:",[160,851,852],{},[175,853,854],{"href":854,"rel":855},"https:\u002F\u002Fng-postcode.oliha.dev\u002Fv1\u002Flookup?code=LA-11-W06-TC-10&level=3",[179],[557,857,861],{"className":858,"code":859,"language":860,"meta":562,"style":562},"language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","{\n  \"data\": {\n    \"postcode\": \"LA-11-W06-TC-10\",\n    \"valid\": true,\n    \"administrative_address\": { \"state_name\": \"LAGOS\", \"lga_name\": \"MOCK LGA 11\", \"locality_name\": \"MOCK LGA 11\", \"zone\": \"SOUTH WEST\" },\n    \"recent_house_address\": { \"recent\": \"10 MOCK STREET, AREA TC, LAGOS\" },\n    \"building_use_status\": \"commercial\"\n  },\n  \"mock\": true\n}\n","json",[164,862,863,868,885,907,920,1005,1036,1055,1060,1074],{"__ignoreMap":562},[566,864,865],{"class":568,"line":569},[566,866,867],{"class":596},"{\n",[566,869,870,873,877,880,882],{"class":568,"line":639},[566,871,872],{"class":596},"  \"",[566,874,876],{"class":875},"spNyl","data",[566,878,879],{"class":596},"\"",[566,881,328],{"class":596},[566,883,884],{"class":596}," {\n",[566,886,887,890,893,895,897,900,902,904],{"class":568,"line":646},[566,888,889],{"class":596},"    \"",[566,891,892],{"class":572},"postcode",[566,894,879],{"class":596},[566,896,328],{"class":596},[566,898,899],{"class":596}," \"",[566,901,276],{"class":576},[566,903,879],{"class":596},[566,905,906],{"class":596},",\n",[566,908,909,911,913,915,917],{"class":568,"line":667},[566,910,889],{"class":596},[566,912,494],{"class":572},[566,914,879],{"class":596},[566,916,328],{"class":596},[566,918,919],{"class":596}," true,\n",[566,921,922,924,927,929,931,933,935,938,940,942,944,947,949,951,953,956,958,960,962,965,967,969,971,974,976,978,980,982,984,986,988,991,993,995,997,1000,1002],{"class":568,"line":674},[566,923,889],{"class":596},[566,925,926],{"class":572},"administrative_address",[566,928,879],{"class":596},[566,930,328],{"class":596},[566,932,597],{"class":596},[566,934,899],{"class":596},[566,936,937],{"class":710},"state_name",[566,939,879],{"class":596},[566,941,328],{"class":596},[566,943,899],{"class":596},[566,945,946],{"class":576},"LAGOS",[566,948,879],{"class":596},[566,950,604],{"class":596},[566,952,899],{"class":596},[566,954,955],{"class":710},"lga_name",[566,957,879],{"class":596},[566,959,328],{"class":596},[566,961,899],{"class":596},[566,963,964],{"class":576},"MOCK LGA 11",[566,966,879],{"class":596},[566,968,604],{"class":596},[566,970,899],{"class":596},[566,972,973],{"class":710},"locality_name",[566,975,879],{"class":596},[566,977,328],{"class":596},[566,979,899],{"class":596},[566,981,964],{"class":576},[566,983,879],{"class":596},[566,985,604],{"class":596},[566,987,899],{"class":596},[566,989,990],{"class":710},"zone",[566,992,879],{"class":596},[566,994,328],{"class":596},[566,996,899],{"class":596},[566,998,999],{"class":576},"SOUTH WEST",[566,1001,879],{"class":596},[566,1003,1004],{"class":596}," },\n",[566,1006,1007,1009,1012,1014,1016,1018,1020,1023,1025,1027,1029,1032,1034],{"class":568,"line":679},[566,1008,889],{"class":596},[566,1010,1011],{"class":572},"recent_house_address",[566,1013,879],{"class":596},[566,1015,328],{"class":596},[566,1017,597],{"class":596},[566,1019,899],{"class":596},[566,1021,1022],{"class":710},"recent",[566,1024,879],{"class":596},[566,1026,328],{"class":596},[566,1028,899],{"class":596},[566,1030,1031],{"class":576},"10 MOCK STREET, AREA TC, LAGOS",[566,1033,879],{"class":596},[566,1035,1004],{"class":596},[566,1037,1038,1040,1043,1045,1047,1049,1052],{"class":568,"line":762},[566,1039,889],{"class":596},[566,1041,1042],{"class":572},"building_use_status",[566,1044,879],{"class":596},[566,1046,328],{"class":596},[566,1048,899],{"class":596},[566,1050,1051],{"class":576},"commercial",[566,1053,1054],{"class":596},"\"\n",[566,1056,1057],{"class":568,"line":768},[566,1058,1059],{"class":596},"  },\n",[566,1061,1062,1064,1067,1069,1071],{"class":568,"line":773},[566,1063,872],{"class":596},[566,1065,1066],{"class":875},"mock",[566,1068,879],{"class":596},[566,1070,328],{"class":596},[566,1072,1073],{"class":596}," true\n",[566,1075,1076],{"class":568,"line":789},[566,1077,1078],{"class":596},"}\n",[160,1080,1081,1082,1087,1088,1090],{},"The root, ",[175,1083,1086],{"href":1084,"rel":1085},"https:\u002F\u002Fng-postcode.oliha.dev",[179],"ng-postcode.oliha.dev",", is a Swagger UI page where you can call every endpoint from the browser. Each endpoint has an ",[164,1089,196],{}," dropdown. Leave it empty for full access, or pick a reserved key to see an error:",[199,1092,1093,1102],{},[202,1094,1095],{},[205,1096,1097,1099],{},[208,1098,210],{},[208,1100,1101],{},"Behaviour",[221,1103,1104,1118,1130,1141,1157,1169,1182,1192],{},[205,1105,1106,1115],{},[226,1107,1108,1111,1112],{},[164,1109,1110],{},"mock_level_1"," to ",[164,1113,1114],{},"mock_level_5",[226,1116,1117],{},"Lookups capped at that level",[205,1119,1120,1125],{},[226,1121,1122],{},[164,1123,1124],{},"mock_no_credits",[226,1126,1127,1129],{},[164,1128,304],{}," on Lookup L2+",[205,1131,1132,1137],{},[226,1133,1134],{},[164,1135,1136],{},"mock_no_scope",[226,1138,1139,1129],{},[164,1140,308],{},[205,1142,1143,1148],{},[226,1144,1145],{},[164,1146,1147],{},"mock_rate_limited",[226,1149,1150,1153,1154],{},[164,1151,1152],{},"429 rate_limited",", with ",[164,1155,1156],{},"Retry-After: 60",[205,1158,1159,1164],{},[226,1160,1161],{},[164,1162,1163],{},"mock_invalid",[226,1165,1166],{},[164,1167,1168],{},"401 invalid_api_key",[205,1170,1171,1176],{},[226,1172,1173],{},[164,1174,1175],{},"mock_no_key",[226,1177,1178,1181],{},[164,1179,1180],{},"401 auth_required",", as NIPOST's gateway answers any call without a key",[205,1183,1184,1189],{},[226,1185,1186,1187],{},"Any key starting ",[164,1188,233],{},[226,1190,1191],{},"Sandbox behaviour: only NIPOST's sandbox postcodes resolve",[205,1193,1194,1198],{},[226,1195,1186,1196],{},[164,1197,249],{},[226,1199,1200],{},"Live behaviour: the sandbox postcodes don't resolve",[183,1202,1204],{"id":1203},"step-3-write-your-integration-against-the-mock","Step 3: Write your integration against the mock",[160,1206,1207],{},"The package includes a small typed client. Point it at the mock now, and at the real gateway later:",[557,1209,1211],{"className":583,"code":1210,"language":585,"meta":562,"style":562},"import { createPostcodeClient, PostcodeApiError } from 'ng-postcode'\n\nconst api = createPostcodeClient({ baseUrl: 'https:\u002F\u002Fng-postcode.oliha.dev' })\n\u002F\u002F Later: createPostcodeClient({ apiKey: process.env.NIPOST_API_KEY })\n\ntry {\n  const result = await api.lookup('LA-11-W06-TC-10', 2)\n  console.log(result.administrative_address?.state_name)\n}\ncatch (err) {\n  if (err instanceof PostcodeApiError && err.code === 'insufficient_credits') {\n    \u002F\u002F Fall back to an L1 check, or ask the user to try later\n  }\n}\n",[164,1212,1213,1237,1241,1273,1278,1282,1289,1326,1352,1356,1366,1406,1411,1416],{"__ignoreMap":562},[566,1214,1215,1217,1219,1222,1224,1227,1229,1231,1233,1235],{"class":568,"line":569},[566,1216,593],{"class":592},[566,1218,597],{"class":596},[566,1220,1221],{"class":600}," createPostcodeClient",[566,1223,604],{"class":596},[566,1225,1226],{"class":600}," PostcodeApiError",[566,1228,625],{"class":596},[566,1230,628],{"class":592},[566,1232,631],{"class":596},[566,1234,166],{"class":576},[566,1236,636],{"class":596},[566,1238,1239],{"class":568,"line":639},[566,1240,643],{"emptyLinePlaceholder":642},[566,1242,1243,1246,1249,1252,1254,1256,1258,1261,1263,1265,1267,1269,1271],{"class":568,"line":646},[566,1244,1245],{"class":875},"const",[566,1247,1248],{"class":600}," api ",[566,1250,1251],{"class":596},"=",[566,1253,1221],{"class":649},[566,1255,653],{"class":600},[566,1257,687],{"class":596},[566,1259,1260],{"class":690}," baseUrl",[566,1262,328],{"class":596},[566,1264,631],{"class":596},[566,1266,1084],{"class":576},[566,1268,656],{"class":596},[566,1270,625],{"class":596},[566,1272,664],{"class":600},[566,1274,1275],{"class":568,"line":667},[566,1276,1277],{"class":670},"\u002F\u002F Later: createPostcodeClient({ apiKey: process.env.NIPOST_API_KEY })\n",[566,1279,1280],{"class":568,"line":674},[566,1281,643],{"emptyLinePlaceholder":642},[566,1283,1284,1287],{"class":568,"line":679},[566,1285,1286],{"class":592},"try",[566,1288,884],{"class":596},[566,1290,1291,1294,1297,1300,1303,1306,1308,1311,1313,1315,1317,1319,1321,1324],{"class":568,"line":762},[566,1292,1293],{"class":875},"  const",[566,1295,1296],{"class":600}," result",[566,1298,1299],{"class":596}," =",[566,1301,1302],{"class":592}," await",[566,1304,1305],{"class":600}," api",[566,1307,756],{"class":596},[566,1309,1310],{"class":649},"lookup",[566,1312,653],{"class":690},[566,1314,656],{"class":596},[566,1316,276],{"class":576},[566,1318,656],{"class":596},[566,1320,604],{"class":596},[566,1322,1323],{"class":710}," 2",[566,1325,664],{"class":690},[566,1327,1328,1331,1333,1336,1338,1341,1343,1345,1348,1350],{"class":568,"line":768},[566,1329,1330],{"class":600},"  console",[566,1332,756],{"class":596},[566,1334,1335],{"class":649},"log",[566,1337,653],{"class":690},[566,1339,1340],{"class":600},"result",[566,1342,756],{"class":596},[566,1344,926],{"class":600},[566,1346,1347],{"class":596},"?.",[566,1349,937],{"class":600},[566,1351,664],{"class":690},[566,1353,1354],{"class":568,"line":773},[566,1355,1078],{"class":596},[566,1357,1358,1361,1364],{"class":568,"line":789},[566,1359,1360],{"class":592},"catch",[566,1362,1363],{"class":600}," (err) ",[566,1365,867],{"class":596},[566,1367,1368,1371,1374,1377,1380,1382,1385,1388,1390,1392,1395,1397,1400,1402,1404],{"class":568,"line":795},[566,1369,1370],{"class":592},"  if",[566,1372,1373],{"class":690}," (",[566,1375,1376],{"class":600},"err",[566,1378,1379],{"class":596}," instanceof",[566,1381,1226],{"class":572},[566,1383,1384],{"class":596}," &&",[566,1386,1387],{"class":600}," err",[566,1389,756],{"class":596},[566,1391,164],{"class":600},[566,1393,1394],{"class":596}," ===",[566,1396,631],{"class":596},[566,1398,1399],{"class":576},"insufficient_credits",[566,1401,656],{"class":596},[566,1403,815],{"class":690},[566,1405,867],{"class":596},[566,1407,1408],{"class":568,"line":800},[566,1409,1410],{"class":670},"    \u002F\u002F Fall back to an L1 check, or ask the user to try later\n",[566,1412,1413],{"class":568,"line":821},[566,1414,1415],{"class":596},"  }\n",[566,1417,1419],{"class":568,"line":1418},14,[566,1420,1078],{"class":596},[160,1422,1423,1424,1427,1428,1430],{},"Switching to the real API means removing ",[164,1425,1426],{},"baseUrl"," and passing your key, a test key against the sandbox first and a live key once you're verified. The client sends it as ",[164,1429,196],{},", the same header the real gateway reads. Keep secret keys on the server; NIPOST issues separate publishable keys for browser and mobile use.",[183,1432,1434],{"id":1433},"step-4-mock-the-api-in-your-tests","Step 4: Mock the API in your tests",[160,1436,1437,1438,1443],{},"For tests, you don't want a network call at all. The package ships ",[175,1439,1442],{"href":1440,"rel":1441},"https:\u002F\u002Fmswjs.io",[179],"MSW"," handlers that answer requests to the real gateway URL:",[557,1445,1447],{"className":583,"code":1446,"language":585,"meta":562,"style":562},"import { setupServer } from 'msw\u002Fnode'\nimport { postcodeHandlers } from 'ng-postcode\u002Fmsw'\n\nconst server = setupServer(...postcodeHandlers())\n\nbeforeAll(() => server.listen())\nafterAll(() => server.close())\n",[164,1448,1449,1469,1489,1493,1515,1519,1542],{"__ignoreMap":562},[566,1450,1451,1453,1455,1458,1460,1462,1464,1467],{"class":568,"line":569},[566,1452,593],{"class":592},[566,1454,597],{"class":596},[566,1456,1457],{"class":600}," setupServer",[566,1459,625],{"class":596},[566,1461,628],{"class":592},[566,1463,631],{"class":596},[566,1465,1466],{"class":576},"msw\u002Fnode",[566,1468,636],{"class":596},[566,1470,1471,1473,1475,1478,1480,1482,1484,1487],{"class":568,"line":639},[566,1472,593],{"class":592},[566,1474,597],{"class":596},[566,1476,1477],{"class":600}," postcodeHandlers",[566,1479,625],{"class":596},[566,1481,628],{"class":592},[566,1483,631],{"class":596},[566,1485,1486],{"class":576},"ng-postcode\u002Fmsw",[566,1488,636],{"class":596},[566,1490,1491],{"class":568,"line":646},[566,1492,643],{"emptyLinePlaceholder":642},[566,1494,1495,1497,1500,1502,1504,1506,1509,1512],{"class":568,"line":667},[566,1496,1245],{"class":875},[566,1498,1499],{"class":600}," server ",[566,1501,1251],{"class":596},[566,1503,1457],{"class":649},[566,1505,653],{"class":600},[566,1507,1508],{"class":596},"...",[566,1510,1511],{"class":649},"postcodeHandlers",[566,1513,1514],{"class":600},"())\n",[566,1516,1517],{"class":568,"line":674},[566,1518,643],{"emptyLinePlaceholder":642},[566,1520,1521,1524,1526,1529,1532,1535,1537,1540],{"class":568,"line":679},[566,1522,1523],{"class":649},"beforeAll",[566,1525,653],{"class":600},[566,1527,1528],{"class":596},"()",[566,1530,1531],{"class":875}," =>",[566,1533,1534],{"class":600}," server",[566,1536,756],{"class":596},[566,1538,1539],{"class":649},"listen",[566,1541,1514],{"class":600},[566,1543,1544,1547,1549,1551,1553,1555,1557,1560],{"class":568,"line":762},[566,1545,1546],{"class":649},"afterAll",[566,1548,653],{"class":600},[566,1550,1528],{"class":596},[566,1552,1531],{"class":875},[566,1554,1534],{"class":600},[566,1556,756],{"class":596},[566,1558,1559],{"class":649},"close",[566,1561,1514],{"class":600},[160,1563,1564,1565,1568,1569,1572],{},"Your production code keeps calling ",[164,1566,1567],{},"https:\u002F\u002Fapi.postcode.gov.ng",", and the handlers answer instead. Send a reserved key from a test to cover the error paths. If you'd rather run a local server, ",[164,1570,1571],{},"npx ng-postcode-mock"," starts one on port 8081, the port NIPOST's docs use for a local gateway.",[183,1574,1576],{"id":1575},"how-the-mock-stays-honest","How the mock stays honest",[160,1578,1579],{},"A mock that looks too real is a liability, so I've been careful about what it claims.",[160,1581,1582,1585],{},[288,1583,1584],{},"Shapes come from the spec, and a test holds them to it."," The paths, parameters and response shapes follow NIPOST's published OpenAPI spec, kept in the repo with its source and fetch date. A contract test validates the mock's responses against it, so when the spec changes and I refresh the copy, the test that fails points at what moved.",[160,1587,1588,1591,1592,262,1594,1597,1598,1601,1602,1605],{},[288,1589,1590],{},"Made-up data says so."," The sample and sandbox postcodes NIPOST publishes are real, so the mock never invents real-looking facts about them. State names and zones are real for the 11 states the mock knows: the 10 the sample postcodes cover, plus Ekiti from the quickstart's example. LGA names and addresses read ",[164,1593,964],{},[164,1595,1596],{},"10 MOCK STREET",". Every response body carries ",[164,1599,1600],{},"\"mock\": true"," and an ",[164,1603,1604],{},"X-Mock: true"," header.",[160,1607,1608,1611,1612,1617],{},[288,1609,1610],{},"Guesses are marked as guesses."," NIPOST hasn't published the inner fields for L4 and L5, any response shape for the nearby search, or the error codes for a rate limit or an invalid key. The mock fills them in, and the README and docs page say which parts are guessed. If you get access and see a real response that differs, please ",[175,1613,1616],{"href":1614,"rel":1615},"https:\u002F\u002Fgithub.com\u002Fpoliha\u002Fng-postcode\u002Fissues",[179],"open an issue"," with it.",[183,1619,1621],{"id":1620},"troubleshooting","Troubleshooting",[160,1623,1624,1627,1628,1630],{},[288,1625,1626],{},"Issue",": Every call returns ",[164,1629,1180],{},", including Search and Lookup L1.",[160,1632,1633,1636,1637,1639,1640,1642],{},[288,1634,1635],{},"Solution",": Every endpoint needs a key, including the ones that cost no credits. Send it in the ",[164,1638,196],{}," header. To check that your code handles the 401, send ",[164,1641,1175],{}," to the mock.",[1644,1645],"hr",{},[160,1647,1648,1650,1651,1653],{},[288,1649,1626],{},": A sample postcode such as ",[164,1652,276],{}," doesn't resolve with your test key.",[160,1655,1656,1658,1659,1661],{},[288,1657,1635],{},": Test keys read the sandbox, and the samples only resolve under a live key. Use the sandbox postcodes, such as ",[164,1660,272],{},", or call autocomplete with your test key to find more.",[1644,1663],{},[160,1665,1666,1668,1669,756],{},[288,1667,1626],{},": A lookup for a well-formed code comes back with ",[164,1670,1671],{},"valid: false",[160,1673,1674,1676],{},[288,1675,1635],{},": The format can be right while the postcode doesn't exist. While you build, use the postcodes NIPOST publishes for your key type.",[183,1678,1680],{"id":1679},"next-steps","Next Steps",[1682,1683,1684,1697,1704],"ul",{},[1685,1686,1687,1688,262,1693],"li",{},"The package and mock: ",[175,1689,1692],{"href":1690,"rel":1691},"https:\u002F\u002Fgithub.com\u002Fpoliha\u002Fng-postcode",[179],"github.com\u002Fpoliha\u002Fng-postcode",[175,1694,573],{"href":1695,"rel":1696},"https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002Fng-postcode",[179],[1685,1698,1699,1700],{},"The official docs and OpenAPI spec: ",[175,1701,1703],{"href":177,"rel":1702},[179],"docs.postcode.gov.ng",[1685,1705,1706],{},"NIPOST's widget SDKs, for letting users pick a postcode on a map, are listed in the same docs",[183,1708,1710],{"id":1709},"final-thoughts","Final Thoughts",[160,1712,1713],{},"A national postcode system is a big piece of infrastructure, and the docs behind it are thorough. The mock exists so anyone can see whether it fits what they're building before signing up for anything, and so their tests can cover the paths a real account rarely shows. If it helped, or if you spot a difference from the real API, I'd like to hear about it.",[160,1715,1716],{},"ng-postcode is unofficial and not affiliated with NIPOST or the Federal Ministry of Communications, Innovation and Digital Economy.",[1718,1719],"blog-cta",{},[1721,1722,1723],"style",{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}",{"title":562,"searchDepth":639,"depth":639,"links":1725},[1726,1727,1728,1729,1730,1731,1732,1733,1734,1735,1736,1737],{"id":185,"depth":639,"text":186},{"id":280,"depth":639,"text":281},{"id":320,"depth":639,"text":321},{"id":459,"depth":639,"text":460},{"id":548,"depth":639,"text":549},{"id":845,"depth":639,"text":846},{"id":1203,"depth":639,"text":1204},{"id":1433,"depth":639,"text":1434},{"id":1575,"depth":639,"text":1576},{"id":1620,"depth":639,"text":1621},{"id":1679,"depth":639,"text":1680},{"id":1709,"depth":639,"text":1710},[7],"2026-10-06","How Nigeria's postcodes and the NIPOST Postcode API work, from the code format to lookup levels, and how to build and test against it with an open source mock.","md",null,"\u002Fassets\u002Fimages\u002Fblog\u002Fbuilding-on-nigerias-postcode-api.webp",{"status":1745},"published",{"title":66,"description":1740},{"loc":67},[7,1749,892,1750,1751,1066,1752,1753],"nigeria","nipost","api","testing","typescript","bp4UyYD4mgMOs3Z96mJMpReki2Ho1yaUvUaX4t3pOA4",[1756,1758],{"title":62,"path":63,"stem":64,"description":1757,"children":-1},"Explore how Tailwind CSS enhances MapleStack’s UI with utility-first styling, enabling rapid design iterations and responsive layouts.",{"title":70,"path":71,"stem":72,"description":1759,"children":-1},"Learn how to set up and debug multiple NestJS applications simultaneously in VSCode. Complete guide with launch.json configuration and debugging tips.",[1761,1771,1780,1788,1795,1804,1812,1820,1829,1837,1845,1853,1861,1869,1878,1887,1895,1903,1911,1918,1926,1934,1942,1950,1958,1966,1974,1983,1991,1999,2007,2014,2022,2030],{"id":1762,"color":1763,"description":1764,"extension":1765,"meta":1766,"name":1767,"slug":1768,"stem":1769,"__hash__":1770},"tags\u002Ftags\u002Fai.yml","purple","Artificial Intelligence and machine learning topics","yml",{},"AI","ai","tags\u002Fai","3Kbwn1GafRMDqK3ErYiBohkqyMbs4y-gmPA5L0htrtI",{"id":1772,"color":1773,"description":1774,"extension":1765,"meta":1775,"name":1776,"slug":1777,"stem":1778,"__hash__":1779},"tags\u002Ftags\u002Fangular.yml","red","Angular framework tutorials and component development",{},"Angular","angular","tags\u002Fangular","4JdHB81zNrypA0P8D8gi97pj0Ds9E3_IW7YbLvnzs1k",{"id":1781,"color":1782,"description":1783,"extension":1765,"meta":1784,"name":1785,"slug":1751,"stem":1786,"__hash__":1787},"tags\u002Ftags\u002Fapi.yml","blue","Designing, building and consuming web APIs",{},"API","tags\u002Fapi","A0x8MlouPoSEbat0Yj1-_xytY1yBgyVkGIlY-bTzPUM",{"id":1789,"color":1790,"description":1791,"extension":1765,"meta":1792,"name":5,"slug":7,"stem":1793,"__hash__":1794},"tags\u002Ftags\u002Farticles.yml","gray","General articles and blog posts",{},"tags\u002Farticles","iT5XsTSsj5v9-aLrtgHxDyk_QCYfK8mlea2vVyCdcZM",{"id":1796,"color":1797,"description":1798,"extension":1765,"meta":1799,"name":1800,"slug":1801,"stem":1802,"__hash__":1803},"tags\u002Ftags\u002Faws.yml","orange","Amazon Web Services cloud platform tutorials and integrations",{},"AWS","aws","tags\u002Faws","i2eOUxfMNR5PbOtUVc7VAbZk5Y7LupicySpVtJY24_A",{"id":1805,"color":1782,"description":1806,"extension":1765,"meta":1807,"name":1808,"slug":1809,"stem":1810,"__hash__":1811},"tags\u002Ftags\u002Fbard.yml","Google Bard AI assistant",{},"Bard","bard","tags\u002Fbard","uy9j-VgMeapYwgVu98-Df6yqWCXCAwfLn5j8Sb15ZmE",{"id":1813,"color":1773,"description":1814,"extension":1765,"meta":1815,"name":1816,"slug":1817,"stem":1818,"__hash__":1819},"tags\u002Ftags\u002Fbuilding-maplestack.yml","Series on building the MapleStack platform",{},"Building MapleStack","building-maplestack","tags\u002Fbuilding-maplestack","hIpKigdKwKbxd8aqva_KMKT-hpxpLDOCnQ9qoRGyjPg",{"id":1821,"color":1822,"description":1823,"extension":1765,"meta":1824,"name":1825,"slug":1826,"stem":1827,"__hash__":1828},"tags\u002Ftags\u002Fchatgpt.yml","green","OpenAI ChatGPT integration and usage",{},"ChatGPT","chatgpt","tags\u002Fchatgpt","32dptlACBGlEhrLxxih3XBVQeeqayyl3nUn4xrjh6mM",{"id":1830,"color":1763,"description":1831,"extension":1765,"meta":1832,"name":1833,"slug":1834,"stem":1835,"__hash__":1836},"tags\u002Ftags\u002Fclaude-code.yml","Building with and extending Anthropic's Claude Code",{},"Claude Code","claude-code","tags\u002Fclaude-code","DILmAuTs9y1dm4L44wLW-52LfKAjbFlccJOJOpIBnD0",{"id":1838,"color":1797,"description":1839,"extension":1765,"meta":1840,"name":1841,"slug":1842,"stem":1843,"__hash__":1844},"tags\u002Ftags\u002Fdataloader.yml","DataLoader utility for batching and caching",{},"DataLoader","dataloader","tags\u002Fdataloader","LmXdDjUElkWFv52Zjjjs9iSl5ho6XzjgngiwhkAnlLk",{"id":1846,"color":1782,"description":1847,"extension":1765,"meta":1848,"name":1849,"slug":1850,"stem":1851,"__hash__":1852},"tags\u002Ftags\u002Fdns.yml","Domain Name System configuration and troubleshooting",{},"DNS","dns","tags\u002Fdns","-ouuVgZR4CIiCKyOp51RQoXCKDvhpBV0KMUf1zqLXNc",{"id":1854,"color":1782,"description":1855,"extension":1765,"meta":1856,"name":1857,"slug":1858,"stem":1859,"__hash__":1860},"tags\u002Ftags\u002Fengineering.yml","Software engineering practice, craft and technical decision making",{},"Engineering","engineering","tags\u002Fengineering","t3NT39qm9R9NCBT8lwSuG9cxZZZa4Xds-X7AINZw-3o",{"id":1862,"color":1773,"description":1863,"extension":1765,"meta":1864,"name":1865,"slug":1866,"stem":1867,"__hash__":1868},"tags\u002Ftags\u002Ferror-handling.yml","Handling, surfacing and recovering from failure across a stack",{},"Error Handling","error-handling","tags\u002Ferror-handling","jxCFis914HceisiNgt920hxUAe4KgCulUZGLzKcNspg",{"id":1870,"color":1871,"description":1872,"extension":1765,"meta":1873,"name":1874,"slug":1875,"stem":1876,"__hash__":1877},"tags\u002Ftags\u002Fgraphql.yml","pink","GraphQL API development, schema design, and implementation guides",{},"GraphQL","graphql","tags\u002Fgraphql","1wfWwIg4deU-5JedMVdkw9j9mualhUlJ1MZDgb6V7R0",{"id":1879,"color":1880,"description":1881,"extension":1765,"meta":1882,"name":1883,"slug":1884,"stem":1885,"__hash__":1886},"tags\u002Ftags\u002Fjavascript.yml","yellow","JavaScript programming language tutorials and best practices",{},"JavaScript","javascript","tags\u002Fjavascript","qZheXj7oAA3rukxJWCK5SLSe3-_mCwMq0OAi9BtEFRw",{"id":1888,"color":1790,"description":1889,"extension":1765,"meta":1890,"name":1891,"slug":1892,"stem":1893,"__hash__":1894},"tags\u002Ftags\u002Fkoajs.yml","Koa.js web framework for Node.js",{},"KoaJS","koajs","tags\u002Fkoajs","KVPDsU-wIMo46n6-bdbF-dRkiwlRbz3RJP7K9vSOrmQ",{"id":1896,"color":1822,"description":1897,"extension":1765,"meta":1898,"name":1899,"slug":1900,"stem":1901,"__hash__":1902},"tags\u002Ftags\u002Fletsencrypt.yml","Free SSL\u002FTLS certificates with Let's Encrypt",{},"Let's Encrypt","letsencrypt","tags\u002Fletsencrypt","0s8AZbiIKoWGwiCNeWEc_OIo3aDAJlkSB5fjQlgcGNk",{"id":1904,"color":1790,"description":1905,"extension":1765,"meta":1906,"name":1907,"slug":1908,"stem":1909,"__hash__":1910},"tags\u002Ftags\u002Flinux.yml","Linux server administration and command-line tools",{},"Linux","linux","tags\u002Flinux","YmvWMX2B2AdXzWfUycxqXt4m9jSgpLE8q7eW6FeiJPk",{"id":1912,"color":1773,"description":1913,"extension":1765,"meta":1914,"name":110,"slug":1915,"stem":1916,"__hash__":1917},"tags\u002Ftags\u002Fmaplestack.yml","Building MapleStack job board platform",{},"maplestack","tags\u002Fmaplestack","v2dZpc6OuxQeK2KTQIC3RYfv4FwNzIAa0VDTzLjWjAA",{"id":1919,"color":1797,"description":1920,"extension":1765,"meta":1921,"name":1922,"slug":1923,"stem":1924,"__hash__":1925},"tags\u002Ftags\u002Fmigration.yml","Platform and data migration guides",{},"Migration","migration","tags\u002Fmigration","scw_ZTGUersSET1o4l_NAkZ3cDKiplB53LqtFRvE7Yc",{"id":1927,"color":1773,"description":1928,"extension":1765,"meta":1929,"name":1930,"slug":1931,"stem":1932,"__hash__":1933},"tags\u002Ftags\u002Fnestjs.yml","Articles about NestJS framework for building efficient, scalable Node.js server-side applications",{},"NestJS","nestjs","tags\u002Fnestjs","GQFi18GrTd6pSmws7rOsyKTbLATtQwywfsZpQwhCsI0",{"id":1935,"color":1822,"description":1936,"extension":1765,"meta":1937,"name":1938,"slug":1939,"stem":1940,"__hash__":1941},"tags\u002Ftags\u002Fnodejs.yml","Node.js runtime environment and server-side JavaScript",{},"Node.js","nodejs","tags\u002Fnodejs","lusY1Ii4Hd3O6gLdY2NbhHchd3YAT_Q1QOm0BqrfY_4",{"id":1943,"color":1822,"description":1944,"extension":1765,"meta":1945,"name":1946,"slug":1947,"stem":1948,"__hash__":1949},"tags\u002Ftags\u002Fnuxt.yml","Nuxt.js framework for Vue.js applications",{},"Nuxt","nuxt","tags\u002Fnuxt","QwQk7HOatKzP3xW3-ie3L7_OJ7O8hbqCT2E8AUtrV2o",{"id":1951,"color":1797,"description":1952,"extension":1765,"meta":1953,"name":1954,"slug":1955,"stem":1956,"__hash__":1957},"tags\u002Ftags\u002Fplugins.yml","Extending tools and frameworks through plugin systems",{},"Plugins","plugins","tags\u002Fplugins","gShR9UaVAM-OLItdfQRg89U_mFSX53KgzFge6eQGSaE",{"id":1959,"color":1782,"description":1960,"extension":1765,"meta":1961,"name":1962,"slug":1963,"stem":1964,"__hash__":1965},"tags\u002Ftags\u002Fpostgresql.yml","PostgreSQL database tutorials, tips, and best practices",{},"PostgreSQL","postgresql","tags\u002Fpostgresql","o-29A3fv27MHMpunvcXvumOEdfKJJ_Wu6s_Wdej2ynI",{"id":1967,"color":1763,"description":1968,"extension":1765,"meta":1969,"name":1970,"slug":1971,"stem":1972,"__hash__":1973},"tags\u002Ftags\u002Fprojects.yml","Articles about personal and professional projects",{},"Projects","projects","tags\u002Fprojects","L834U-TbUh7RZkMdabAGEHgsgGb1hUWpWeIomHD81sE",{"id":1975,"color":1976,"description":1977,"extension":1765,"meta":1978,"name":1979,"slug":1980,"stem":1981,"__hash__":1982},"tags\u002Ftags\u002Freact.yml","cyan","React library guides for building user interfaces",{},"React","react","tags\u002Freact","Cl0931Ka3nLB6sYwUevYf3q--ugJfBakfGOEXDpY6Mw",{"id":1984,"color":1976,"description":1985,"extension":1765,"meta":1986,"name":1987,"slug":1988,"stem":1989,"__hash__":1990},"tags\u002Ftags\u002Fsaas.yml","Building and running software as a service products",{},"SaaS","saas","tags\u002Fsaas","74u9GtZbJ7IF37YdnFP6JagBgw0PblIZK2mc3r8bbew",{"id":1992,"color":1782,"description":1993,"extension":1765,"meta":1994,"name":1995,"slug":1996,"stem":1997,"__hash__":1998},"tags\u002Ftags\u002Fstellar.yml","Stellar blockchain network and cryptocurrency development",{},"Stellar","stellar","tags\u002Fstellar","H71kcSAHbcG_q3Y5cFkgonNN5uH5JxVsXlZjLaotbnk",{"id":2000,"color":1976,"description":2001,"extension":1765,"meta":2002,"name":2003,"slug":2004,"stem":2005,"__hash__":2006},"tags\u002Ftags\u002Ftailwindcss.yml","Tailwind CSS utility-first styling framework",{},"Tailwind CSS","tailwindcss","tags\u002Ftailwindcss","_Q7hBntSUWoSWUcYMwmpz1kfCE7RVlSTR9U86k3av_g",{"id":2008,"color":1782,"description":2009,"extension":1765,"meta":2010,"name":2011,"slug":1753,"stem":2012,"__hash__":2013},"tags\u002Ftags\u002Ftypescript.yml","TypeScript language features, type safety, and development tips",{},"TypeScript","tags\u002Ftypescript","zv13aSlv3NhTqdsNRYQ0pKjE5j8rZZk5zv2kD6guG4c",{"id":2015,"color":1822,"description":2016,"extension":1765,"meta":2017,"name":2018,"slug":2019,"stem":2020,"__hash__":2021},"tags\u002Ftags\u002Fvalidation.yml","Validating input and data at every layer of an application",{},"Validation","validation","tags\u002Fvalidation","Y2whDaQYOSOUCRjs2TcEedcEvVA8a2b3kq_-xULT3gY",{"id":2023,"color":1822,"description":2024,"extension":1765,"meta":2025,"name":2026,"slug":2027,"stem":2028,"__hash__":2029},"tags\u002Ftags\u002Fvps.yml","Virtual Private Server setup and management",{},"VPS","vps","tags\u002Fvps","SOzIPECxOX5g_l0rF3QhJEWwMnOeRe0htUuhWc62PXE",{"id":2031,"color":1782,"description":2032,"extension":1765,"meta":2033,"name":2034,"slug":2035,"stem":2036,"__hash__":2037},"tags\u002Ftags\u002Fwordpress.yml","WordPress CMS and related topics",{},"WordPress","wordpress","tags\u002Fwordpress","FiVBFOXCbR45_HKOqwcZAu9dDru5YOemYUcilPwarYQ",[2039,2041,2043,2045,2047,2049,2051,2053,2055,2057,2059,2061,2063,2065,2067,2069,2071,2073,2075,2077,2079,2081,2083,2085,2087,2089,2091,2093,2095,2097,2099,2101,2103,2105],{"id":1762,"color":1763,"description":1764,"extension":1765,"meta":2040,"name":1767,"slug":1768,"stem":1769,"__hash__":1770,"count":639},{},{"id":1772,"color":1773,"description":1774,"extension":1765,"meta":2042,"name":1776,"slug":1777,"stem":1778,"__hash__":1779,"count":569},{},{"id":1781,"color":1782,"description":1783,"extension":1765,"meta":2044,"name":1785,"slug":1751,"stem":1786,"__hash__":1787,"count":646},{},{"id":1789,"color":1790,"description":1791,"extension":1765,"meta":2046,"name":5,"slug":7,"stem":1793,"__hash__":1794,"count":768},{},{"id":1796,"color":1797,"description":1798,"extension":1765,"meta":2048,"name":1800,"slug":1801,"stem":1802,"__hash__":1803,"count":646},{},{"id":1805,"color":1782,"description":1806,"extension":1765,"meta":2050,"name":1808,"slug":1809,"stem":1810,"__hash__":1811,"count":569},{},{"id":1813,"color":1773,"description":1814,"extension":1765,"meta":2052,"name":1816,"slug":1817,"stem":1818,"__hash__":1819,"count":768},{},{"id":1821,"color":1822,"description":1823,"extension":1765,"meta":2054,"name":1825,"slug":1826,"stem":1827,"__hash__":1828,"count":569},{},{"id":1830,"color":1763,"description":1831,"extension":1765,"meta":2056,"name":1833,"slug":1834,"stem":1835,"__hash__":1836,"count":569},{},{"id":1838,"color":1797,"description":1839,"extension":1765,"meta":2058,"name":1841,"slug":1842,"stem":1843,"__hash__":1844,"count":569},{},{"id":1846,"color":1782,"description":1847,"extension":1765,"meta":2060,"name":1849,"slug":1850,"stem":1851,"__hash__":1852,"count":569},{},{"id":1854,"color":1782,"description":1855,"extension":1765,"meta":2062,"name":1857,"slug":1858,"stem":1859,"__hash__":1860,"count":639},{},{"id":1862,"color":1773,"description":1863,"extension":1765,"meta":2064,"name":1865,"slug":1866,"stem":1867,"__hash__":1868,"count":569},{},{"id":1870,"color":1871,"description":1872,"extension":1765,"meta":2066,"name":1874,"slug":1875,"stem":1876,"__hash__":1877,"count":639},{},{"id":1879,"color":1880,"description":1881,"extension":1765,"meta":2068,"name":1883,"slug":1884,"stem":1885,"__hash__":1886,"count":679},{},{"id":1888,"color":1790,"description":1889,"extension":1765,"meta":2070,"name":1891,"slug":1892,"stem":1893,"__hash__":1894,"count":639},{},{"id":1896,"color":1822,"description":1897,"extension":1765,"meta":2072,"name":1899,"slug":1900,"stem":1901,"__hash__":1902,"count":569},{},{"id":1904,"color":1790,"description":1905,"extension":1765,"meta":2074,"name":1907,"slug":1908,"stem":1909,"__hash__":1910,"count":569},{},{"id":1912,"color":1773,"description":1913,"extension":1765,"meta":2076,"name":110,"slug":1915,"stem":1916,"__hash__":1917,"count":773},{},{"id":1919,"color":1797,"description":1920,"extension":1765,"meta":2078,"name":1922,"slug":1923,"stem":1924,"__hash__":1925,"count":569},{},{"id":1927,"color":1773,"description":1928,"extension":1765,"meta":2080,"name":1930,"slug":1931,"stem":1932,"__hash__":1933,"count":667},{},{"id":1935,"color":1822,"description":1936,"extension":1765,"meta":2082,"name":1938,"slug":1939,"stem":1940,"__hash__":1941,"count":646},{},{"id":1943,"color":1822,"description":1944,"extension":1765,"meta":2084,"name":1946,"slug":1947,"stem":1948,"__hash__":1949,"count":646},{},{"id":1951,"color":1797,"description":1952,"extension":1765,"meta":2086,"name":1954,"slug":1955,"stem":1956,"__hash__":1957,"count":569},{},{"id":1959,"color":1782,"description":1960,"extension":1765,"meta":2088,"name":1962,"slug":1963,"stem":1964,"__hash__":1965,"count":646},{},{"id":1967,"color":1763,"description":1968,"extension":1765,"meta":2090,"name":1970,"slug":1971,"stem":1972,"__hash__":1973,"count":768},{},{"id":1975,"color":1976,"description":1977,"extension":1765,"meta":2092,"name":1979,"slug":1980,"stem":1981,"__hash__":1982,"count":639},{},{"id":1984,"color":1976,"description":1985,"extension":1765,"meta":2094,"name":1987,"slug":1988,"stem":1989,"__hash__":1990,"count":569},{},{"id":1992,"color":1782,"description":1993,"extension":1765,"meta":2096,"name":1995,"slug":1996,"stem":1997,"__hash__":1998,"count":569},{},{"id":2000,"color":1976,"description":2001,"extension":1765,"meta":2098,"name":2003,"slug":2004,"stem":2005,"__hash__":2006,"count":569},{},{"id":2008,"color":1782,"description":2009,"extension":1765,"meta":2100,"name":2011,"slug":1753,"stem":2012,"__hash__":2013,"count":768},{},{"id":2015,"color":1822,"description":2016,"extension":1765,"meta":2102,"name":2018,"slug":2019,"stem":2020,"__hash__":2021,"count":569},{},{"id":2023,"color":1822,"description":2024,"extension":1765,"meta":2104,"name":2026,"slug":2027,"stem":2028,"__hash__":2029,"count":569},{},{"id":2031,"color":1782,"description":2032,"extension":1765,"meta":2106,"name":2034,"slug":2035,"stem":2036,"__hash__":2037,"count":639},{},1791381574264]