/** * Gmail API - Node.js/TypeScript Example * * Complete working example showing all major operations. * Save this file and run: npx ts-node example-nodejs.ts */ import fs from "fs/promises"; import readline from "readline"; import { google, gmail_v1 } from "googleapis"; import { OAuth2Client } from "google-auth-library"; // Configuration const SCOPES = ["https://www.googleapis.com/auth/gmail.modify"]; const CREDENTIALS_PATH = "credentials.json"; const TOKEN_PATH = "token.json"; /** * Authenticate user and return Gmail service */ async function authenticate(): Promise { try { const credentialsContent = await fs.readFile(CREDENTIALS_PATH, "utf-8"); const credentials = JSON.parse(credentialsContent); const { client_id, client_secret, redirect_uris } = credentials.installed; const oauth2Client = new google.auth.OAuth2( client_id, client_secret, redirect_uris[0] ); // Check for cached token try { const tokenContent = await fs.readFile(TOKEN_PATH, "utf-8"); const token = JSON.parse(tokenContent); oauth2Client.setCredentials(token); // Refresh if expired if (token.expiry_date && token.expiry_date < Date.now()) { const newToken = await oauth2Client.refreshAccessToken(); oauth2Client.setCredentials(newToken.credentials); await fs.writeFile(TOKEN_PATH, JSON.stringify(newToken.credentials)); } console.log("āœ… Using cached credentials"); return google.gmail({ version: "v1", auth: oauth2Client }); } catch { // No token, need to authenticate console.log("āš ļø No cached token, initiating authentication..."); return authenticateUser(oauth2Client); } } catch (error) { console.error("āŒ Authentication failed:", error); throw error; } } /** * Get user authorization via browser */ async function authenticateUser( oauth2Client: OAuth2Client ): Promise { const authUrl = oauth2Client.generateAuthUrl({ access_type: "offline", scope: SCOPES, }); console.log(`\nšŸ”— Please visit this URL to authorize:\n${authUrl}\n`); const rl = readline.createInterface({ input: process.stdin, output: process.stdout, }); return new Promise((resolve, reject) => { rl.question("Enter the authorization code: ", async (code) => { rl.close(); try { const { tokens } = await oauth2Client.getToken(code); oauth2Client.setCredentials(tokens); await fs.writeFile(TOKEN_PATH, JSON.stringify(tokens)); console.log("āœ… Token saved"); resolve(google.gmail({ version: "v1", auth: oauth2Client })); } catch (error) { reject(error); } }); }); } /** * Get user profile */ async function getProfile(gmail: gmail_v1.Gmail) { console.log("\nšŸ“‹ Getting user profile..."); const response = await gmail.users.getProfile({ userId: "me" }); const data = response.data; console.log(` Email: ${data.emailAddress}`); console.log(` Total Messages: ${data.messagesTotal}`); console.log(` Total Threads: ${data.threadsTotal}`); return data; } /** * List all labels */ async function listLabels(gmail: gmail_v1.Gmail) { console.log("\nšŸ·ļø Listing labels..."); const response = await gmail.users.labels.list({ userId: "me" }); const labels = response.data.labels || []; labels.slice(0, 10).forEach((label) => { console.log(` - ${label.name} (ID: ${label.id})`); }); console.log(` ... and ${Math.max(0, labels.length - 10)} more`); return labels; } /** * List inbox messages */ async function listInboxMessages(gmail: gmail_v1.Gmail) { console.log("\nšŸ“§ Listing inbox messages (last 5)..."); const response = await gmail.users.messages.list({ userId: "me", q: "in:inbox", maxResults: 5, }); const messages = response.data.messages || []; for (const message of messages) { const msgDetails = await gmail.users.messages.get({ userId: "me", id: message.id!, format: "metadata", metadataHeaders: ["Subject", "From", "Date"], }); const headers = msgDetails.data.payload?.headers || []; const subject = headers.find((h) => h.name === "Subject")?.value || "No Subject"; const from = headers.find((h) => h.name === "From")?.value || "Unknown"; const date = headers.find((h) => h.name === "Date")?.value || "Unknown"; console.log(`\n From: ${from}`); console.log(` Subject: ${subject}`); console.log(` Date: ${date}`); } return messages; } /** * Search messages */ async function searchMessages(gmail: gmail_v1.Gmail, query: string) { console.log(`\nšŸ” Searching for: ${query}`); const response = await gmail.users.messages.list({ userId: "me", q: query, maxResults: 5, }); const messages = response.data.messages || []; console.log(` Found ${messages.length} messages`); return messages; } /** * Get full message details */ async function getFullMessage(gmail: gmail_v1.Gmail, messageId: string) { console.log(`\nšŸ“„ Getting full message: ${messageId}`); const response = await gmail.users.messages.get({ userId: "me", id: messageId, format: "full", }); const message = response.data; const headers = message.payload?.headers || []; const bodyData = message.payload?.body?.data || ""; const subject = headers.find((h) => h.name === "Subject")?.value || "No Subject"; const from = headers.find((h) => h.name === "From")?.value || "Unknown"; const to = headers.find((h) => h.name === "To")?.value || "Unknown"; let body = ""; if (bodyData) { body = Buffer.from(bodyData, "base64").toString(); } console.log(` Subject: ${subject}`); console.log(` From: ${from}`); console.log(` To: ${to}`); console.log(` Body length: ${body.length} chars`); return { subject, from, to, body: body.substring(0, 500), // First 500 chars }; } /** * Send a message */ async function sendMessage( gmail: gmail_v1.Gmail, to: string, subject: string, body: string ) { console.log(`\nāœ‰ļø Sending message to ${to}...`); const message = `From: me\r\n` + `To: ${to}\r\n` + `Subject: ${subject}\r\n` + `\r\n` + `${body}`; const encodedMessage = Buffer.from(message) .toString("base64") .replace(/\+/g, "-") .replace(/\//g, "_") .replace(/=+$/, ""); const response = await gmail.users.messages.send({ userId: "me", requestBody: { raw: encodedMessage, }, }); console.log(` āœ… Message sent with ID: ${response.data.id}`); return response.data; } /** * Create a draft */ async function createDraft( gmail: gmail_v1.Gmail, to: string, subject: string, body: string ) { console.log(`\nšŸ“ Creating draft for ${to}...`); const message = `From: me\r\n` + `To: ${to}\r\n` + `Subject: ${subject}\r\n` + `\r\n` + `${body}`; const encodedMessage = Buffer.from(message) .toString("base64") .replace(/\+/g, "-") .replace(/\//g, "_") .replace(/=+$/, ""); const response = await gmail.users.drafts.create({ userId: "me", requestBody: { message: { raw: encodedMessage, }, }, }); console.log(` āœ… Draft created with ID: ${response.data.id}`); return response.data; } /** * Apply label to message */ async function applyLabelToMessage( gmail: gmail_v1.Gmail, messageId: string, labelName: string ) { console.log(`\nšŸ·ļø Applying label "${labelName}" to message...`); const labelsResponse = await gmail.users.labels.list({ userId: "me" }); const label = labelsResponse.data.labels?.find((l) => l.name === labelName); if (!label) { console.log(` āŒ Label "${labelName}" not found`); return null; } const response = await gmail.users.messages.modify({ userId: "me", id: messageId, requestBody: { addLabelIds: [label.id!], }, }); console.log(` āœ… Label applied`); return response.data; } /** * List threads */ async function listThreads(gmail: gmail_v1.Gmail) { console.log("\nšŸ’¬ Listing threads (last 3)..."); const response = await gmail.users.threads.list({ userId: "me", q: "in:inbox", maxResults: 3, }); const threads = response.data.threads || []; for (const thread of threads) { const threadData = await gmail.users.threads.get({ userId: "me", id: thread.id!, format: "metadata", }); const numMessages = threadData.data.messages?.length || 0; const firstMsg = threadData.data.messages?.[0]; const headers = firstMsg?.payload?.headers || []; const subject = headers.find((h) => h.name === "Subject")?.value || "No Subject"; console.log( `\n Thread ${thread.id}: ${numMessages} messages - "${subject}"` ); } return threads; } /** * Move message to trash */ async function moveToTrash(gmail: gmail_v1.Gmail, messageId: string) { console.log(`\nšŸ—‘ļø Moving message to trash...`); const response = await gmail.users.messages.trash({ userId: "me", id: messageId, }); console.log(` āœ… Message moved to trash`); return response.data; } /** * Main demo function */ async function main() { console.log("šŸš€ Gmail API - Node.js/TypeScript Example\n"); try { // Authenticate const gmail = await authenticate(); // Run examples await getProfile(gmail); await listLabels(gmail); await listInboxMessages(gmail); // Search example await searchMessages(gmail, "is:unread"); // Get first message details const messages = await listInboxMessages(gmail); if (messages.length > 0) { await getFullMessage(gmail, messages[0].id!); } // Draft example (uncomment to use) // await createDraft( // gmail, // "recipient@example.com", // "Test Draft", // "This is a test draft created by the example script." // ); // List threads await listThreads(gmail); console.log("\nāœ… All examples completed!"); } catch (error) { console.error("\nāŒ Error:", error); process.exit(1); } } // Run if executed directly main();