Express
A route, a shared client, and the error handling a production service needs.
Install
npm install express @emails.sh/sdk dotenv# .env. The key comes from https://emails.sh/dashboard/api-keys.
EMAILSSH_API_KEY=esh_your_key_hereThe server
// 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.