Zod validation for phone numbers
Asked Answered
zod
C

5

19

I am new to using zod for valiadtion. I have read through the documentation but I can see anything on how to add a validation for a phone number.

I used the email one to make sure a user enters a correct email, but how do I do the same for a phone number.

Currently it looks like this:

contactNumber: z
    .string()
    .min(10, { message: 'Must be a valid mobile number' })
    .max(14, { message: 'Must be a valid mobile number' }),

I am just not sure this is correct. I have made it a string because I want the user to be able to put a + in front of the number if necessary for example: +27823456787. I don't want the user to be able to put any letters in the number example: 072r536e98, If the user puts letters I want an error message to say "Must be a valid number".

Can anyone maybe advise me on the correct why to do the validation?

Cavallaro answered 25/10, 2022 at 11:4 Comment(2)
You cannot be advised if you don't specify what format you are requiring for a phone number.Arteritis
Phone numbers are incredibly more complex than emails. For one, emails are global, while phone numbers formats vary for each region. So first come up with your "correct" format, then we can think about validation.Hallett
A
19

You could use a zod refinement to chain together a check from a library that is more capable of making that check. As was mentioned in the comments, the rules are complicated, and specific to the region.

There seems to be a validation from validator.js for example that could handle refining the string to a mobile phone number with isMobilePhone but you may want/need to have the user specify the country they are in.

An example of how this would work might be:

import { z } from "zod";
import validator from "validator";

const schema = z.object({
  name: z.string(),
  phone: z.string().refine(validator.isMobilePhone)
});

console.log(
  schema.safeParse({
    name: "test",
    phone: "+1-212-456-7890"
  })
); // Success

console.log(
  schema.safeParse({
    name: "test",
    phone: "2124567890"
  })
); // Success

console.log(
  schema.safeParse({
    name: "test",
    phone: "1234"
  })
); // Failure
Allowed answered 26/10, 2022 at 7:28 Comment(0)
N
18

you can use regex, it worked for me =D.

const phoneRegex = new RegExp(
  /^([+]?[\s0-9]+)?(\d{3}|[(]?[0-9]+[)])?([-]?[\s]?[0-9])+$/
);

const schema = z.object({
     phone: z.string().regex(phoneRegex, 'Invalid Number!'),
})
Nicky answered 27/4, 2023 at 19:44 Comment(2)
The phone number (ex.) "1" passes this regex. It wasn't what I was expecting for the question, which seemed to want a mininum number of characters (or perhaps digits). I think you should explain the regex.Carillonneur
@TylerCollier din't know this regex was this bad, thank you. But the idea was to show him that it is possible to use regex.Nicky
M
5

I use libphonenumber-js to enforce strict validation, and to convert all phone numbers to a consistent format.

import parsePhoneNumberFromString from 'libphonenumber-js';
import { z } from 'zod';

export const zPhone = z.string().transform((arg, ctx) => {
  const phone = parsePhoneNumberFromString(arg, {
    // set this to use a default country when the phone number omits country code
    defaultCountry: 'NG',
    
    // set to false to require that the whole string is exactly a phone number,
    // otherwise, it will search for a phone number anywhere within the string
    extract: false,
  });

  // when it's good
  if (phone && phone.isValid()) {
    return phone.number;
  }

  // when it's not
  ctx.addIssue({
    code: z.ZodIssueCode.custom,
    message: 'Invalid phone number',
  });
  return z.NEVER;
});
Monamonachal answered 23/2, 2024 at 8:27 Comment(0)
P
2

Here's a reusable phone() function that makes use of libphonenumber-js:

import { isValidPhoneNumber, parsePhoneNumber } from "libphonenumber-js";
import { z } from "zod";

function phone(schema: z.ZodString) {
    return schema
        .refine(isValidPhoneNumber, "Please specify a valid phone number (include the international prefix).")
        .transform((value) => parsePhoneNumber(value).number.toString());
}

const contactNumber = phone(z.string());
type ContactNumber = z.infer<typeof contactNumber>; // string

The transform part transforms a valid phone number entered by the user into a canonical format (since many valid phone number formats are accepted).

Polysepalous answered 5/6, 2024 at 14:53 Comment(0)
E
0

you can use this format

const schema user = z.object({
    mobile_number: z.string().refine((value) => /^[+]{1}(?:[0-9-()/.]\s?){6,15}[0-9]{1}$/.test(value)),
})
Encompass answered 28/2, 2024 at 17:13 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.