You can display a user’s Google Contacts in a JavaScript app without calling the Google People API directly. The safe pattern has three parts: the browser calls your own server, your server calls Nylas with a stored Google grant ID, and the browser renders only the contact fields it needs.
This keeps your Nylas API key out of client-side code and gives you one Contacts API shape that also works for Outlook and Exchange later.
Before you begin
Section titled “Before you begin”You’ll need:
- A Nylas application with a valid API key
- A Google grant created through hosted OAuth or another supported auth flow
- Google contact scopes on the connector, usually
https://www.googleapis.com/auth/contacts.readonly - A server route where your frontend can request contacts
For Google-specific source behavior, scopes, and quotas, see How to list Google contacts.
Create a server route for Google Contacts
Section titled “Create a server route for Google Contacts”Never call Nylas directly from browser JavaScript with your API key. Create a server route that reads the user’s stored grant ID, calls /v3/grants/{grant_id}/contacts, and returns a trimmed response to the browser.
The Express route below returns up to 50 saved Google contacts. It maps the full Nylas contact object into a small view model with id, name, email, company, and jobTitle.
import express from "express";import Nylas from "nylas";
const app = express();const nylas = new Nylas({ apiKey: process.env.NYLAS_API_KEY });
app.get("/api/google-contacts", async (req, res) => { const grantId = await getGrantIdForCurrentUser(req);
const response = await nylas.contacts.list({ identifier: grantId, queryParams: { limit: 50, source: "address_book", }, });
const contacts = response.data.map((contact) => ({ id: contact.id, name: [contact.givenName, contact.surname].filter(Boolean).join(" "), email: contact.emails?.[0]?.email ?? "", company: contact.companyName ?? "", jobTitle: contact.jobTitle ?? "", }));
res.json({ contacts });});If the user has more than 50 contacts, follow response.nextCursor and fetch another page with pageToken. For large address books, cache the result on your server and refresh it with contact webhooks instead of listing contacts on every page load.
Render contacts in browser JavaScript
Section titled “Render contacts in browser JavaScript”The browser calls your /api/google-contacts route, not Nylas. The example below renders a contact list with names and email addresses.
<ul id="contacts"></ul>
<script type="module"> const list = document.querySelector("#contacts"); const response = await fetch("/api/google-contacts"); const { contacts } = await response.json();
list.replaceChildren( ...contacts.map((contact) => { const item = document.createElement("li"); item.textContent = `${contact.name || contact.email} <${contact.email}>`; return item; }), );</script>For React, use the same route from an effect or data loader. Keep the grant lookup and API key on the server side.
import { useEffect, useState } from "react";
export function GoogleContactsList() { const [contacts, setContacts] = useState([]);
useEffect(() => { fetch("/api/google-contacts") .then((response) => response.json()) .then((body) => setContacts(body.contacts)); }, []);
return ( <ul> {contacts.map((contact) => ( <li key={contact.id}> <strong>{contact.name || contact.email}</strong> {contact.email && <span> {contact.email}</span>} </li> ))} </ul> );}Add search or autocomplete
Section titled “Add search or autocomplete”The Contacts API filters by email, phone_number, group, and source. It doesn’t have a fuzzy name-search parameter, so a JavaScript contact picker usually loads contacts once and filters them locally.
const searchContacts = (contacts, query) => { const term = query.trim().toLowerCase();
return contacts .filter((contact) => { const name = contact.name.toLowerCase(); const email = contact.email.toLowerCase(); return name.includes(term) || email.includes(term); }) .slice(0, 8);};For a production recipient picker, flatten each contact into one entry per email address, remove duplicate records by lowercase email, and rank frequent correspondents before alphabetical matches. The recipient autocomplete recipe covers that pattern.
Things to know about Google Contacts in JavaScript
Section titled “Things to know about Google Contacts in JavaScript”Keep API credentials off the client. Browser code should never hold your Nylas API key. Use a server route, server action, or edge function that authenticates the user and looks up the grant ID.
Use source=address_book for saved contacts. Google also has automatically created “other contacts” from Gmail. Those appear through the inbox source and need the contacts.other.readonly scope.
Expect sync delay. Google doesn’t offer modern push notifications for contact changes. Nylas polls Google contact changes about every 5 minutes, then emits contact webhooks.
Load profile pictures on demand. Contact lists can include many photos. Render names and email addresses first, then fetch profile images only when the row is visible.
What’s next
Section titled “What’s next”- How to list Google contacts for Google-specific scopes, sources, and quotas
- Build recipient autocomplete for a production To-field picker
- Search contacts for exact filters and local name search
- Contacts API reference for every parameter and response field