Search code examples
node.jsmeteorfetchmailgun

Problem converting from HTTP request to fetch in Meteor nodejs server using mailgun API to send message


This code, using the meteor request based package HTTP to call the mailgun api has worked for years:

HTTP.post(process.env.MAILGUN_API + '/messages', {
    auth: 'api:' + process.env.MAILGUN_API_KEY,
    params
}, function(error, result) {
    if (error) {
        standardServerError(error, 'while sending email to ' + params.to);
        // throw error;
    }
});

Meteor deprecated the HTTP / request package in Meteor 2.0. So I converted to fetch as follows

const response = await fetch(process.env.MAILGUN_API + '/messages', {
    method: 'POST',
    headers: {
        'Authorization': 'Basic ' + Buffer.from('api:' + process.env.MAILGUN_API_KEY).toString('base64'), 
        'Content-Type': 'application/json'
    },
    body: JSON.stringify(params)
});
return response;

Unfortunately it doesn't work. Gets a response code of 400 with statusText 'BAD REQUEST' The params are the same and the deprecated call works.

The answer is that when you call the mailgun api with http, some magic conversions happen behind the scenes. When using fetch, the magic does not happen. If I explicitly convert the JSON data to form data before making the mailgun request, then it works as expected.

let formData = multipartFormData(params);
const response = fetch(mailgunEndpoint + '/messages', {
            method: 'POST',
            headers: _.extend({
                'Authorization': 'Basic ' + Buffer.from(`${mailgunUser}:${mailgunAPIKey}`).toString('base64'),
            }, formData.headers),
            body: formData.content
        }).then(response => {
            if (traceSendEmail) {
                console.warn(`{status: ${response.status}, statusText: ${response.statusText}}`);
            }
            return response;
        }).catch(err => {
            standardServerError(err, 'while sending email to ' + params.to);
            return false;
        });

Solution

  • The answer is that when you call the api with http package, some magic conversions happen behind the scenes. When using fetch, the magic does not happen. You must explicitly convert the JSON data to form data before making the api request, then code works as expected.

    let formData = multipartFormData(params);
        const response = fetch(mailgunEndpoint + '/messages', {
                    'method': 'POST',
                    'headers': _.extend({
                        'Authorization': 'Basic ' + Buffer.from(`${mailgunUser}:${mailgunAPIKey}`).toString('base64'),
                    }, formData.headers),
                    'body': formData.content
                }).then(response => {
                    if (traceSendEmail) {
                        console.warn(`{status: ${response.status}, statusText: ${response.statusText}}`);
                    }
                    return response;
                }).catch(err => {
                    standardServerError(err, 'while sending email to ' + params.to);
                    return false;
                });