Skip to content

feat!: upgrade to Lettermint Node SDK 3.0 - #13

Open
bjarn wants to merge 1 commit into
mainfrom
feat/lettermint-sdk-v3
Open

bjarn wants to merge 1 commit into
mainfrom
feat/lettermint-sdk-v3

Conversation

@bjarn

@bjarn bjarn commented Oct 4, 2026

Copy link
Copy Markdown

What changes

The module now depends on lettermint@^3.0.0 (SDK 2.x no longer gets fixes).

Unchanged for users: module options, runtime config keys and env vars (NUXT_LETTERMINT_API_KEY, NUXT_LETTERMINT_API_TOKEN, NUXT_LETTERMINT_BASE_URL, NUXT_LETTERMINT_TIMEOUT), the client useLettermint() composable, sendEmail() / sendEmails() signatures and payload mapping, the /api/lettermint/send request and response shapes, toLettermintFailure()'s { statusCode, message, data } shape, and LettermintPayloadError.

Sending is stateless end to end. sendEmail() / sendEmails() call emails.send() / emails.sendBatch() with the idempotency key passed per call. No message state lives on any client. v2 already built a fresh SDK instance per call to work around the 2.x shared builder; SDK 3 removes that hazard at the source. New tests send two concurrent messages with different recipients, bodies, tags, attachments and idempotency keys, answered in reverse order, both through sendEmail() with stubbed fetch and through the real Nitro endpoint against the mock API. Each request and each answer stays with its own message.

What users can see:

  • useLettermint() and useLettermintApi() (server) return the SDK 3 Lettermint client: emails instead of email, typed query objects, iterate(), and some team API methods moved (routes.list(projectId), team.members.list(), ...). Both clients carry every configured credential. Each part uses its own token, and the SDK never falls back to the other one.
  • useLettermintEmail() returns the immutable SDK 3 EmailBuilder. Chains work as before. Code that builds a message over several statements has to keep each setter's return value. .idempotencyKey(k).send() becomes .send({ idempotencyKey: k }), and attach() takes an object.
  • Error re-exports follow SDK 3: LettermintApiError, LettermintAuthenticationError, LettermintPermissionError, LettermintNotFoundError, LettermintConflictError, LettermintRateLimitError, LettermintServerError, LettermintConnectionError, LettermintUnexpectedResponseError, LettermintRedirectError and LettermintConfigError are new. LettermintValidationError (422) and LettermintTimeoutError keep their meaning. LettermintHttpRequestError stays as a deprecated alias of LettermintApiError. LettermintClientError (400-only) is removed because SDK 3 has no equivalent. Error properties are now status/body/code instead of statusCode/responseBody/errorType.
  • The SDK's own pre-send LettermintValidationError (for example malformed tags) is a different class from the module's 422 export with the same name. sendEmail()/sendEmails() turn it into LettermintPayloadError, so the endpoint now answers bad tags with 400. In v2 they hit a plain TypeError and got a 500.
  • Endpoint responses are otherwise unchanged: timeout is still 504 (the message text changed to SDK 3's "timed out after N ms"), unreachable API or non-JSON answer is still 502, and upstream 401/403 is still 502. toLettermintFailure() now also maps connection, invalid-response, redirect, pre-send-validation and config errors (it returned null for the first two in v2). A misconfigured client gets a generic 500 message and the detail goes to the log only.
  • SendEmailResponse is SDK 3's SendMailResponse, a union on status, so reading scheduled_at needs status === 'scheduled' first. MessageStatus/TlsPolicy are open enums.
  • Configured tokens are trimmed. SDK 3 rejects surrounding whitespace, while fetch used to strip it, so a trailing newline in an env file keeps working.
  • The module does not verify webhooks, so there was nothing to port. UPGRADE.md notes the async Webhook.verify(rawBody, headers) and the now-required X-Lettermint-Delivery header for apps that verify webhooks themselves.

Breaking for the module?

Yes, so this should be released as nuxt-lettermint 3.0.0. The module hands out SDK objects as they are (useLettermint(), useLettermintApi(), useLettermintEmail()) and re-exports SDK error classes and types, so the SDK's breaking changes reach users directly. The commit is feat!: with a BREAKING CHANGE: footer, so semantic-release will cut a major. package.json's version is not bumped, following the repo's convention. Apps that only use the config, sendEmail()/sendEmails(), the endpoint and the client composable should need no code changes beyond the SendEmailResponse narrowing. UPGRADE.md has a new "Upgrade to v3" section with before and after examples. The README covers the new error classes, the immutable builder and typed team-API queries.

Verification

  • npm ci + npm install lettermint@^3.0.0 (lockfile: 2.7.0 → 3.0.0)
  • npm run lint: clean
  • npm run dev:prepare && npm run test:types (module, tests and playground): clean
  • npm run test: 9 files, 137 tests passed. All tests use stubbed fetch or the local mock Lettermint server, so none reach the real API or send email.
  • npm run prepack (nuxt-module-build build): succeeds
  • npm run dev:build (playground Nitro build with SDK 3): succeeds with no bundler warnings

🤖 Generated with Claude Code

Move the module from lettermint@^2 to lettermint@^3.0.0.

sendEmail() and sendEmails() now go through the stateless emails.send()
and emails.sendBatch(), with the idempotency key passed per call, so no
message state lives on any client. useLettermint() and useLettermintApi()
return the SDK 3 client with every configured credential, and
useLettermintEmail() returns the immutable SDK 3 builder. Configured
tokens are trimmed, as fetch did for SDK 2.

The re-exported error classes follow SDK 3 (LettermintApiError and its
subclasses, LettermintConnectionError, LettermintConfigError, ...), and
toLettermintFailure() maps them onto the same { statusCode, message, data }
shape as before. Tags the SDK refuses become a LettermintPayloadError, so
the endpoint answers them with 400.

Tests cover concurrent sends end to end (through the endpoint and the
mock API) and never reach the real API.

BREAKING CHANGE: useLettermint() and useLettermintApi() return the SDK 3
client (`emails` instead of `email`, typed query objects, some team API
methods moved), useLettermintEmail() returns the immutable SDK 3 builder,
LettermintClientError is removed and SDK errors read `status`/`body`
instead of `statusCode`/`responseBody`, and SendEmailResponse is a union
on `status`. See UPGRADE.md.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant