Envello
Tutorial

Sending transactional email from NestJS, the right way

Envello Team·2026-07-17·5 min read

NestJS's module system is a good fit for isolating an external API call like this: a dedicated EmailService, injected wherever a send is needed, keeps the HTTP details out of your controllers and business logic.

The service

A minimal EmailService using NestJS's built-in HttpModule (which wraps Axios) or plain fetch, either works fine:

  • @Injectable() class EmailService { async send(payload: SendEmailPayload) { const res = await fetch('https://api.envello.dev/emails', { method: 'POST', headers: { Authorization: `Bearer ${this.configService.get('ENVELLO_API_KEY')}`, 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); if (!res.ok) throw new EmailSendError(res.status); } }
  • Use NestJS's ConfigService for the API key rather than reading process.env directly, so it's validated and testable the same way the rest of your config is

Where dependency injection helps

Injecting EmailService into whatever module needs it (an AuthModule for password resets, an OrdersModule for confirmations) means you can mock it cleanly in tests, without needing to stub fetch globally or hit a real API in your test suite.

If you want the Node SDK instead

A Node/TypeScript SDK exists (@envello/sdk-node internally, the eventual public npm package name is still being finalized). Wrapping the SDK client inside the same EmailService pattern above works identically, you'd just swap the fetch call for the SDK's client.emails.send() method.

Envello

EU-hosted transactional email, done right by default.