@br-geo-kit/address
v1.0.0
Published
Parse, format and validate Brazilian postal addresses
Maintainers
Readme
@br-geo-kit/address
Parse, render and validate a Brazilian postal address. Pure, offline, no dependencies beyond the kit.
pnpm add @br-geo-kit/addressParsing a street line
Brazilian addresses are written logradouro, número, complemento, but
the separators are not reliable: the comma before the number is often
missing, the complement is sometimes introduced with a dash, and a
building may be s/n.
import { parseStreetLine } from '@br-geo-kit/address'
parseStreetLine('Av. Paulista, 1578 - apto 42')
// { street: 'Avenida Paulista', type: 'Avenida', name: 'Paulista',
// number: '1578', complement: 'apto 42', withoutNumber: false }
parseStreetLine('Rua das Flores 123') // number: '123'
parseStreetLine('Rua XV de Novembro, s/n') // number: null, withoutNumber: true
parseStreetLine('Rodovia BR 101') // the 101 belongs to the roadAbbreviations are expanded — Av., av, AVENIDA all become
Avenida — because the same street arrives written four ways across
four systems, and a lookup keyed on the raw string finds none of them.
withoutNumber is not the same as number === null. The first
means the line said "no number" outright; the second means it did not
mention one. A shipping form needs to tell a complete address from an
incomplete one.
It never invents a number and never corrects spelling. Guessing that
Paulsta meant Paulista is how a parcel ends up on the wrong street
with nobody having approved it.
Rendering
The order is fixed by the Correios, not a matter of taste.
import { formatAddress, formatAddressLines } from '@br-geo-kit/address'
formatAddress(address)
// 'Avenida Paulista, 1578, apto 42, Bela Vista, São Paulo - SP, 01310-100'
formatAddressLines(address)
// ['Avenida Paulista, 1578, apto 42', 'Bela Vista', 'São Paulo - SP', '01310-100']
formatAddressLine(address) // the one-line form a delivery app showsMissing parts are dropped, never rendered as null or as an empty
segment with stray punctuation around it. A CEP único with no street
comes out as Porto Velho - RO, 76800-000, not
, , Porto Velho - RO, 76800-000.
Validating
import { validateAddress, isValidAddress } from '@br-geo-kit/address'
validateAddress(address)
// [] — nothing wrong
validateAddress({ cep: '123', city: '', state: 'XX' })
// [ { field: 'cep', code: 'malformed', message: '...' },
// { field: 'city', code: 'required', ... },
// { field: 'state', code: 'malformed', ... } ]Returns every issue, not the first — a form that reveals one problem per submission is a form people abandon. And returns them rather than throwing, because a failed validation is an expected outcome.
Two defaults that look wrong and are not:
- A street is not required. Roughly 1 200 municipalities are
addressed by a single CEP with no street. Requiring one rejects their
residents. Turn it on with
{ requireStreet: true }. - A number is not required. A CEP lookup never returns one, so
validating a freshly-resolved address would report every one of them
as invalid. Turn it on with
{ requireNumber: true }when checking what a user submitted.
This validates shape, not existence. See API conventions for the three levels of "valid".
Also here
Re-exported from @br-geo-kit/core so a form needs one import:
import { maskCep, formatCep, normalizeCep, isWellFormedCep, toUF } from '@br-geo-kit/address'
maskCep('013101') // '01310-1' — progressive, for an input field
formatCep('01310100') // '01310-100'
normalizeCep(1310100) // '01310100' — the leading zero a spreadsheet droppedmaskCep never returns null: a partial CEP is not an error while the
user is mid-keystroke.
Licence
MIT
