Skip to main content

Parsing Emails

This guide covers the fundamentals of email parsing with postal-mime, including handling different email formats and structures.

Understanding Email Structure

An RFC822 email consists of headers and a body, separated by a blank line:

Header-Name: Header Value
Another-Header: Another Value

This is the email body.

Basic Parsing

The simplest approach uses the static parse method:

import PostalMime from 'postal-mime';

const email = await PostalMime.parse(rawEmailData);

Instance Method

You can also create an instance for parsing:

const parser = new PostalMime();
const email = await parser.parse(rawEmailData);
caution

Parser instances cannot be reused. Create a new instance for each email you parse.

Parsing Different Input Types

postal-mime accepts various input formats:

String Input

const rawEmail = `From: sender@example.com
To: recipient@example.com
Subject: Test Email

Hello, World!`;

const email = await PostalMime.parse(rawEmail);

ArrayBuffer / Uint8Array

// From file upload or fetch response
const response = await fetch('/path/to/email.eml');
const arrayBuffer = await response.arrayBuffer();
const email = await PostalMime.parse(arrayBuffer);

Blob (Browser)

// From file input
const fileInput = document.querySelector('input[type="file"]');
const file = fileInput.files[0];
const email = await PostalMime.parse(file);

Node.js Buffer

import { readFile } from 'fs/promises';

const buffer = await readFile('email.eml');
const email = await PostalMime.parse(buffer);

ReadableStream

const response = await fetch('/path/to/email.eml');
const email = await PostalMime.parse(response.body);

Working with Headers

All Headers

Access the complete list of headers:

email.headers.forEach(header => {
console.log(`${header.key}: ${header.value}`);
});

Finding Specific Headers

const contentType = email.headers.find(h => h.key === 'content-type');
console.log(contentType?.value);

Custom Headers

X-headers and other custom headers are available:

const xMailer = email.headers.find(h => h.key === 'x-mailer');
console.log(xMailer?.value);

Understanding Address Fields

Address fields like from, to, cc return structured objects:

// Single address
console.log(email.from);
// { name: "John Doe", address: "john@example.com" }

// Multiple recipients
console.log(email.to);
// [
// { name: "Alice", address: "alice@example.com" },
// { name: "Bob", address: "bob@example.com" }
// ]

Address Groups

Email addresses can be grouped:

// Parsing "Team: alice@example.com, bob@example.com;"
const addresses = email.to;

addresses.forEach(addr => {
if (addr.group) {
console.log(`Group: ${addr.name}`);
addr.group.forEach(member => {
console.log(` - ${member.address}`);
});
} else {
console.log(`Individual: ${addr.address}`);
}
});

Text and HTML Content

Multipart/Alternative Handling

When an email has both text and HTML versions, both are available:

console.log(email.text); // Plain text version
console.log(email.html); // HTML version

Automatic Conversion

If only one format exists, postal-mime automatically generates the other:

// Email with only HTML content
const htmlOnlyEmail = await PostalMime.parse(htmlEmail);
console.log(htmlOnlyEmail.html); // Original HTML
console.log(htmlOnlyEmail.text); // Automatically converted to plain text

// Email with only text content
const textOnlyEmail = await PostalMime.parse(textEmail);
console.log(textOnlyEmail.text); // Original text
console.log(textOnlyEmail.html); // Automatically converted to HTML

Handling Nested Emails

Emails can contain other emails (message/rfc822):

// By default, nested emails are parsed and their content is inlined
const email = await PostalMime.parse(forwardedEmail);
console.log(email.text); // Contains content from all nested levels

// To treat nested emails as attachments instead
const email = await PostalMime.parse(forwardedEmail, {
forceRfc822Attachments: true
});

// Find the nested email attachment
const nestedEmail = email.attachments.find(
att => att.mimeType === 'message/rfc822'
);

// Parse the nested email separately
const decoder = new TextDecoder();
const nestedContent = decoder.decode(nestedEmail.content);
const nested = await PostalMime.parse(nestedContent);

Character Encoding

postal-mime automatically handles various character encodings:

// UTF-8, ISO-8859-1, Windows-1252, etc. are automatically decoded
const email = await PostalMime.parse(emailWithJapaneseContent);
console.log(email.subject); // Correctly decoded Unicode text

MIME Encoded Words

Headers with encoded words (=?charset?encoding?text?=) are automatically decoded:

// Raw header: "Subject: =?UTF-8?B?44GT44KT44Gr44Gh44Gv?="
console.log(email.subject); // "こんにちは"

Date Handling

The date field is converted to ISO 8601 format when valid:

console.log(email.date);
// "2024-01-15T10:30:00.000Z"

// If the date is invalid, the original string is preserved

Error Handling

Handle parsing errors gracefully:

try {
const email = await PostalMime.parse(rawEmail);
} catch (error) {
if (error.message.includes('nesting depth')) {
console.error('Email has too many nested parts');
} else if (error.message.includes('header size')) {
console.error('Email headers are too large');
} else {
console.error('Failed to parse email:', error);
}
}

Complete Parsing Flow