Criando serviço de envio de e-mail com ELIXIR!🔮

Elixir é uma linguagem de programação incrível e nesse post eu resolvi criar um pequeno serviço de e-mail para falar um pouco dela.

Criando serviço de envio de e-mail com ELIXIR!🔮
Photo by Kai Oberhäuser / Unsplash

Que tal criar um mini servidor HTTP para envio de e-mail SMTP com o Elixir?
Nesse post vou demonstrar como criar um super simples, pequeno e muito leve. Também vamos ver como fazer envio de e-mail quando o usuário acessar uma determinada rota do servidor.

Iniciando

Primeiro precisamos preparar nosso ambiente, caso você ainda não tenha o Elixir no seu ambiente, basta seguir esse guia oficial que é simples e bem detalhado.
Com o Elixir instalado, vamos criar um novo projeto executando o seguinte comando no terminal:

mix new email --sup

Desta forma vamos criar um novo projeto utilizando o Mix, que é uma ferramenta que vem junto com o Elixir para criar, testar e compilar códigos em Elixir, além de gerenciar dependências.
O parâmetro --sup é para que possamos gerar uma aplicação com o esqueleto no padrão OTP.

Instalando dependências

Podemos navegar até o nosso projeto e abri-lo no editor de código para agora instalar as dependências que vamos usar, são elas:

  • plug_cowboy: Uma implementação do Cowboy para o Elixir.
  • poison: Biblioteca JSON para o Elixir.
  • plug: Uma especificação para componentes e adaptadores web (uma forma de padronizar o objeto de conexão).
  • bamboo: Envio de e-mail com Elixir.
  • bamboo_smtp: Adaptador para usarmos SMTP no bamboo.

Para isso, vamos abrir o arquivo mix.exs que fica na raiz do projeto e adicionar as seguintes linhas dentro do bloco deps:

{:plug, "~> 1.5"},
{:plug_cowboy, "~> 1.0"},
{:poison, "~> 3.1"},
{:bamboo, "~> 2.1.0"},
{:bamboo_smtp, "~> 4.0.1"}

Agora no terminal vamos instalar de fato as dependências(baixa-las), para isso execute o comando:

mix deps.get

Criando servidor HTTP

Com as dependências instaladas vamos começar configurar nosso servidor HTTP, para isso vamos configurar a inicialização dele, abra o arquivo lib/email/application.ex e adicione a seguinte linha dentro da lista children:

Plug.Adapters.Cowboy.child_spec(scheme: :http, plug: Email.Router, options: [port: 8085])

Pela ordem de parâmetros:

  • Schema é o protocolo que vamos utilizar: HTTP(tcp) ou HTTPS(ssl).
  • plug é para que possamos informar um Plug personalizado, nesse caso será o nosso router (eu recomendo essa leitura aqui para ficar mais claro).
  • Options são opções que podemos passar para o servidor, neste caso usamos apenas a porta, você pode ver outras aqui.

Como especificamos nosso modulo Email.Router, precisamos cria-lo agora, para isso, dentro da pasta lib/email crie um arquivo com o nome email_router.ex e dentro dele vamos declarar o nosso modulo e rotas, dessa forma:

defmodule Email.Router do
  use Plug.Router
  use Plug.Debugger
  require Logger

plug(Plug.Logger, log: :debug)
plug(:match)
plug(:dispatch)

get "/" do
  send_resp(conn, 200, "Server rodando...")
end

match _ do
  send_resp(conn, 404, "Pagina não encontrada")
end

end

Após definir nosso modulo, utilizamos o use para injetar dois módulos no contexto do nosso router.
Com require Logger vamos importar alguns recursos do logger (exceto funções). Temos algumas chamadas plug, que basicamente vão configurar os módulos de log, match(para fazer o match das rotas de acordo com a requisição) e dispatch (processar a requisição após o match).

Na última parte do arquivo, temos as nossas rotas: uma GET na raiz que responde com status code 200 e uma mensagem, e a segunda que serve como um fallback, para caso a rota acessada não exista previamente na aplicação, renderizando um texto de "Pagina não encontrada".

Rodando servidor HTTP

Agora vamos compilar e rodar nosso servidor, para isso rode no seu terminal o seguinte comando:

 iex -S mix

Agora basta acessar o endereço: http://localhost:8085/ e vamos ver nosso servidor rodando:

Elixir http

Enviando e-mail

Para fazermos o envio do e-mail, vamos começar configurando nosso servidor SMTP, para esse tutorial, vou utilizar o Mailtrap. Os Emails enviados pelo Mailtrap no plano gratuito não vão chegar na caixa de entrada do email de destino informado (gmail, hotmail, etc), eles sempre irão para a caixa de entrada do Mailtrap.

Na raiz do nosso projeto vamos criar uma pasta config e um arquivo config.exs dentro dela, o module Config é utilizado para definirmos várias configurações do nosso aplicativo, além de configurações do iex também.
Nele vamos colocar nossa configuração de SMTP, o conteúdo ficará dessa forma:

use Mix.Config

config :email, Email.Mailer,
  adapter: Bamboo.SMTPAdapter,
  server: "SERVER",
  hostname: "SERVER",
  port: 2525,
  username: "USER", # or {:system, "SMTP_USERNAME"}
  password: "PASS", # or {:system, "SMTP_PASSWORD"}
  tls: :always, # can be `:always` or `:never`
  allowed_tls_versions: [:"tlsv1", :"tlsv1.1", :"tlsv1.2"], # or {:system, "ALLOWED_TLS_VERSIONS"} w/ comma seprated values (e.g. "tlsv1.1,tlsv1.2")
  ssl: false, # can be `true`
  retries: 1,
  no_mx_lookups: false, # can be `true`
  auth: :if_available # can be `:always`. If your smtp relay requires authentication set it to `:always`.

Basta preencher com suas informações e pronto (você pode ver um exemplo preenchido aqui).
Vamos precisar criar um modulo mailer passando o nome da nossa aplicação, para isso, crie um arquivo chamado mailer.exs dentro de lib/email e dentro dele vamos por:

defmodule Email.Mailer do
  use Bamboo.Mailer, otp_app: :email
end

Agora abra o arquivo email_router.ex e na rota de GET vamos adicionar o seguinte:

 import Bamboo.Email #no início do arquivo
 ...
 new_email()
 |> to("[email protected]")
 |> from("[email protected]")
 |> subject("Teste")
 |> text_body("Testando envio de email")
 |> Email.Mailer.deliver_now

Aqui o código começa a ficar um pouco "estranho" pra quem não tem contato com Elixir, mas vou explicar: Primeiro fazemos o import do modulo "Email" dentro de "Bamboo", dessa forma temos acesso as funções contidas no modulo.
Em seguida fazemos uma chamada a função new_email() que vem do modulo que importamos e começamos uma sequência de pipe. Utilizamos o pipe para pegar o resultado de uma expressão e lançar como primeiro argumento de uma outra. Esteticamente lembra o fluent pattern.

Agora, basta rodar nosso servidor novamente e acessarmos nossa rota GET e verificar o e-mail do Mail trap:

 iex -S mix

Nossa caixa de entrada no Mailtrap:

Mailtrap

Conclusão

Podemos melhorar ainda mais esse código, podemos por exemplo pegar os parâmetros de envio do e-mail (destinatário, mensagem e assunto) via GET ou POST, podemos também carregar as informações do SMTP através de variaveis de ambiente, deixando a aplicação mais simples de modificar (caso eu tenha mais de um abiente) e mais segura.
Espero que tenha gostado, já faz algum tempo que queria escrever sobre Elixir e foi super legal.
Aqui está o projeto finalizado.

Veja esse episódio super legal do Coffee Zone onde falamos de Elixir.