Implementing Trash Management in Gmail with NestJS and TypeORM

Abdullah Irfan
3 min readNov 21, 2023

--

Image generated by Bing

This is eighth story of series Building a Robust Backend: A Comprehensive Guide Using NestJS, TypeORM, and Microservices. Our purpose is to build an email sync system for Gmail, oAuth2 for email accounts set, we can proceed with introducing features in our application. With basic functions of synchronization done, we need to perform trash and un-trash functions. Like other Email services, Gmail keeps its structure in the form of labels, i.e., when an Email is in inbox, it will have label of INBOX, when in trash, it will have label of TRASH.

Fortunately, the Gmail API’s Resource$Users class provides this functionality, and we just need to access it through messages object, provided it thread id and it will perform the respective operation. Below is the function performing this operation.

  private async updateGmailLabel(
userId: string,
threadId: string,
isTrash: boolean,
): Promise<void> {
const oAuth2Client = await this.prepareOAuthClient(userId);
if (!oAuth2Client) {
throw new Error('OAuth2 client initialization failed');
}

const gmail = google.gmail({ version: 'v1', auth: oAuth2Client });

if (isTrash) {
await gmail.users.messages.trash({ userId: 'me', id: threadId });
return;
}

await gmail.users.messages.untrash({ userId: 'me', id: threadId });
}

This is updating records in live Gmail service, but we need to update the record in our local DB as well, so for that we will update the record by thread_id and handle labels accordingly. The code is:

  private async updateThreadRecord(
threadId: string,
isTrash: boolean,
): Promise<void> {
const thread = await this.gmailThreadRepository.findOne({
where: { thread_id: threadId },
});
if (!thread) {
throw new Error('Thread not found');
}

const labelIds = new Set(thread.label_ids);
if (isTrash) {
labelIds.add('TRASH');
} else {
labelIds.delete('TRASH');
labelIds.add('INBOX');
}

await this.gmailThreadRepository.update(
{ thread_id: threadId },
{ label_ids: Array.from(labelIds) },
);
}

Now to access these functions we will define a main function that will perform respective trash and un-trash operations, the code for these operations will be simple function calls:

  async moveToTrash(
id: string,
threadId: string,
): Promise<ResponseMessageInterface> {
try {
await this.updateGmailLabel(id, threadId, true);
await this.updateThreadRecord(threadId, true);
return customMessage(HttpStatus.OK, MESSAGE.SUCCESS);
} catch (error) {
console.error('Error in update:', error);
customMessage(HttpStatus.BAD_REQUEST, MESSAGE.BAD_REQUEST);
}
}

async restoreFromTrash(
id: string,
threadId: string,
): Promise<ResponseMessageInterface> {
try {
await this.updateGmailLabel(id, threadId, false);
await this.updateThreadRecord(threadId, false);
return customMessage(HttpStatus.OK, MESSAGE.SUCCESS);
} catch (error) {
console.error('Error in update:', error);
customMessage(HttpStatus.BAD_REQUEST, MESSAGE.BAD_REQUEST);
}
}

And lastly, we need to define methods in controller to create routes for moving and restoring emails to/from trash. The code for controller is:

  @Get('trash/:account_id/:thread_id')
async moveToTrash(
@Param('account_id') account_id: string,
@Param('thread_id') thread_id: string,
) {
return await this.gmailAccountService.moveToTrash(account_id, thread_id);
}

@Get('un-trash/:account_id/:thread_id')
async restoreFromTrash(
@Param('account_id') account_id: string,
@Param('thread_id') thread_id: string,
) {
return await this.gmailAccountService.restoreFromTrash(
account_id,
thread_id,
);
}
Success response from methods

Now we have operational system to move mails to/from trash with local DB sync. In next story we will setup methods to mark emails as read/unread. As usual, this story code is available on GitHub in feature/move-from-to-trash-inbox branch. If you appreciate this work, please show your support by clapping for the story and giving star on repository.

Before we conclude, here’s a handy toolset you might want to check out: The Dev’s Tools. It’s not directly related to our tutorial, but we believe it’s worth your attention. The Dev’s Tools offers an expansive suite of utilities tailored for developers, content creators, and digital enthusiasts:

  • Image Tools: Compress single or multiple images efficiently, and craft custom QR codes effortlessly.
  • JSON Tools: Validate, compare, and ensure the integrity of your JSON data.
  • Text Tools: From comparing texts, shuffling letters, and cleaning up your content, to generating random numbers and passwords, this platform has got you covered.
  • URL Tools: Ensure safe web browsing with the URL encoder and decoder.
  • Time Tools: Calculate date ranges and convert between Unix timestamps and human-readable dates seamlessly.

It’s a treasure trove of digital utilities, so do give it a visit!

--

--