🪟 Windows TippsAndroid 17: Neue Version ist hier – Das ist alles neu(16.09.2026 um 11:40 Uhr)
🕵️ Hacking12 Best CASB Solutions Compared (2026): Features & Pricing(16.09.2026 um 09:31 Uhr)
🕵️ Hacking12 Best CIEM Tools Compared (2026): Features & Pricing(16.09.2026 um 09:37 Uhr)
🪟 Windows TippsAndroid 17: Neue Version ist hier – Das ist alles neu(16.09.2026 um 11:40 Uhr)
🕵️ Hacking12 Best CASB Solutions Compared (2026): Features & Pricing(16.09.2026 um 09:31 Uhr)
🕵️ Hacking12 Best CIEM Tools Compared (2026): Features & Pricing(16.09.2026 um 09:37 Uhr)

🔧 Programmierung 🕛 vor 2 Monaten 8 Min Lesezeit
0

SMTP outbound como ciudadano de primera clase: mandá emails sin pip install yagmail

↗ Quelle (dev.to)
🗣️ Stimme:
📑 Inhaltsübersicht

Notificaciones, password resets, magic links, alerts. Todos los lenguajes resuelven SMTP outbound con una librería externa. En Fitz, smtp.send(opts) es builtin del lenguaje. Async desde día uno. Paridad bit-a-bit fitz runfitz build. Sin pip install yagmail / npm install nodemailer / cargo add lettre / Maven JavaMail.







El detalle que se olvida



Cualquier aplicación seria termina necesitando mandar mails:




  • Notificación cuando se cae un servicio (incidents → email al on-call).

  • Password reset / magic-link auth (link único al email del user).

  • Welcome email después de un signup.

  • Reportes diarios / weekly digests despachados desde un cron job.

  • Alertas de jobs nocturnos que fallan.



Y todos los lenguajes lo resuelven con una librería externa.



Detectamos el gap construyendo fitzwatch (status page open-source escrito en Fitz puro): el corazón del producto avisa al on-call cuando un monitor cae. Sin SMTP nativo, la única opción era webhook → n8n/zapier → email. Dos sistemas externos para mandar un email.



Hoy, los 8 sub-bloques de la mini-tanda están en main. Una línea — smtp.send({...}).await? — y el email sale.






El stack típico de Python






CODE
pip install yagmail
# o usar smtplib del stdlib (RFC 5321 a mano)









CODE
# Con yagmail (sintaxis amigable):
import yagmail

yag = yagmail.SMTP("[email protected]", "password")
yag.send("[email protected]", "Subject", "Body text")

# Con smtplib stdlib (bajo nivel):
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart

msg = MIMEMultipart("alternative")
msg["Subject"] = "Subject"
msg["From"] = "[email protected]"
msg["To"] = "[email protected]"
msg.attach(MIMEText("Body text", "plain"))
msg.attach(MIMEText("<p>Body HTML</p>", "html"))

with smtplib.SMTP("smtp.gmail.com", 587) as server:
server.starttls()
server.login("[email protected]", "password")
server.send_message(msg)






Dos opciones, ambas requieren leerte la RFC o aprender la API de yagmail.






El stack típico de JS/Node






CODE
npm install nodemailer









CODE
import nodemailer from "nodemailer"

const transporter = nodemailer.createTransport({
host: "smtp.gmail.com",
port: 587,
secure: false,
auth: { user: "[email protected]", pass: "password" },
})

await transporter.sendMail({
from: "[email protected]",
to: "[email protected]",
subject: "Subject",
text: "Body text",
html: "<p>Body HTML</p>",
})






Una sola lib, bien diseñada, pero npm install agrega ~50 paquetes transitivos al package.json.






El stack típico de Rust






CODE
cargo add lettre
cargo add tokio --features full









CODE
use lettre::message::{header::ContentType, Mailbox, MultiPart, SinglePart};
use lettre::transport::smtp::authentication::Credentials;
use lettre::{AsyncSmtpTransport, AsyncTransport, Message, Tokio1Executor};

let creds = Credentials::new("[email protected]".into(), "password".into());
let mailer = AsyncSmtpTransport::<Tokio1Executor>::starttls_relay("smtp.gmail.com")
.unwrap()
.port(587)
.credentials(creds)
.build();

let email = Message::builder()
.from("[email protected]".parse::<Mailbox>().unwrap())
.to("[email protected]".parse::<Mailbox>().unwrap())
.subject("Subject")
.multipart(MultiPart::alternative()
.singlepart(SinglePart::builder().header(ContentType::TEXT_PLAIN).body("Body text".to_string()))
.singlepart(SinglePart::builder().header(ContentType::TEXT_HTML).body("<p>Body HTML</p>".to_string()))
)
.unwrap();

mailer.send(email).await.unwrap();






Funcional, pero verboso. 25 líneas para mandar UN mail.






El stack típico de Java






CODE
<dependency>
<groupId>com.sun.mail</groupId>
<artifactId>jakarta.mail</artifactId>
<version>2.0.1</version>
</dependency>






Y después la receta canónica de Properties + Session + MimeMessage + Transport.send(...) que probablemente sumás 40 líneas.






El stack de Fitz






CODE
let r = smtp.send({
"to": "[email protected]",
"from": "[email protected]",
"subject": "Subject",
"body_text": "Body text",
"body_html": "<p>Body HTML</p>",
}).await?






Que es esto.





  • smtp es builtin del lenguaje. No hay pip install/npm install/cargo add.


  • body_text + body_html juntos → multipart/alternative automático.


  • .await? propaga errores como Result::Err(Str). El compilador exige manejo.

  • Config (host, port, user, password, TLS) se lee de env vars al primer send. Lo mismo que cualquier app en Kubernetes/Docker espera.



r.delivered, r.message_id y r.duration_ms salen tipados como Bool/Str/Int.






Lo que smtp.send te garantiza






1. Cero deps externas



El binario fitz ya viene con lettre 0.11 linkeado estático adentro. Cuando hacés fitz build, el resultado es UN ejecutable de ~10 MB que tiene cliente SMTP, TLS via rustls, sin openssl en el host destino.




CODE
ldd ./mi-app
# linux-vdso.so.1
# libgcc_s.so.1
# libc.so.6
# (nada de smtp libs ni openssl)









2. Paridad bit-a-bit fitz runfitz build



El intérprete (fitz run) y el codegen a Rust (fitz build) emiten el mismo wire SMTP. Mismo handshake, mismo SCRAM auth, mismo Message-ID. Bug en un path = bug en el otro. Tests E2E lo validan en cada commit.






3. Async desde día uno



smtp.send(...) devuelve Future<Result<SmtpResult>>. Integra natural con el resto del stack:




CODE
@background
async fn send_welcome(email: Str) -> Null {
let _ = smtp.send({
"to": email,
"subject": "Bienvenido",
"body": "Hola, gracias por unirte.",
}).await
return null
}

@post("/signup")
fn signup(input: SignupInput) {
// ... crear el user en la DB ...
let _ = spawn(send_welcome(input.email))
return 201 { "id": new_user_id }
}






El handler responde 201 al cliente inmediato. El email se manda en otro task tokio. Si el SMTP server tarda 2 segundos, no le importa al cliente — ya respondió hace 2 segundos.



Y con @cron armás digests:




CODE
@cron("0 0 9 * * *")  // todos los días 09:00
async fn daily_digest() -> Null {
let r = smtp.send({
"to": "[email protected]",
"subject": "Daily digest",
"body_html": "<p>Hoy: ...</p>",
}).await
match r {
Ok(_) => log.info("digest.sent"),
Err(e) => log.error("digest.failed", error: e),
}
return null
}









4. Result<T> como modelo de error



Errores de transporte (DNS, auth, TLS, server reject) llegan como Result::Err(Str) con prefijo "smtp: ":




CODE
match smtp.send(opts).await {
Ok(r) => log.info("smtp.delivered", message_id: r.message_id),
Err(e) => {
// El prefijo te permite clasificar:
// "smtp: server rejected mail: ..." → 5xx del server
// "smtp: transient error: ..." → 4xx temporario
// "smtp: client error: ..." → TLS/auth fail
// "smtp: invalid `to` address ..." → parse error
log.warn("smtp.failed", error: e)
}
}






El checker estático exige manejo. Si tu fn retorna Result<...>, podés propagar con ?. Si no, te exige match. Cero excepciones runtime.






5. Magic-link auth en 30 líneas



El caso showcase del stack web first-class de Fitz: HTTP server-side + auth con jwt.encode + SMTP outbound, todo combinado en un binario.




CODE
type EmailRequest {
email: Str
}

@post("/auth/magic-link")
async fn magic_link(input: EmailRequest) -> Result<Str> {
let payload = { "email": input.email }
let token = jwt.encode(payload, "secret")
let link = "https://app.example.com/verify?t={token}"
let r = smtp.send({
"to": input.email,
"subject": "Tu link de login",
"body_text": "Hacé click acá:\n{link}\n\nExpira en 5 min.",
"body_html": "<p>Hacé click <a href=\"{link}\">acá</a>.</p>",
}).await?
return Ok(r.message_id)
}






Sin Auth0, sin Supabase, sin Stripe webhooks. Un binario, un puerto, un comando para deployarlo.






Setup local con MailHog



Para dev no querés mandar mails reales. ). Cap exhaustivo + 3 ejemplos runnable contra MailHog en la cada vez que cerramos una mini-tanda grande. La próxima cubrirá una feature concreta detectada durante fitzwatch.

Vollständiger Original-Artikel
Den kompletten Beitrag mit allen Details direkt auf dev.to lesen.
↗ Original-Artikel auf dev.to lesen
Wie bewertest du diesen Beitrag?
1 Klick Feedback
Teilen mit Netzwerk & Team:

Community-Analysen & Experten-Meinungen 0

Verfasse deine eigene Analyse, teile Workarounds oder diskutiere diesen Vorfall im Blog.
Noch keine Community-Analyse verfasst. Markiere einen Textabschnitt oder klicke oben auf Eigene Analyse verfassen“!
Community Pulse: Relevanz-Einschätzung
1 Klick Experten-Votum
🔴 Akute Relevanz 0%
🟡 In Evaluierung 0%
🟢 Keine Auswirkung 0%
Spannende Innovation 0%
Verwandte Story-Cluster & Quellen (Vektor-KI)
Port 8095 Engine
2 Quellen
CVE-2026-88255 | ZenHive mpp up to 0.16.1 Duplicate Submission Gate lib/mpp/replay.ex reserve_hash_atomic input validation (EUVD-2026-80256)
1 Quelle
Android 17: Neue Version ist hier – Das ist alles neu
1 Quelle
Die entscheidende Hürde: Xpeng will deutsch und nicht chinesisch sein
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten SMTP outbound como ciudadano de primera clase: mandá emails sin pip install yagmail

Thematisch verwandte Begriffe: SMTP, outbound, como, ciudadano · 6 Treffer

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...