Welcome to Part 14 of this series of guides on how to integrate Gmail with Angular and Spring Boot.

In this guide, you'll lose some weight.

Well, not you personally. But the response payload that you send back to the client.

You see, right now it's just too much. Too big. Too heavy.

So I'll show you how to put it on a diet.

Or you can just go straight to the code on GitHub.

The Story So Far

Thus far in the series, you have:

  • Created an OAuth2 client ID and secret on Google Cloud Console
  • Enabled the Gmail API via Google Cloud Console
  • Set the necessary properties in your Spring Boot application.properties file 
  • Set up a DataStore implementation with its associated factory
  • Set up a refresh listener
  • Created a utility class that handles the Google authorization code flow
  • Set up proper Gmail consent with the Google Cloud Platform
  • Created a controller that handles a request to get the Google authorization code flow URL
  • Used Postman to get the Google authorization code URL
  • Used that URL to authorize your application to access your Gmail inbox
  • Updated the controller so it handles a token request
  • Updated the utility class so it creates the credential from a token
  • Persisted that credential
  • Used Postman to retrieve the token
  • Learned why you didn't actually need to retrieve the token
  • Learned about the structure of the Message object
  • Learned about MIME
  • Created an Email type that gets sent back to the client
  • Wrote code that converts a Message object to an Email object
  • Instantiated the Gmail service
  • Used the Gmail service to retrieve just the messages you want to retrieve
  • Used the Gmail service to extracted full Message objects from very lightweight objects
  • Added a new endpoint to listen for inbox requests
  • Used that endpoint to send back the user's most recent emails
  • Learned how to disallow your application from accessing your inbox
  • Created a new component on the Angular side that shows emails from the user's inbox
  • Created a new service on the Angular side that retrieves emails from the Spring Boot microservice

And if you haven't done those things, Part 1 is waiting for you.

The Story Unfolding

In this guide, you will:

  • Eliminate the body from the payload that you send back to the client
  • Learn why you should eliminate the body from the payload that you send back to the client

And then you can go back to dancing a jig.

The Heavyweight Isn't a Champ

As it stands right now, you're sending back a huge response payload to the client.

Recall that the request gets the first 20 messages in the user's email inbox by default. That's not so bad in and of itself.

But for each one of those messages, you send back the whole body of the email in both plain text and HTML format (if both are available).

But maybe the user doesn't want to read all those emails. Why send back the bodies?

Instead, you should just send back the important info (sent date, from, subject, and a small snippet of the body) and let the user decide which emails he or she wants to read.

Then, you can retrieve the full bodies of the emails on an individual basis as the user requests them.

That seems much more efficient.

So let's get busy.

Make the Model More Beautiful

The first thing you need to do is add a new property to the Email class.

private String snippet;

Yep. That's all you need.

Be sure to add the associated getter and setter with it.

That's going to contain the first several words of the message body. That way, your users can get a "sneak peek" of the email contents before deciding whether or not to read the whole thing.

Fortunately, Google's Gmail API sends back the snippet with the message. So you don't have to parse the body to create the snippet yourself.

That's pretty cool.

Re-Surveying the Service

You also need to make a change to GmailService (formerly GmailUtil).

Start by updating the getEmail() method.

    private Email getEmail(Message message, boolean lightweight) {
        Email email = new Email();
 
        if (!lightweight) {
            setEmailBody(email, message);
        }
        
        email.setId(message.getId());
        email.setSnippet(message.getSnippet());
        
        setValuesFromHeaders(email, message);
        
        return email;
    }

That method now accepts a second parameter: lightweight. It's a boolean that tells the code whether or not to retrieve the body of the email message.

If the boolean is true, the method returns all the usual data for the email except the body.

If it's false, it returns the body as well.

You can see that the method only invokes the setEmailBody() method if the boolean is false. Here's what that method looks like:

    private void setEmailBody(Email email, Message message) {
        email.setHtml(getBody(message, "text/html"));
        email.setPlainText(getBody(message, "text/body"));
        
        if (StringUtils.isEmpty(email.getHtml()) && StringUtils.isEmpty(email.getPlainText())) {
            email.setPlainText(getData(message));
        }
    }

You've seen that code before. But it was in the getEmail() method in the old branch.

One more thing: don't miss the line in getEmail() where it sets the snippet. That's new, too.

Next, note the change to getSingleEmailMessageById(). It also accepts the lightweight boolean parameter:

    public Email getSingleEmailMessageById(String id, Gmail service, boolean lightweight) {
        try {
            Message retrievedMessage = service
                                        .users()
                                        .messages()
                                        .get("me", id)
                                        .setFormat(GmailFormat.FULL)
                                        .execute();
            
            Email email = getEmail(retrievedMessage, lightweight);
            return email;
        } catch (IOException ie) {
            LOG.error("Problem retrieving individual message!");
        }   
        
        return null;
    }

And that code invokes the getEmail() method that you just saw.

Finally, getInbox() invokes the getSingleEmailMessageById() method withe the lightweight parameter set to true.

    public List<Email> getInbox(Credential credential) throws IOException, GeneralSecurityException {
        Gmail service = new Gmail.Builder(GoogleNetHttpTransport.newTrustedTransport(), new GsonFactory(), credential)
                .setApplicationName(Constants.GMAIL_APPLICATION_NAME)
                .build();           
        
        List<Message> list = getMessageList(service);
        List<Email> emails = new ArrayList<Email>();
        
        if (list != null) {
            list.forEach(message -> {
                Email email = getSingleEmailMessageById(message.getId(), service, true);
                if (email != null) emails.add(email);
            });
        }
        
        return emails;
    }

That will return the inbox messages without the bodies. 

And that will substantially lighten the load sent back to the client.

No changes on the controller are necessary. Everything will just pass through.

You & The UI

Now that you've got the synopsis, it's time to do something with it.

Head back over to the Angular app's source code. Update the Email interface so it includes the snippet property.

export interface Email {
  html: string;
  plainText: string;
  subject: string;
  date: number;
  from: string;
  to: string;
  id: string;
  snippet: string;
}

Then, update this part of inbox.component.html.

        <ng-container matColumnDef="subject">
          <tr><th mat-header-cell *matHeaderCellDef mat-sort-header> Subject </th></tr>
          <tr><td mat-cell *matCellDef="let row" style="width:80%"> {{row.subject}} - {{row.snippet}} </td></tr>
        </ng-container>

In that <ng-container> element, you'll see that the code now adds a dash and the snippet after the subject.

That follows the pattern set forth by Gmail itself. If you go to the web interface of Gmail, you'll see that's exactly how the inbox displays your emails.

Hey, if it's good enough for Google, it's good enough for me.

Now save that. Fire up your Angular app and all your necessary downstream microservices.

Then, login to the app and select User and Email from the left-hand sidebar. You should see something that looks like this:

Once again, I've redacted some stuff because you don't need to know that much about what's going into my inbox.

But even if I didn't redact anything it would still look crappy. You've got some UI work in your future.

And I will help you with that in the next guide.

Wrapping It Up

You continue to make progress. Well done.

There's still a long way to go. But, as they say, the longest journey begins with the first step.

You've already taken several steps. And you're well on your way to reaching your destination.

Until you get there, though, have fun!

Photo by Anna Tarazevich from Pexels