Validating phone numbers in Zod is key for data integrity. We will explore five common methods, complete with code snippets, to handle this task. We will also examine the limitations of these traditional approaches and show how Abstract API overcomes these specific challenges.
How to Implement Phone Number Validation in Zod
Here are four common methods to validate phone numbers with Zod. Each approach uses a different technique, from simple regular expressions to more comprehensive validation libraries for robust checks.
Direct Regex Match
The most straightforward approach is to use a regular expression. The "z.string().regex()" method allows you to define a specific pattern that the phone number string must match. This acts as a basic syntactic check.
This method requires no external libraries and executes quickly. The code defines a Zod schema for a string and applies a regex pattern to it, with a custom "invalid" message for numbers that do not match the pattern. Some developers recommend this for simple cases.
const phone = z.string().regex(/^([+]?[\s0-9]+)?(\d{3}|[(]?[0-9]+[)])?([-]?[\s]?[0-9])+$/, 'invalid');
Refinement with validator.js
For more robust validation, you can use the "refine" method with a third-party library like "validator.js". This approach keeps your Zod schema clean while it outsources the complex validation logic to a specialized tool.
The library contains a maintained catalog of country-specific phone number formats. The code first imports the "validator" library. Then, it defines a Zod string schema and uses "refine" to pass the input to the "validator.isMobilePhone" function, which checks its validity against known mobile patterns.
import validator from 'validator';
const phone = z.string().refine(v => validator.isMobilePhone(v, 'any'), { message: 'invalid' });
Transformation with libphonenumber-js
A more powerful method involves the "libphonenumber-js" library, which can parse, validate, and normalize phone numbers into a standard format. This is often implemented with Zod's "transform" or "superRefine" methods.
The process starts with the import of the "parsePhoneNumberFromString" function. The Zod schema then uses "transform" to process the input string. Inside the transform, the function attempts to parse the number. If the number is invalid, it adds a custom issue, a pattern discussed in a relevant GitHub issue. If valid, it returns the number in the standard E.164 format, a technique also found on Stack Overflow.
import { parsePhoneNumberFromString } from 'libphonenumber-js';
const phone = z
.string()
.transform((v, ctx) => {
const p = parsePhoneNumberFromString(v, { defaultCountry: 'US', extract: false });
if (!p?.isValid()) {
ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'invalid' });
return z.NEVER;
}
return p.number; // always returns E.164
});
Native E.164 Validation in Zod 4
With the release of Zod 4, a dedicated helper for the E.164 format is available. The "z.e164()" method provides a simple, built-in way to validate numbers that conform to this international standard.
This helper uses a specific regex that requires the number to start with a "+" symbol, followed by one to fifteen digits. It offers the same API surface as "z.string()" without the need for extra code or external dependencies, as noted in community discussions.
const phone = z.e164();
Challenges of Phone Number Validation in Zod
These traditional methods present several drawbacks. They often struggle with the dynamic nature of global phone number formats and cannot confirm if a number is actually in use or reachable.
- Zod lacks a native phone schema. This forces developers to use complex regex patterns that quickly become outdated as global numbering rules change. This approach creates brittle code that requires constant maintenance.
- The libphonenumber-js library can accept non-dialable numbers. When used with Zod's refine method, these invalid numbers pass validation, which requires extra logic to catch edge cases the library misses.
- The metadata for libphonenumber-js often lags behind official numbering plan updates. A Zod validator that relies on this library may incorrectly reject new, valid phone numbers or approve old, deprecated ones.
- Zod validation operates only at a syntactic level. Methods like z.e164() confirm format compliance but cannot verify if a number is active, assigned, or poses a fraud risk.
Validate Phone Numbers with Abstract API
Validate phone numbers effectively in Zod to maintain clean user data and improve application reliability.
Get started for free
How Abstract API Handles Phone Number Validation in Zod
Abstract API addresses the core weaknesses of traditional methods by off-loading validation logic to a cloud system that stays current for over 195 countries.
- It confirms if a number is actually assigned and reachable, not just its syntax.
- It removes the maintenance burden of local patterns, as numbering plans update automatically in the cloud.
- It returns metadata like carrier, line-type, and location without large offline libraries that increase a client build's size.
How to Add Abstract API to Your Development Environment
Once you know Abstract’s capabilities, to add its phone number validation to your project is simple.
- Sign up at Abstract and copy the phone-validation API key from the dashboard.
- Install dependencies with the command npm i zod node-fetch dotenv –-save.
- Add ABSTRACT_API_KEY=your_key to your .env file and load it with dotenv/config.
- Create a Zod schema that calls Abstract inside an async .refine predicate.
- Use schema.parseAsync(data) in your route or controller.
Sample Phone Number Validation Implementation with Abstract API
The code below defines a Zod schema for a phone number. It uses an asynchronous refine method to send the number to Abstract API for validation. The schema only passes if the API response confirms the number is valid with the field "valid" set to "true". Otherwise, it returns a custom error message.
import 'dotenv/config'
import { z } from 'zod'
import fetch from 'node-fetch'
const phoneSchema = z.string().nonempty().refine(
async (value) => {
const url = `https://phonevalidation.abstractapi.com/v1/?api_key=${process.env.ABSTRACT_API_KEY}&phone=${encodeURIComponent(value)}`
const res = await fetch(url)
const data = await res.json()
return data.valid === true
},
{ message: 'Phone number is invalid or inactive' }
)
// usage: await phoneSchema.parseAsync(req.body.phone)
The API returns a detailed JSON object. For Zod's pass or fail logic, you only need the "valid":true field. Additional data includes format objects to normalize numbers, country and location details for geo-based rules, carrier information, and the line type to distinguish between mobile and landline numbers.
Final Thoughts
Traditional validation with static regular expressions or large libraries only checks syntax. This approach fails to confirm if a number is active and creates a maintenance burden. Abstract API solves these problems with a cloud-based system that provides real-time data for over 195 countries.
To reliably validate user phone numbers, create a free account on Abstract API and get your API key.
Validate Phone Numbers with Abstract API
Validate phone numbers in your Zod schemas to collect accurate information from your users.
Get started for free