Guides
Last updated
July 25, 2025

5 Ways to Validate Phone Numbers in Joi

Nicolas Rios
Nicolas Rios
Table of Contents:
ON THIS PAGE
Get your free
phone validation
 API key now
stars rating
4.8 from 1,863 votes
See why the best developers build on Abstract
START FOR FREE
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.
No credit card required

Ensuring phone numbers are valid is a frequent task when building forms with Joi. We will walk through five different methods for phone number validation, providing working code for each. You will also learn about the drawbacks of common techniques and how Abstract API overcomes them.

How to Implement Phone Number Validation in Joi

Here are four different methods to validate phone numbers with Joi. Each approach has distinct features, and we provide the code to implement them in your project.

Regex-Only Schema

This method uses Joi’s native "pattern()" function, which runs fast and has zero runtime dependencies. You can define a regular expression to match a specific phone number format, like E.164.

If you need to accept multiple national formats, you can create several regex patterns. Then, you can wrap them in Joi’s "alternatives()" function to validate against any of them.

const Joi = require('joi');const e164 = /^\+?[1-9]\d{1,14}$/;           // accepts +15551234567 etc.const schema = Joi.string().pattern(e164).required();schema.validate('+15551234567'); // { value: '+15551234567' }  schema.validate('555-1234');     // ValidationError

Salesflare joi-phone-number Extension

The joi-phone-number extension is a wrapper around the "google-libphonenumber" library. It supports options like "defaultCountry" to assume a country code and "strict" mode to reject impossible numbers.

It also works well with Joi’s "convert" option. This feature can rewrite the phone number value to a desired canonical format, such as "e164", "international", "national", or "rfc3966", before it reaches your business logic.

const BaseJoi   = require('joi');  const extension = require('joi-phone-number');  const Joi       = BaseJoi.extend(extension);const schema = Joi.string().phoneNumber({ defaultCountry: 'US',                                          format: 'e164',   // output conversion                                          strict: true });  // reject impossible numbersschema.validate('415-555-1212');      // => '+14155551212'  schema.validate('+99 123');           // ValidationError

@tepez/joi-phone-number-extensions

This joi-phone-number-extensions package exposes more capabilities from the underlying "libphonenumber" library. It allows for more granular control, such as setting a "defaultRegion" or enforcing an explicit "region".

You can also filter for specific line types, like "MOBILE", and define a format for the normalized output. This extension provides a first-class Joi type, "Joi.phoneNumber()", which makes your schema’s intent clearer than a generic string validation.

const BaseJoi = require('joi');  const ext     = require('@tepez/joi-phone-number-extensions');  const Joi     = BaseJoi.extend(ext);const schema = Joi.phoneNumber()                   // dedicated type, not string()                 .defaultRegion('US')              // assume US if no ‘+’                 .region('US')                     // enforced country                 .type('MOBILE')                   // only mobile lines                 .format('E164');                  // normalised outputschema.validate('+1 541-754-3010');  // '+15417543010'

Hand-Rolled Custom Rule with libphonenumber-js

You can build a custom validation rule directly with a library like libphonenumber-js. This approach avoids a third-party Joi plugin, which means you own the conversion logic and the error messages.

This gives you full control over the implementation. You can choose the metadata size ("core", "min", or "max") and wrap the rule in "Joi.extend()" to create a reusable ".phone()" rule across your schemas.

const Joi = require('joi');  const { isValidNumber, parsePhoneNumber } = require('libphonenumber-js/max'); // use max metadataconst phoneSchema = Joi.string().custom((value, helpers) => {  const parsed = parsePhoneNumber(value, 'US'); // default region  if (!parsed || !parsed.isValid()) {    return helpers.error('any.invalid');  }  return parsed.number; // convert to E.164}, 'libphonenumber validation');phoneSchema.validate('(415) 555-1212'); // '+14155551212'

Challenges of Phone Number Validation in Joi

The previously discussed methods present significant challenges for robust validation. Problems range from outdated dependencies and complex rules to the inherent limits of offline structural checks.

  • Plugins like joi-phone-number often lag behind updates to the core library they wrap. This delay means new, valid phone number ranges get rejected, which turns legitimate user signups into validation errors.
  • Some extensions, like joi-phone-number, have historically bypassed proper checks. This oversight allows syntactically correct but non-existent numbers to pass validation, as the logic exists outside of Joi's primary test suite.
  • A regex-only schema struggles with diverse global formats. A single pattern cannot account for all national rules, prefixes, and lengths. This approach becomes unmaintainable and often accepts numbers that will never connect.
  • Joi validates structure, not status. It cannot check if a number is active, disposable, or disconnected. All offline methods, from regex to custom rules, fail to confirm if a number actually works.

Validate Phone Numbers with Abstract API
Add phone number validation to your Joi project to collect accurate user data from day one.
Get started for free

How Abstract API Handles Phone Number Validation in Joi

Abstract API addresses the core weaknesses of traditional Joi validation by adding a live verification layer that queries real-time telecom data sources.

  • It confirms if a number is active and dialable, rather than just check its format.
  • The API returns the current carrier and a precise line type, such as mobile, landline, or VOIP.
  • It uses live telecom data, so validation rules do not depend on static, outdated datasets.
  • This allows for complex business rules that simple pattern matches cannot support, like the rejection of VOIP numbers or the restriction of sign-ups to specific countries.

How to Bring Abstract API to Your Dev Environment

Once you possess familiarity with Abstract’s capabilities, the addition of its phone number validation API to your project is simple. The process requires just a few steps to get your environment ready.

  • Sign up at Abstract API and get the Phone Validation API key.
  • Install the necessary packages with the command below.
npm install @abstractapi/javascript-phone-validation joi
  • In your bootstrap code, import AbstractPhoneValidation and configure it with your key.
  • Create a Joi schema and embed an async custom validator that calls AbstractPhoneValidation.verify(phone).
  • Reject or transform the input based on the data.valid field and any extra rules you need.
  • Ship your code. Abstract keeps the data fresh via the API, so no extra maintenance occurs.

Sample Phone Number Validation Implementation with Abstract API

The code below shows a custom Joi validator. It calls Abstract API to check if a phone number is a valid US mobile number. If the number fails the check, the validator returns an error. Otherwise, it returns the original value.

const Joi = require('joi');
const { AbstractPhoneValidation } = require('@abstractapi/javascript-phone-validation');
AbstractPhoneValidation.configure(process.env.ABSTRACT_PHONE_KEY);

const phoneSchema = Joi.string().custom(async (value, helpers) => {
  const data = await AbstractPhoneValidation.verify(value);
  if (!data.valid || data.line_type !== 'mobile' || data.country_code !== 'US') {
    return helpers.error('any.invalid');
  }
  return value;
}, 'Abstract phone check');

await phoneSchema.validateAsync(req.body.phone);

The API response provides rich data for validation logic. The "valid" field confirms the number is active. The "format" field gives local and international versions. The "country" and "location" fields let you enforce geographic restrictions. The "type" field distinguishes mobile from landline or VOIP, and "carrier" identifies the current network owner. These fields allow Joi to perform real operational checks beyond simple pattern matches. A successful validation returns a JSON object like this:

{
  "phone": "14152007986",
  "valid": true,
  "format": {
    "international": "+14152007986",
    "local": "(415) 200-7986"
  },
  "country": { "code": "US", "name": "United States", "prefix": "+1" },
  "location": "California",
  "type": "mobile",
  "carrier": "T-Mobile USA, Inc."
}

Final Thoughts

Traditional validation methods only check a number’s format against static, often outdated, data. They cannot confirm if a number is active, identify its line type, or name its current carrier. Abstract API overcomes these limits with live data checks for truly reliable validation. Consider an account on Abstract API to get your free API key.

Validate Phone Numbers with Abstract API
Ensure you collect accurate user data by implementing phone number validation in your Joi schemas.
Get started for free

Related Articles

Phone Validation
key now
Get your free
stars rating
4.8 from 1,863 votes
See why the best developers build on Abstract
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.
No credit card required