Validar um código postal de Portugal
Um código postal português (CP7) tem sete dígitos no formato NNNN-NNN — por exemplo, 1000-098. Validar é confirmar duas coisas: que o formato está certo e que o código existe. A primeira faz-se com uma expressão regular; a segunda, com um pedido à API do Moradas, grátis e sem chave.
1. O formato: expressão regular
Quatro dígitos, um hífen e três dígitos:
^\d{4}-\d{3}$
Os quatro primeiros dígitos (CP4) indicam a zona postal; dentro dessa zona, os três últimos (CP3) distinguem ruas, troços de rua ou grandes destinatários com código próprio, como empresas e instituições.
| Entrada | Resultado | O que fazer |
|---|---|---|
1000-098 | válido | — |
1000098, 1000 098 | formato inválido | normalizar para 1000-098 |
1000 | incompleto | só o CP4 — peça os sete dígitos |
1000-98 | formato inválido | falta um dígito |
Os clientes escrevem o código de muitas maneiras — com espaço, sem hífen. Normalize antes de validar:
function normalizeCP7(text) {
const digits = text.replace(/\D/g, ''); // keep the digits only
return digits.length === 7 ? digits.slice(0, 4) + '-' + digits.slice(4) : null;
}
normalizeCP7('1000 098'); // '1000-098'
/^\d{4}-\d{3}$/.test('1000-098'); // true
/^\d{4}-\d{3}$/.test('1000098'); // false
Noutras linguagens:
// PHP
preg_match('/^\d{4}-\d{3}$/D', $cp7) === 1
# Python
re.fullmatch(r"\d{4}-\d{3}", cp7, re.ASCII) is not None
// C#
Regex.IsMatch(cp7, @"^\d{4}-\d{3}\z")
2. A existência: um pedido à API
Um código com o formato certo pode não existir. O endpoint /cp/{cp7} responde 200 com a localidade, o concelho, o distrito e as ruas abrangidas, ou 404 se o código não existir:
GET https://moradas.dev/cp/1000-098
{ "cp7": "1000-098", "cp4": "1000", "cp3": "098",
"distrito": "Lisboa", "concelho": "Lisboa", "localidade": "Lisboa",
"arterias": [ { "street": "Praça do Chile", ... } ] }
GET https://moradas.dev/cp/9999-999
404 { "error": "CP7 not found" }
Validação completa, no browser ou em Node.js:
async function validateCP7(input) {
const digits = input.replace(/\D/g, '');
if (digits.length !== 7) return { valid: false, reason: 'format' };
const cp7 = digits.slice(0, 4) + '-' + digits.slice(4);
try {
const res = await fetch('https://moradas.dev/cp/' + cp7);
if (res.status === 404) return { valid: false, reason: 'not found', cp7 };
if (!res.ok) return { valid: null, cp7 }; // 429 or outage: don't block the customer
const card = await res.json();
return { valid: true, cp7, localidade: card.localidade, concelho: card.concelho, distrito: card.distrito };
} catch (e) {
return { valid: null, cp7 }; // network error: don't block the customer
}
}
await validateCP7('1000 098');
// { valid: true, cp7: '1000-098', localidade: 'Lisboa', concelho: 'Lisboa', distrito: 'Lisboa' }
Se a API não responder — limite de pedidos ou falha de rede — trate o código como «não verificado» e deixe o cliente continuar: o serviço é grátis e não tem garantia de disponibilidade. O mesmo pedido em PHP, Python e C#, com nova tentativa após um 429, está no início rápido da API.
3. Melhor ainda: preencher o código em vez de o validar
Muitos erros nascem de escrever o código postal à mão. Com o autocomplete do Moradas, o cliente escolhe a rua numa lista e o CP7 é preenchido pelo número de porta — numa rua com vários códigos postais, é o número que decide qual se aplica.