# Express

A route, a shared client, and the error handling a production service needs.

### Install

Node 18 or newer:
```bash
npm install express @emails.sh/sdk dotenv
```

.env:
```bash
# .env. The key comes from https://emails.sh/dashboard/api-keys.
EMAILSSH_API_KEY=esh_your_key_here
```

### The server

server.js:
```ts
// server.js
import 'dotenv/config';
import express from 'express';
import { Emailssh } from '@emails.sh/sdk';

if (!process.env.EMAILSSH_API_KEY) {
  throw new Error('EMAILSSH_API_KEY is not set. Add it to .env.');
}

// One client for the process. It holds no connection state, so a module-level
// instance is correct and cheaper than one per request.
const mail = new Emailssh({ apiKey: process.env.EMAILSSH_API_KEY });
const app = express();
app.use(express.json());

app.post('/signup', async (req, res) => {
  const { email } = req.body;
  if (typeof email !== 'string' || !email.includes('@')) {
    return res.status(400).json({ error: 'Enter an email address.' });
  }

  try {
    const { id } = await mail.send({
      from: 'Acme <hello@acme.com>',
      to: [email],
      subject: 'Welcome to Acme',
      html: '<p>Confirm your address to finish signing up.</p>',
      text: 'Confirm your address to finish signing up.',
      idempotencyKey: `signup-${email}`
    });
    res.json({ id });
  } catch (err) {
    // Log the reason, return something the user can act on.
    console.error('emails.sh refused the send', err);
    res.status(502).json({ error: 'Could not send the confirmation email.' });
  }
});

app.listen(3000, () => console.log('listening on http://localhost:3000'));
```

If the send is not something the caller waits on, do not make them: write the request to your database first, answer, and send after. A user should not see a 502 because an email provider had a slow second.

---

Base URL: https://emails.sh/v1. Auth: `Authorization: Bearer esh_...`.
Whole API in one file: https://emails.sh/llms.txt. All documentation: https://emails.sh/docs.md.
