How to Send Email with Nim

Nim offers an smtp module, but it is a bit annoying to use out of the box. This blogpost hopes to be a mini-tutorial on the basics of how to use the smtp library and give developers best practices for handling outgoing email in ways that Google or iCloud will accept.

SMTP in a Nutshell

SMTP, or the Simple Mail Transfer Protocol is the backbone of how email works. It's a very simple line-based protocol, and there are wrappers for it in almost every programming language. Usage is pretty simple:

The client connects to the server

The client authenticates itself with the server

The client signals that it would like to create an outgoing message to the server

The client sends the raw contents of the message to the server

The client ends the message

The client disconnects

Unfortunately, the devil is truly in the details here. There are a few things that absolutely must be present in your emails in order for services like GMail to accept them. They are:

The From header specifying where the message was sent from

header specifying where the message was sent from The Mime-Version that your code is using (if you aren't sure, put 1.0 here)

here) The Content-Type that your code is sending to users (probably text/plain )

For a more complete example, let's create a Mailer type and a constructor:

# mailer.nim import asyncdispatch, logging, smtp, strformat, strutils type Mailer* = object address: string port: Port myAddress: string myName: string username: string password: string proc newMailer*(address, port, myAddress, myName, username, password: string): Mailer = result = Mailer( address: address, port: port.parseInt.Port, myAddress: myAddress, myName: myName, username: username, password: password, )

And let's write a mail method to send out email:

proc mail(m: Mailer, to, toName, subject, body: string) {.async.} = let toList = @[fmt"{toName} <{to}>"] msg = createMessage(subject, body, toList, @[], [ ("From", fmt"{m.myName} <{m.myAddress}"), ("MIME-Version", "1.0"), ("Content-Type", "text/plain"), ]) var client = newAsyncSmtp(useSsl = true) await client.connect(m.address, m.port) await client.auth(m.username, m.password) await client.sendMail(m.myAddress, toList, $msg) info "sent email to: ", to, " about: ", subject await client.close()

Breaking this down, you can clearly see the parts of the SMTP connection as I laid out before. The Mailer creates a new transient SMTP connection, authenticates with the remote server, sends the properly formatted email to the server and then closes the connection cleanly.

If you want to test this code, I suggest testing it with a freely available email provider that offers TLS/SSL-encrypted SMTP support. This also means that you need to compile this code with --define: ssl , so create config.nims and add the following:

--define: ssl

Here's a little wrapper using cligen:

when isMailModule: import cligen, os let smtpAddress = getEnv("SMTP_ADDRESS") smtpPort = getEnv("SMTP_PORT") smtpMyAddress = getEnv("SMTP_MY_ADDRESS") smtpMyName = getEnv("SMTP_MY_NAME") smtpUsername = getEnv("SMTP_USERNAME") smtpPassword = getEnv("SMTP_PASSWORD") proc sendAnEmail(to, toName, subject, body: string) = let m = newMailer(smtpAddress, smtpPort, smtpMyAddress, smtpMyName, smtpUsername, smtpPassword) waitFor m.mail(to, toName, subject, body) dispatch(sendAnEmail)

Usage is simple:

$ nim c -r mailer.nim --help Usage: sendAnEmail [required&optional-params] Options(opt-arg sep :|=|spc): -h, --help print this cligen-erated help --help-syntax advanced: prepend,plurals,.. -t=, --to= string REQUIRED set to --toName= string REQUIRED set toName -s=, --subject= string REQUIRED set subject -b=, --body= string REQUIRED set body

I hope this helps, this module is going to be used in my future post on how to create an application using Nim's Jester framework.

Share on Mastodon

This article was posted on M08 28 2019. Facts and circumstances may have changed since publication. Please contact me before jumping to conclusions if something seems wrong or unclear.

Series: howto

Tags: nim email

The art for Mara was drawn by Selicre.