Build a Threaded Email Inbox View with the Nylas Email API

Learn how to use the Nylas Email API to build a threaded email inbox view that let’s your users easily access their email data from within your app.

Ben Lloyd Pearson | August 21, 2020

It can be easy to think about email as being similar to physical mail, with independent messages that are delivered as self-contained packages. But, in reality, a single message is usually part of a larger conversation, and the other messages in those conversations play an important role to help users understand the greater context behind the messages. 

If you’re a developer who wants to integrate content from your user’s email inboxes into your app, you need to take this context into consideration to build proper groupings of emails that provide your users with a more cohesive, natural view of their email conversations. Fortunately, The Nylas Email API abstracts away the complexity of grouping email conversations into threads, so you can spend less time dealing with complex email data and more time building the features your users love. 

Want a PDF of this article?

Share it with a friend or save it for later reading.

The Nylas Platform provides a single point of integration that instantly connects 100% of email, calendar and contacts providers to your app, via simple REST APIs. This article will show you how to use Nylas to build a threaded email conversation view.

Getting Started with Inbox Zero

To demonstrate how to build a threaded email view, we’ve built Inbox Zero, a demo application you can run to explore and understand the Nylas Email API. It’s built using:

  • Next.js, a framework for making server-rendered React apps,
  • Express.js to create RESTful endpoints on the server,
  • JSON Web Tokens (JWT) for authentication and session management,
  • Lowdb, a lightweight key-value database powered by Lodash, for storing user information, and
  • The Nylas Node SDK, a client helper library that makes it easy to leverage the Nylas Platform.

The Inbox Zero Homepage

Threaded Email Views

Before we get into building anything, it’s important to understand how Nylas treats emails. The Nylas Email API provide two fundamental components for working with emails:

  • Messages are individual emails that serve as the basic component in an email interaction. 
  • Threads are a series of messages that have been sent in the same email conversation. Nylas takes care of the hard work to figure out which messages belong to which threads, making it easier for you to get the emails your user needs.

In Inbox Zero, the primary inbox view shows basic information about each thread, including the subject, last message date, and the most recent sender in the conversation. If the user clicks on one of these thread, it will expand to show the latest email in the thread.

Threaded Inbox View with Inbox Zero

Let’s get started building!

Create a Paginated List of the Latest Email Threads

First, we need to generate a list of threads from the user’s email account. The focus of Inbox Zero is to help users manage the unread emails in their inbox, so we only care about messages that are both in their inbox and marked as unread. The Nylas Email API provides filters that let you control the data that is returned, and the Inbox Zero app filters the request based on the in and unread attributes.

const PAGE_LIMIT = 6;
const page = req.query.page >= 1 ? req.query.page : 1;
const threads = await nylas.threads.list({
        in: "inbox",
        unread: true,
        view: "expanded",
        limit: PAGE_LIMIT + 1,
        offset: (page - 1) * PAGE_LIMIT
});

You might be wondering what limit, offset, and the page references in this example are. When users view their inbox, they’re most likely looking for their recent messages. If the user has hundreds, or thousands of unread messages in their inbox, they would have to wait for all of those messages to transfer from the API server before they can see their mail, even if they only want to look at the first one. This would create a poor user experience to say the least. 

So, to keep apps performant and more useful, it’s a good idea to limit downloads to the smallest amount of information that’s useful to the user. One technique that can be used to accomplish this is pagination: breaking apart the total dataset into chunks that are of a more manageable size. You can think of paging just like pages in a book, you wouldn’t want one giant sheet of paper with all the words on it, so they’re split up over a series of pages for easier digestion.

The limit is the maximum number of thread objects that should be returned. The offset is how far down the list of threads we want to pull from. So, for example, let’s say we want the user to see 6 threads at a time. The first request would have an offset of 0 and a limit of 6. When the user clicks to the next page, the app would update the offset to 6 since it has already displayed the first group of threads. The limit would remain 6 to continue displaying the same number of messages.

An important aspect of pagination is making sure the user can actually navigate to the next page! However, we only want to display the next page button if there are additional threads to display, so the app needs to make a second request to the Nylas Email API to determine if there is another page to display. If so, the next page button is displayed.

Now, we have everything necessary to display a page of threads to the user! 

res.send({
        hasPrevious: page > 1,
        hasNext: threads.length > PAGE_LIMIT,
        threads: threads.slice(0, PAGE_LIMIT).map(thread => ({
                id: thread.id,
                subject: thread.subject,
                from: thread.from,
                date: thread.lastMessageTimestamp,
                snippet: thread.snippet,
                unread: thread.unread,
                hasAttachments: thread.hasAttachments
        }))
});

Display an Entire Email Conversation

Now that the user has a list of their threads, they’re probably going to want to read some of them.  When the user clicks on one of the threads that was retrieved in the previous section, the thread ID is used to redirect them to that individual thread’s route. From there, we can use the Nylas SDK to get the contents of the individual messages. Inbox Zero starts by fetching the full thread data:

const id = req.params.id;
const thread = await nylas.threads.find(id, null, { view: "expanded" });

Line two of this example shows how to request a specific email thread using the expanded view query parameter.  With a normal view, Nylas returns a message_ids attribute that lists the ids for all of the messages that are a part of the conversation. When you request an expanded view, Nylas replaces this attribute with a messages attribute that provides a list of message objects that include everything except for the email body. 

This makes it possible for us to get the message’s subject, snippet, participants, and more without needing to make separate requests to the Nylas Email API, and it’s helpful in this situation because we don’t necessarily care about displaying all of the details for each of the historical messages in the conversation unless the reader clicks to expand one. When it’s time to render the individual email messages, the app makes a request to the Nylas Messages endpoint to get the entire message object.

const messages = await nylas.messages.list({ thread_id: id, view: "expanded" });

One last feature we want the user to have is the ability to navigate between email threads, so we should create previous and next buttons to do exactly that. To accomplish this, the Inbox Zero app gets the IDs for the two chronological email threads by making a request to the Nylas Threads endpoint using filters that are based on the timestamp for the current thread. Once it has identified the appropriate threads, it creates two buttons that let the user navigate to the route for the specific thread IDs. 

const [previousThreadIds, nextThreadIds] = await Promise.all([
        nylas.threads.list({
                in: "inbox",
                unread: true,
                last_message_after: thread.lastMessageTimestamp,
                limit: 1000,
                view: "ids"
        }),
        nylas.threads.list({
                in: "inbox",
                unread: true,
                last_message_before: thread.lastMessageTimestamp,
                limit: 1,
                view: "ids"
        })
]);
const previousThreadId = previousThreadIds.length > 0 ? previousThreadIds[previousThreadIds.length - 1] : null;
const nextThreadId = nextThreadIds.length > 0 ? nextThreadIds[0] : null;

The very last step is to send all of the data to be rendered in the front end.

return res.status(200).json({
        id,
        subject: thread.subject,
        from: threadFrom,
        messages: messages.map(simplifyMessage),
        date: thread.lastMessageTimestamp,
        snippet: thread.snippet,
        hasAttachments: thread.hasAttachments,
        unread: thread.unread,
        senderUnread,
        previousThreadId,
        nextThreadId
});

View Download File Attachments

Of course, no inbox app would be complete without the ability to download file attachments. Each of the message objects has an attribute named files that contains a list of IDs that represent file attachments. The Inbox Zero app uses these IDs to make requests to the Nylas Files endpoint to get the file name for display, and to provide the files for the user to download if they choose. 

const id = req.query.id;
const file = await req.nylas.files.find(id);
const fileData = await file.download();
res.send(fileData.body);

Extend Your Email Functionality With Nylas

This article outlines what it takes to build a functional threaded view of a user’s email inbox, but why stop there? With Nylas, developers also unlock the full capability of users’ calendars and contact books too, and these combined with email functionality allow you to turn your app into a scheduling powerhouse. With Nylas, you can build deeply contextual experiences that let your users access their email, calendar, and contacts seamlessly from within your app. Get started today!

Ben Lloyd Pearson

Ben was the Developer Advocate for Nylas. He is a triathlete, musician, avid gamer, and loves to seek out the best breakfast tacos in Austin, Texas.