Nuxt

A server route and runtimeConfig, which is where a private key belongs in Nuxt.

Nuxt splits configuration into public and private. Anything under runtimeConfig without a public key stays on the server, which is exactly what an API key needs.

Install

Nuxt 3 or 4
npm install @emails.sh/sdk
.env
# .env, gitignored by the Nuxt starter.
# The key comes from https://emails.sh/dashboard/api-keys.
NUXT_EMAILSSH_API_KEY=esh_your_key_here
nuxt.config.ts
// nuxt.config.ts
export default defineNuxtConfig({
  runtimeConfig: {
    // Server only. NUXT_EMAILSSH_API_KEY in the environment overrides this at
    // runtime, which is how it gets set in production.
    emailsshApiKey: ''
  }
});

The server route

server/api/send.post.ts
// server/api/send.post.ts
import { Emailssh } from '@emails.sh/sdk';

export default defineEventHandler(async (event) => {
  const { emailsshApiKey } = useRuntimeConfig(event);
  if (!emailsshApiKey) {
    throw createError({ statusCode: 500, statusMessage: 'NUXT_EMAILSSH_API_KEY is not set' });
  }

  const { to, subject, html } = await readBody(event);
  const mail = new Emailssh({ apiKey: emailsshApiKey });

  try {
    const { id } = await mail.send({ from: 'Acme <hello@acme.com>', to: [to], subject, html });
    return { id };
  } catch (err) {
    console.error('emails.sh refused the send', err);
    throw createError({ statusCode: 502, statusMessage: 'Could not send that email' });
  }
});

Calling it from a page

components/SubscribeForm.vue script
// In any component. The key never leaves the server; this is just a POST.
async function subscribe(address: string) {
  const { id } = await $fetch<{ id: string }>('/api/send', {
    method: 'POST',
    body: {
      to: address,
      subject: 'Confirm your subscription',
      html: '<p>Click the link in this email to confirm.</p>'
    }
  });
  return id;
}