К списку постов

Валидация данных из внешнего API

Как на frontend, так и на backend нам приходится работать с внешним API. Особенную боль доставляют внешние API, которые не привязаны к вашему релизному циклу и могут меняться как бог на душу положит. Поэтому хорошо иметь способ из валидировать без лапши кода.

Для примера, допустим мы стучимся в сервис, который выдаёт нам информацию о нашем подписчике - его email и подписан он или нет. Нам нужно для дальнейшей работы обеспечить наличие этих двух полей. Идём в тупую!

const getData = async () => {
    const { data } = await axios.get('https://my-api/1');
    if(!data.email) {
        throw new Error('Нет адреса почты');
    }
    if (!data.isSubscribed) {
        throw new Error('Нет информации о подписке');
    }
    return data;
}

В целом выглядит не очень страшно... пока у нас 2 поля. А представьте 10 или 20? А если будут вложенные структуры 🤯? К тому же мы тут ещё не проверили, что почта имеет корректный формат. Как бы хотелось иметь более декларативный способ обработки ошибок! И он есть: class-validator.

Если вы работали с NestJS, то наверняка с ним неявно сталкивались при написании pipes. Давайте его тут используем.

Шаг 1: опишем наш приходящий класс:

class ExternalData {
    constructor(data: ExternalData) {
        Object.assign(this, data);
    }

    email: string;
    isSubscribed: boolean;
}

Шаг 2: Теперь давайте докинем ему декораторов, которые будут описывать нужные данные:

import { IsBoolean, IsEmail, IsString } from 'class-validator';

class ExternalData {
    constructor(data: ExternalData) {
        Object.assign(this, data);
    }

    @IsString({ message: 'Нет адреса почты' })
    @IsEmail({}, { message: 'Неверный формат адреса почты'})
    email: string;

    @IsBoolean({ message: 'Нет информации о подписке'})
    isSubscribed: boolean;
}

В каждом из них мы можем прописать дополнительные опции, в том числе своё сообщение об ошибке, если оно необходимо. Мы фактически описали все наши данные по их формату и типу за 3 строчки код (класс бы нам всё равно нужен был в виде интерфейса для получения данных).

Конструктор класса нам будет необходим для быстрого создания инстанса с пришедшими данными.

Шаг 3: Magic!

import { validate } from 'class-validator';

const getData = async () => {
    const { data } = await axios.get('https://my-api/1');
    validate(new ExternalData(data)).then(errors => {
        if (errors.length > 0) {
            // Пришедшие данные не верны
        } else {
            // Всё верно!
        }
    });
}

В результате нам достаточно просто вызвать validate для инстанса класса и в errors мы получим список ошибок с текстом и указанием где именно оно произошло. Всё! Наш класс прошёл валидацию, и мы можем спокойно с ним работать. Если нет - отправляем ошибки интерации в лог или внешнюю систему, чтобы оповестить разработчиков, что злые разработчики стороннего API опять строят нам козни!

Подпишись на статьи

Всего 1 раз в месяц я буду высылать вам подборки свежих статей с сайта.

Если хотите получать материалы и статьи каждую неделю подпишитесь на канал в Telegram PurpleCode.