DEV Community

Cover image for Best way to store phone numbers in your app
Jai
Jai

Posted on

Best way to store phone numbers in your app

When storing phone numbers in the backend, it’s crucial to consider efficiency, standardization, validation, and internationalization. The best way to store phone numbers often depends on your application’s needs (e.g., international support, validation rules, format requirements).

Option 1: Storing the complete phone number with country code

This approach stores the full phone number, including the country code, in international E.164 format (which is the internationally recognized phone number format).

Why E.164 Format?

  • Standardization: E.164 is a globally accepted phone number format that ensures uniqueness. It includes the country code and removes any formatting characters (like spaces, hyphens, or parentheses), leaving only digits.
  • Consistency: By storing phone numbers in E.164 format, you avoid inconsistencies in formatting across different users and systems.

  • Validation: You can easily validate phone numbers using libraries such as libphonenumber (used by Google) to verify that the phone number is well-formed and valid for a specific country.

E.164 Format Example:

  • US: +18005555555
  • UK: +442079460958
  • India: +919167988354

How to Store:

Database Schema: Store the phone number as a single string field in E.164 format.
Field type: String or VARCHAR (depending on the database).
Example: VARCHAR(15) (E.164 allows a maximum of 15 digits).

Advantages:

  • Internationalization: It supports all global numbers, making your system easily adaptable to any country.
  • Simplified Logic: Since phone numbers are stored in a uniform format, you don’t need to deal with variations in format, validation, or extraction of country code.
  • No Redundant Data: You store one canonical format, reducing the risk of redundancy or inconsistent data.

Disadvantages:

  • User Experience: Users must input the full phone number with the country code, which may not be ideal for all use cases (e.g., local number-only input).
CREATE TABLE users (
  id INT PRIMARY KEY,
  phone_number VARCHAR(15) NOT NULL
);

Enter fullscreen mode Exit fullscreen mode

Option 2: Storing phone number with separate country code

In this approach, you split the phone number into two fields: one for the country code and one for the local number. This allows more flexibility for localized formatting and may be preferable if your application requires different phone number behaviors by region (e.g., validation rules, format display).

CREATE TABLE users (
  id INT PRIMARY KEY,
  country_code VARCHAR(5) NOT NULL,
  local_number VARCHAR(20) NOT NULL
);
Enter fullscreen mode Exit fullscreen mode

Advantages:

  • Localized input.
  • Easy formatting
  • Simplified parsing.

Disadvantages:

  • More Complex Logic: You may need extra logic for managing the country code and local number separately, particularly for validation or when reconstructing the number for display.
  • Potential Redundancy: If the user changes country codes or phone numbers frequently, you may run into issues with redundancy, especially when working with multiple entries across different systems.

Example of Best Practice (E.164):

Here’s a full example of handling phone numbers with E.164 format and libphonenumber for validation in Node.js:

  1. Install libphonenumber:
npm install google-libphonenumber
Enter fullscreen mode Exit fullscreen mode
  1. Example Code (Node.js/Express):
const { PhoneNumberUtil, PhoneNumberFormat } = require('google-libphonenumber');
const phoneUtil = PhoneNumberUtil.getInstance();

const validatePhoneNumber = (phoneNumber, countryCode) => {
  try {
    const number = phoneUtil.parseAndKeepRawInput(phoneNumber, countryCode);
    const isValid = phoneUtil.isValidNumber(number);
    const formattedNumber = phoneUtil.format(number, PhoneNumberFormat.E164);

    return {
      isValid,
      formattedNumber
    };
  } catch (error) {
    return { isValid: false, error: 'Invalid phone number' };
  }
};

// Example usage
const { isValid, formattedNumber } = validatePhoneNumber('800-555-5555', 'US');
console.log(isValid, formattedNumber);  // true, +18005555555
Enter fullscreen mode Exit fullscreen mode

Retrieve country code from a phone number

To retrieve the country code (like US for United States) from a phone number using this library, you can use the getRegionCodeForNumber function. This function takes a phone number (which must be parsed using parseAndKeepRawInput or similar) and returns the country code in the ISO 3166-1 alpha-2 format (e.g., IN, US, GB, etc.).

Working example -

const { PhoneNumberUtil } = require('google-libphonenumber');
const phoneUtil = PhoneNumberUtil.getInstance();

// Function to get country code (ISO 3166-1 alpha-2)
const getCountryCodeFromPhoneNumber = (phoneNumber) => {
  try {
    // Parse the phone number and get the region (country code)
    const number = phoneUtil.parseAndKeepRawInput(phoneNumber);
    const countryCode = phoneUtil.getRegionCodeForNumber(number);
    return countryCode;
  } catch (error) {
    console.error("Error parsing phone number:", error);
    return null;
  }
};

// Test with different phone numbers
const testPhoneNumbers = [
  "+919167988354", // India
  "+14155552671",  // USA
  "+447777123456", // UK
  "+81 90 1234 5678", // Japan
];

testPhoneNumbers.forEach((phoneNumber) => {
  const countryCode = getCountryCodeFromPhoneNumber(phoneNumber);
  console.log(`Phone number: ${phoneNumber}, Country Code: ${countryCode}`);
});

Enter fullscreen mode Exit fullscreen mode

Explanation:

  • parseAndKeepRawInput(phoneNumber):
    This method is used to parse the phone number and keep the raw input, meaning it can handle input in various formats (e.g., with spaces, dashes, or parentheses).

  • getRegionCodeForNumber(number):
    This function returns the country code (ISO 3166-1 alpha-2 code, such as IN, US, GB, etc.) associated with the parsed phone number.
    It does this by matching the number against its internal list of country-specific phone number patterns.

Expected Output

Phone number: +919167988354, Country Code: IN
Phone number: +14155552671, Country Code: US
Phone number: +447777123456, Country Code: GB
Phone number: +819012345678, Country Code: JP
Enter fullscreen mode Exit fullscreen mode

Important Considerations:

  • The phone number must be parsed correctly, and it should include the country code (+ followed by digits). If the phone number is in local format (without the country code), you will need to specify the default region (country) while parsing.

  • Default region: If the phone number doesn't contain a country code, you can provide a default country code using the parse method (e.g., phoneUtil.parse(phoneNumber, 'IN') for India).

Conclusion:

  • E.164 format is generally the best option for storing phone numbers in the backend because it’s universally recognized and simplifies validation and storage.
  • Storing the country code and local number separately can be useful in localized systems but adds complexity.
  • Use tools like libphonenumber to handle phone number validation, formatting, and parsing efficiently.

Top comments (0)