Sending transactional email from NestJS, the right way
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.