Back to Blog

Mailgun with Astro

Posted on by Patrick
Mailgun with Astro

To integrate Mailgun with Astro.js, there are a few packages which need to be installed first:

Mailgun.js

(https://www.npmjs.com/package/mailgun.js) is a javascript sdk for Mailgun built with webpack, babel & es6.

> npm install mailgun.js

 

Form Data

(https://www.npmjs.com/package/form-data) which is a a library to create readable “multipart/form-data” streams. Can be used to submit forms and file uploads to other web applications. To install run:

> npm install form-data

 

Once the packages have been added, then create a new API route under src > pages > contact.ts and with that file, paste the following:

import type { APIRoute } from 'astro';
import formData from 'form-data';
import Mailgun from 'mailgun.js';

const mailgun = new Mailgun(formData);
const mg = mailgun.client({
  key: 'key-0000000000000000',
  username: 'api',
  url: 'https://api.eu.mailgun.net'
});

export const POST: APIRoute = async ({ request }) => {
  try {
    const formData = await request.formData();
    const name = formData.get('name');
    const message = formData.get('message');

    if (!name || !message) {
      return new Response('Name and message are required.', { status: 400 });
    }

    const emailData = {
      from: 'noreply@mg.domain.com',
      to: 'recipient@domain.com',
      subject: `Contact Form Submission from ${name}`,
      text: message as string,
    };

    await mg.messages.create('mailgun-domain.com', emailData);

    return new Response('Message sent successfully!', { status: 200 });
  } catch (error) {
    console.error(error);
    return new Response('Failed to send message.', { status: 500 });
  }
};

// Optional GET handler to handle the case when the endpoint is accessed with GET
export const GET: APIRoute = async () => {
  return new Response('This endpoint only supports POST requests.', { status: 405 });
};

This assumes you replace the API key, Mailgun region and the mailgun-domain.

Then, within your page, just add your form eg:

<form action="/api/contact" method="POST">
  <label for="name">Name:</label>
  <input type="text" id="name" name="name" required />
    
  <label for="message">Message:</label>
  <textarea id="message" name="message" required></textarea>
    
  <button type="submit">Submit</button>
</form>

Noting the action url /api/contact as this may change dependent on the API route created.

Final addition

Ensure Server Side Rendering is enabled within the astro.config.mjs file using :

> output: 'server'

An example config shown below:

export default defineConfig({
  output: 'server',
});
©2026 - Wurkhouse | All Rights Reserved