Nick lazily jumps over the full stack

Как работать с API Mailerlite через Cloudflare Workers

Для своей формы рассылки я использую сервис Mailerlite. Когда у меня была форма рассылки, я использовал сервис Mailerlite. Чтобы подключить форму к сайту, можно использовать виджет на основе Javascript, который доступен в настройках формы. Однако на моём сайте, построенном на Vue.js, форма отображалась только при первой загрузке страницы. Ну а так как я только учу Vue и просто хочу делать что-то классное вместо того, чтобы копаться в несовместимостях, я решил сделать свою собственную форму. Ведь API к сервису доступен.

Для использования API необходим секретный ключ, который нельзя встраивать в статический сайт, поэтому мне нужна была серверная часть. Тут-то и появляются Cloudflare Workers. Просто представьте: вы загружаете кусок Javascript кода, который начинает принимать запросы по указанному адресу как если бы вы настроили свой сервер. Только код работает в глобальной сети сервиса Cloudflare, не требует никакой инфраструктуры и это всё бесплатно. Ну как такое не использовать!

Фронтенд

Форма рассылки — это компонент Vue, который для отправки имейла обращается по адресу /api/subscribe.

Бэкенд

Это код Javascript, который работает в среде Cloudflare Workers. Он мониторит POST запросы по адресу /api/subscribe и перенаправляет их в API сервиса Mailerlite.

addEventListener("fetch", event => {
    event.respondWith(handleRequest(event.request))
})

const corsHeaders = {
    'Access-Control-Allow-Origin': ALLOWED_ORIGIN,
    'Access-Control-Request-Method': 'POST, GET',
    'Access-Control-Request-Headers': '*',
}

async function handleRequest(req) {
    // OPTIONS request
    if (req.method === 'OPTIONS') {
        return new Response('OK', {headers: corsHeaders});
    }

    // POST /api/subscribe
    if (req.method === 'POST' && /api\/subscribe$/.test(req.url)) {
        // Parse request body as `FormData` instance
        let fdata = await req.json();
        const url = `https://api.mailerlite.com/api/v2/groups/${MAILERLITE_GROUP_ID}/subscribers`;

        const resp = await fetch(url, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'X-MailerLite-ApiKey': MAILERLITE_API_KEY
            },
            // cache: 'no-store',
            body: JSON.stringify({
                email: fdata.email || ''
            })
        });

        const data = await resp.json();

        return new Response(JSON.stringify(data), {
            headers: {
                'Content-Type': 'application/json',
                ...corsHeaders
            }
        });
    }

    // default response
    return new Response(JSON.stringify('hi'), {
        headers: {
            "Content-Type": "application/json",
            ...corsHeaders
        },
    });
}

Документация по Workers просто замечательная и хорошо объясняет, как это все работает. Кроме того, там есть множество примеров. Но некоторые неочевидные моменты упомянуть стоит:

  • Заголовки CORS необходимо добавлять к любому ответу из скрипта, в противном случае запрос типа OPTIONS работать не будет.
  • Кроме заголовков CORS, для обеспечения безопасности бэкенда хорошо подходит ограничение количества запросов в единицу времени, по крайней мере в данном случае. У Cloudflare есть и такая бесплатная опция.
  • Скрипт использует несколько переменных окружения, которые должны быть определены в проекте Workers:
    • ALLOWED_ORIGIN — домен, с которого форма рассылки шлет запросы.
    • MAILERLITE_GROUP_ID — идентификатор группы с подписчиками из аккаунта Mailerlite.
    • MAILERLITE_API_KEY — ключ API.

* * *

Теперь про фоновую картинку и про то, что я достиг в этот раз. Чтобы привнести что-то личное и сделать картинку по-настоящему уникальной, с помощью своего древнего планшета я написал "Dear Subscriber". Только вот поначалу результат был аховый. То ли это планшет очень старый, то ли что-то с драйверами, которые Windows ставит по умолчанию, то ли еще что-то, но линия дрожала и всё получалось каким-то угловатым. Но с помощью одной программы все-таки смог написать естественно. Это Lazy Nezumi: там совершенно дикое количество параметров и возможностей, но мне пригодился режим по умолчанию. Получилось действительно хорошо, на мой взгляд.

Посмотреть картинку: