Search code examples
node.jsnodemaileramazon-seslocalstack

Node Localstack Nodemailer SES errors


Using Nodemailer, Localstack, and the Node AWS SDK to send an email I am getting various errors:

AWS_SES_ENDPOINT=localhost:4566

Error: connect ECONNREFUSED 127.0.0.1:80
    at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1161:16)

AWS_SES_ENDPOINT=127.0.0.1:4566

uncaughtException: Invalid URL
TypeError [ERR_INVALID_URL]: Invalid URL
    at new NodeError (node:internal/errors:371:5)
    at onParseError (node:internal/url:552:9)
    at new URL (node:internal/url:628:5)
    at parseUrl (/something/node_modules/@aws-sdk/url-parser/dist-cjs/index.js:6:60)
    at resolveEndpointsConfig (/something/node_modules/@aws-sdk/config-resolver/dist-cjs/endpointsConfig/resolveEndpointsConfig.js

AWS_SES_ENDPOINT=http://localhost:4578 and AWS_SES_ENDPOINT=http://127.0.0.1:4578

Error: read ECONNRESET
    at TCP.onStreamRead (node:internal/stream_base_commons:220:20

AWS_SES_ENDPOINT=http://localhost:4566 and AWS_SES_ENDPOINT=smtp://127.0.0.1:4566

UnknownError
    at deserializeAws_querySendRawEmailCommandError (/something/node_modules/@aws-sdk/client-ses/dist-cjs/protocols/Aws_query.js:2779:24)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at async /something/node_modules/@aws-sdk/middleware-serde/dist-cjs/deserializerMiddleware.js:7:24
    at async /something/node_modules/@aws-sdk/middleware-signing/dist-cjs/middleware.js:11:20
    at async StandardRetryStrategy.retry (/something/node_modules/@aws-sdk/middleware-retry/dist-cjs/StandardRetryStrategy.js:51:46)
    at async /something/node_modules/@aws-sdk/middleware-logger/dist-cjs/loggerMiddleware.js:6:22

AWS_SES_ENDPOINT=smtp://127.0.0.1:4578

Error [TimeoutError]: socket hang up
     at connResetException (node:internal/errors:691:14)
     at Socket.socketOnEnd (node:_http_client:471:23)
     at Socket.emit (node:events:402:35)
     at endReadableNT (node:internal/streams/readable:1343:12)
     at processTicksAndRejections (node:internal/process/task_queues:83:21) {
   code: 'ECONNRESET',

SES for nodemailer is set up like:

const sesConfig: SESClientConfig = {
    region: "us-east-1",
    endpoint: AWS_SES_ENDPOINT,
};

const ses = new SES(sesConfig);

Localstack is set up in docker-compose.yml like:

services:
  qatool-localstack:
    container_name: "my-localstack"
    image: localstack/localstack
    ports:
      - "127.0.0.1:4566:4566"
      - "127.0.0.1:4572:4572"
      - "127.0.0.1:4578:4578"
    environment:
      - SERVICES=sqs:4566,s3:4572,ses:4578
      - DEFAULT_REGION=us-east-1
      - DATA_DIR=${TMPDIR:-/tmp/}localstack/data
      - HOST_TMP_FOLDER=${TMPDIR:-/tmp/}localstack
    volumes:
      - "${TMPDIR:-/tmp}/localstack:/tmp/localstack"
      - "/var/run/docker.sock:/var/run/docker.sock"
      - './localstackSetup.sh:/docker-entrypoint-initaws.d/make-s3.sh'

nodemailer and aws ses versions:

    "@aws-sdk/client-ses": "^3.110.0",
    "nodemailer": "^6.7.5",

Solution

  • The first two errors for localhost:4566 and 127.0.0.1:4566 are because a protocol wasnt included. You can see AWS SES doesn't parse the config properly for example:

    ses.config.endpoint().then((val) => { console.log(JSON.stringify(val)); });
    

    gives:

    {"hostname":"","protocol":"localhost:","path":"4566"}
    

    Where localhost is the protocol instead of the hostname.

    The connections to port 4578 didn't work for me.

    Aafter doing (replace from-email):

    aws ses verify-email-identity --email-address from-email@from.com  --endpoint-url=http://localhost:4566
    

    These endpoints worked:

    http://127.0.0.1:4566
    http://localhost:4566
    smtp://localhost:4566
    smtp://127.0.0.1:4566
    

    You can verify the email was sent by going to :

    http://localhost:4566/_localstack/ses

    Localstack doesn't send the actual email in the free version.

    You can verify SES is running by going to:

    http://localhost:4566/health