You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Express JS 集成 Google Sheet:如何实现用户授权访问及数据双向同步

Express JS 实现Google账号授权对接Google Sheet双向同步方案

前置准备

首先完成Google Cloud端的基础配置:

  • 访问Google Cloud控制台创建新项目,搜索并启用Google Sheets API
  • 进入「API和服务」→「OAuth同意屏幕」页面,配置应用名称、联系方式等必填信息,授权范围添加https://www.googleapis.com/auth/spreadsheets(可读写Sheet权限),如果仅需要只读权限可以替换为https://www.googleapis.com/auth/spreadsheets.readonly
  • 进入「凭据」页面,创建「OAuth 2.0客户端ID」,应用类型选择「Web应用」,添加授权重定向URI为你的Express服务的回调地址(例如本地开发可以填http://localhost:3000/auth/google/callback),创建完成后保存生成的客户端ID和客户端密钥

核心依赖安装

项目需要用到以下npm包:

npm install express express-session passport passport-google-oauth20 googleapis

功能实现步骤

1. 谷歌账号授权流程实现

首先初始化Express服务、Session、Passport授权策略:

const express = require('express');
const session = require('express-session');
const passport = require('passport');
const GoogleStrategy = require('passport-google-oauth20').Strategy;
const { google } = require('googleapis');
const app = express();

// 解析post请求体
app.use(express.json());
// Session配置,生产环境建议把session存在数据库而非内存
app.use(session({
  secret: '你自定义的session加密密钥',
  resave: false,
  saveUninitialized: false,
  cookie: { secure: process.env.NODE_ENV === 'production' }
}));

// 初始化Passport
app.use(passport.initialize());
app.use(passport.session());

// 配置Google OAuth2授权策略
passport.use(new GoogleStrategy({
    clientID: '你之前生成的Google客户端ID',
    clientSecret: '你之前生成的Google客户端密钥',
    callbackURL: "http://localhost:3000/auth/google/callback",
    scope: ['https://www.googleapis.com/auth/spreadsheets', 'profile', 'email']
  },
  (accessToken, refreshToken, profile, done) => {
    // 此处需要将accessToken、refreshToken和用户的唯一标识绑定存入你的数据库
    // 后续调用Google Sheet接口需要用这两个凭证
    const userInfo = {
      id: profile.id,
      name: profile.displayName,
      email: profile.emails[0].value,
      accessToken,
      refreshToken
    };
    return done(null, userInfo);
  }
));

// Passport序列化/反序列化用户
passport.serializeUser((user, done) => done(null, user));
passport.deserializeUser((user, done) => done(null, user));

// 触发谷歌授权的接口
app.get('/auth/google', passport.authenticate('google', {
  accessType: 'offline', // 必须加这个参数才能拿到refreshToken
  prompt: 'consent' // 强制用户每次授权都选账号,避免自动登录
}));

// 授权回调接口
app.get('/auth/google/callback',
  passport.authenticate('google', { failureRedirect: '/login' }),
  (req, res) => {
    // 授权成功后跳转到应用的表格页面
    res.redirect('/sheet-page');
  }
);

注意:只有首次授权时会返回refreshToken,如果后续需要重新获取,需要用户取消对应用的授权后重新发起授权请求

2. 拉取Google Sheet内容到应用

实现接口根据用户传入的表格ID和范围拉取对应数据:

app.get('/api/sheet/data', async (req, res) => {
  // 校验用户是否已授权
  if (!req.user) return res.status(401).json({ error: '用户未授权' });
  const { spreadsheetId, range = 'Sheet1!A:Z' } = req.query;

  // 初始化OAuth2客户端
  const oauth2Client = new google.auth.OAuth2();
  oauth2Client.setCredentials({
    access_token: req.user.accessToken,
    refresh_token: req.user.refreshToken
  });

  const sheets = google.sheets({ version: 'v4', auth: oauth2Client });

  try {
    const result = await sheets.spreadsheets.values.get({
      spreadsheetId,
      range
    });
    res.json(result.data.values);
  } catch (err) {
    // 处理accessToken过期的情况,用refreshToken刷新
    if (err.code === 401) {
      try {
        const { tokens } = await oauth2Client.refreshAccessToken();
        // 更新数据库中对应用户的accessToken
        req.user.accessToken = tokens.access_token;
        // 重试拉取数据
        const retryResult = await sheets.spreadsheets.values.get({ spreadsheetId, range });
        return res.json(retryResult.data.values);
      } catch (refreshErr) {
        return res.status(401).json({ error: '授权已过期,请重新登录' });
      }
    }
    res.status(500).json({ error: err.message });
  }
});

其中spreadsheetId可以从Google Sheet的URL中获取,URL格式为https://docs.google.com/spreadsheets/d/[spreadsheetId]/edit,中间的字符串就是表格ID。

3. 应用侧修改同步到Google Sheet

实现接口将用户在应用内的修改同步到对应的Google Sheet:

app.post('/api/sheet/update', async (req, res) => {
  if (!req.user) return res.status(401).json({ error: '用户未授权' });
  const { spreadsheetId, range, values } = req.body;

  const oauth2Client = new google.auth.OAuth2();
  oauth2Client.setCredentials({
    access_token: req.user.accessToken,
    refresh_token: req.user.refreshToken
  });

  const sheets = google.sheets({ version: 'v4', auth: oauth2Client });

  try {
    const result = await sheets.spreadsheets.values.update({
      spreadsheetId,
      range,
      valueInputOption: 'RAW', // 直接存储原始值,不需要Google解析公式等
      resource: { values }
    });
    res.json({
      success: true,
      updatedCells: result.data.updatedCells
    });
  } catch (err) {
    if (err.code === 401) {
      try {
        const { tokens } = await oauth2Client.refreshAccessToken();
        req.user.accessToken = tokens.access_token;
        const retryResult = await sheets.spreadsheets.values.update({
          spreadsheetId,
          range,
          valueInputOption: 'RAW',
          resource: { values }
        });
        return res.json({ success: true, updatedCells: retryResult.data.updatedCells });
      } catch (refreshErr) {
        return res.status(401).json({ error: '授权已过期,请重新登录' });
      }
    }
    res.status(500).json({ error: err.message });
  }
});

4. 实时同步优化

如果需要做到用户修改后实时同步,可以在前端给表格的修改事件加300-500ms的防抖,避免每次修改都发起请求,减少接口调用频次。如果需要实现Google Sheet侧的修改也同步到你的应用,可以使用Google Sheets API的推送通知功能,配置Webhook接收变更事件,更新你侧的缓存数据。

参考文档

  • Google Sheets API 官方开发文档,包含所有接口的参数说明、错误码处理方案
  • Google OAuth 2.0 Web服务端授权官方文档,详细讲解授权流程的规则和注意事项
  • Passport Google OAuth2 策略官方使用文档

内容的提问来源于stack exchange,提问作者NOOBIEBOY97

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 12:15:04