> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sendpost.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Express.js Quickstart

> Learn how to send emails with SendPost in your Express.js application with route handlers, middleware, and error handling

## Prerequisites

To get the most out of this guide, you'll need to:

* [**Create a SendPost Account**](https://app.sendpost.io/register)
* [**Node.js 14+**](https://nodejs.org/) and [**Express.js**](https://expressjs.com/)
* A **Sub-Account API Key** from your SendPost dashboard

<Info>
  You can find your API keys in your [SendPost Dashboard](https://app.sendpost.io). Make sure to use your Sub-Account API Key for sending emails.
</Info>

## 1. Install Dependencies

```shellscript npm theme={null}
npm install express sendpost-js-sdk dotenv
```

## 2. Environment Setup

Create a `.env` file in your project root:

```env theme={null}
SENDPOST_API_KEY=your_sub_account_api_key
SENDPOST_FROM_EMAIL=hello@playwithsendpost.io
SENDPOST_FROM_NAME=SendPost
PORT=3000
```

## 3. Basic Setup

Create `app.js` or `server.js`:

```javascript JavaScript theme={null}
require('dotenv').config();
const express = require('express');
const app = express();

app.use(express.json());
app.use(express.urlencoded({ extended: true }));

const PORT = process.env.PORT || 3000;

app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});
```

## 4. Create Email Service

Create `services/emailService.js`:

```javascript JavaScript theme={null}
const sendpost = require('sendpost-js-sdk');

const emailApi = new sendpost.EmailApi();

class EmailService {
  constructor() {
    this.apiKey = process.env.SENDPOST_API_KEY;
    this.fromEmail = process.env.SENDPOST_FROM_EMAIL;
    this.fromName = process.env.SENDPOST_FROM_NAME;
  }

  async sendEmail({
    to,
    subject,
    htmlBody,
    textBody,
    from,
    groups,
    trackOpens = true,
    trackClicks = true,
    customFields,
  }) {
    try {
      const emailMessage = new sendpost.EmailMessage();
      
      emailMessage.from = from || {
        email: this.fromEmail,
        name: this.fromName,
      };

      // Handle single email string or array of recipients
      if (typeof to === 'string') {
        emailMessage.to = [{ email: to }];
      } else {
        emailMessage.to = to.map(recipient => {
          if (typeof recipient === 'string') {
            return { email: recipient };
          }
          return recipient;
        });
      }

      emailMessage.subject = subject;
      emailMessage.htmlBody = htmlBody;
      emailMessage.textBody = textBody || htmlBody.replace(/<[^>]*>/g, '');
      emailMessage.trackOpens = trackOpens;
      emailMessage.trackClicks = trackClicks;

      if (groups) {
        emailMessage.groups = Array.isArray(groups) ? groups : [groups];
      }

      const opts = { emailMessage };
      const response = await emailApi.sendEmail(this.apiKey, opts);

      return {
        success: true,
        messageId: response.messageId,
        data: response,
      };
    } catch (error) {
      console.error('SendPost error:', error);
      return {
        success: false,
        error: error.message || 'Failed to send email',
        details: error,
      };
    }
  }
}

module.exports = new EmailService();
```

## 5. Send Your First Email

### Simple Route Handler

```javascript JavaScript theme={null}
const emailService = require('./services/emailService');

app.post('/api/send-email', async (req, res) => {
  try {
    const { to, subject, htmlBody, textBody } = req.body;

    if (!to || !subject || !htmlBody) {
      return res.status(400).json({
        success: false,
        error: 'Missing required fields: to, subject, htmlBody',
      });
    }

    const result = await emailService.sendEmail({
      to,
      subject,
      htmlBody,
      textBody,
    });

    if (result.success) {
      res.json({
        success: true,
        messageId: result.messageId,
      });
    } else {
      res.status(500).json({
        success: false,
        error: result.error,
      });
    }
  } catch (error) {
    res.status(500).json({
      success: false,
      error: error.message,
    });
  }
});
```

## 6. Common Use Cases

### Welcome Email Route

```javascript JavaScript theme={null}
app.post('/api/welcome-email', async (req, res) => {
  try {
    const { email, firstName } = req.body;

    const result = await emailService.sendEmail({
      to: {
        email,
        name: firstName,
        customFields: { firstName },
      },
      subject: 'Welcome {{firstName}}!',
      htmlBody: `
        <h1>Welcome, {{firstName}}!</h1>
        <p>Thank you for joining us. We're excited to have you on board.</p>
        <p>Get started by exploring our features:</p>
        <ul>
          <li>Feature 1</li>
          <li>Feature 2</li>
          <li>Feature 3</li>
        </ul>
        <p>If you have any questions, just reply to this email.</p>
        <p><a href="{{unsubscribe}}">Unsubscribe</a></p>
      `,
      textBody: `
        Welcome, {{firstName}}!
        
        Thank you for joining us. We're excited to have you on board.
        
        Get started by exploring our features:
        - Feature 1
        - Feature 2
        - Feature 3
        
        If you have any questions, just reply to this email.
        
        Unsubscribe: {{unsubscribe}}
      `,
      groups: ['welcome'],
    });

    if (result.success) {
      res.json({ success: true, messageId: result.messageId });
    } else {
      res.status(500).json({ success: false, error: result.error });
    }
  } catch (error) {
    res.status(500).json({ success: false, error: error.message });
  }
});
```

### Password Reset Email

```javascript JavaScript theme={null}
app.post('/api/password-reset', async (req, res) => {
  try {
    const { email, resetToken, firstName } = req.body;
    const resetUrl = `${process.env.APP_URL}/reset-password?token=${resetToken}`;

    const result = await emailService.sendEmail({
      to: {
        email,
        name: firstName,
        customFields: {
          firstName,
          resetUrl,
        },
      },
      subject: 'Reset Your Password',
      htmlBody: `
        <h2>Hello {{firstName}},</h2>
        <p>We received a request to reset your password. Click the button below to create a new password:</p>
        <p>
          <a href="{{resetUrl}}" style="background-color: #5750EC; color: white; padding: 12px 24px; text-decoration: none; border-radius: 4px; display: inline-block;">
            Reset Password
          </a>
        </p>
        <p>Or copy and paste this link into your browser:</p>
        <p>{{resetUrl}}</p>
        <p>This link will expire in 1 hour.</p>
        <p>If you didn't request this, please ignore this email.</p>
      `,
      textBody: `
        Hello {{firstName}},
        
        We received a request to reset your password. Click the link below:
        
        {{resetUrl}}
        
        This link will expire in 1 hour.
        
        If you didn't request this, please ignore this email.
      `,
      groups: ['password-reset'],
      trackClicks: true,
    });

    if (result.success) {
      res.json({ success: true, messageId: result.messageId });
    } else {
      res.status(500).json({ success: false, error: result.error });
    }
  } catch (error) {
    res.status(500).json({ success: false, error: error.message });
  }
});
```

### Order Confirmation Email

```javascript JavaScript theme={null}
app.post('/api/order-confirmation', async (req, res) => {
  try {
    const { email, firstName, orderId, items, total } = req.body;

    const result = await emailService.sendEmail({
      to: {
        email,
        name: firstName,
        customFields: {
          firstName,
          orderId,
          items,
          total: total.toFixed(2),
        },
      },
      subject: 'Order {{orderId}} Confirmed',
      htmlBody: `
        <h1>Thank you for your order, {{firstName}}!</h1>
        <p>Your order <strong>{{orderId}}</strong> has been confirmed.</p>
        
        <h2>Order Details</h2>
        <table style="width: 100%; border-collapse: collapse;">
          <thead>
            <tr style="background-color: #f5f5f5;">
              <th style="padding: 12px; text-align: left; border: 1px solid #ddd;">Item</th>
              <th style="padding: 12px; text-align: left; border: 1px solid #ddd;">Quantity</th>
              <th style="padding: 12px; text-align: right; border: 1px solid #ddd;">Price</th>
            </tr>
          </thead>
          <tbody>
            {{#each items}}
            <tr>
              <td style="padding: 12px; border: 1px solid #ddd;">{{name}}</td>
              <td style="padding: 12px; border: 1px solid #ddd;">{{quantity}}</td>
              <td style="padding: 12px; text-align: right; border: 1px solid #ddd;">${{price}}</td>
            </tr>
            {{/each}}
          </tbody>
          <tfoot>
            <tr>
              <td colspan="2" style="padding: 12px; text-align: right; border: 1px solid #ddd;"><strong>Total:</strong></td>
              <td style="padding: 12px; text-align: right; border: 1px solid #ddd;"><strong>${{total}}</strong></td>
            </tr>
          </tfoot>
        </table>
        
        <p>We'll send you a shipping confirmation once your order is on its way.</p>
        <p><a href="{{unsubscribe}}">Unsubscribe from order updates</a></p>
      `,
      groups: ['order-confirmation'],
    });

    if (result.success) {
      res.json({ success: true, messageId: result.messageId });
    } else {
      res.status(500).json({ success: false, error: result.error });
    }
  } catch (error) {
    res.status(500).json({ success: false, error: error.message });
  }
});
```

## 7. Using Middleware for Email Routes

Create `middleware/emailValidation.js`:

```javascript JavaScript theme={null}
const validateEmailRequest = (req, res, next) => {
  const { to, subject, htmlBody } = req.body;

  if (!to) {
    return res.status(400).json({
      success: false,
      error: 'Recipient email (to) is required',
    });
  }

  if (!subject) {
    return res.status(400).json({
      success: false,
      error: 'Email subject is required',
    });
  }

  if (!htmlBody) {
    return res.status(400).json({
      success: false,
      error: 'Email HTML body is required',
    });
  }

  // Validate email format
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  const emails = Array.isArray(to) ? to : [to];
  
  for (const email of emails) {
    const emailAddress = typeof email === 'string' ? email : email.email;
    if (!emailRegex.test(emailAddress)) {
      return res.status(400).json({
        success: false,
        error: `Invalid email format: ${emailAddress}`,
      });
    }
  }

  next();
};

module.exports = { validateEmailRequest };
```

Use the middleware:

```javascript JavaScript theme={null}
const { validateEmailRequest } = require('./middleware/emailValidation');

app.post('/api/send-email', validateEmailRequest, async (req, res) => {
  // ... email sending logic
});
```

## 8. Error Handling Middleware

```javascript JavaScript theme={null}
const errorHandler = (err, req, res, next) => {
  console.error('Error:', err);

  if (err.name === 'ValidationError') {
    return res.status(400).json({
      success: false,
      error: err.message,
    });
  }

  res.status(500).json({
    success: false,
    error: 'Internal server error',
    message: process.env.NODE_ENV === 'development' ? err.message : undefined,
  });
};

app.use(errorHandler);
```

## 9. Batch Email Sending

```javascript JavaScript theme={null}
app.post('/api/send-batch', async (req, res) => {
  try {
    const { recipients, subject, htmlBody, textBody } = req.body;

    if (!recipients || !Array.isArray(recipients) || recipients.length === 0) {
      return res.status(400).json({
        success: false,
        error: 'Recipients array is required',
      });
    }

    // SendPost supports up to 500 recipients per request
    if (recipients.length > 500) {
      return res.status(400).json({
        success: false,
        error: 'Maximum 500 recipients per request',
      });
    }

    const result = await emailService.sendEmail({
      to: recipients,
      subject,
      htmlBody,
      textBody,
    });

    if (result.success) {
      res.json({
        success: true,
        messageId: result.messageId,
        recipientsCount: recipients.length,
      });
    } else {
      res.status(500).json({
        success: false,
        error: result.error,
      });
    }
  } catch (error) {
    res.status(500).json({
      success: false,
      error: error.message,
    });
  }
});
```

<Warning>
  Make sure your sender email domain is verified in your SendPost account before sending emails.
</Warning>

## 10. Next Steps

Explore more examples and use cases:

<Columns cols={3}>
  <Card title="Express.js Documentation" icon="express" href="https://expressjs.com/">
    Learn more about Express.js framework
  </Card>

  <Card title="SendPost JavaScript SDK" icon="npm" href="https://www.npmjs.com/package/sendpost-js-sdk">
    View the official SDK package on npm
  </Card>

  <Card title="Quickstart Example" icon="github" href="https://github.com/sendpost/sendpost-express-example">
    View a complete working example on GitHub
  </Card>
</Columns>
