如何使用Next.js实现向Google Sheet写入数据的功能?
解决方案
第一步 调整权限配置
- 替换Google API鉴权范围:原有
spreadsheets.readonly是只读权限,写入需要修改为可写权限范围,推荐使用https://www.googleapis.com/auth/spreadsheets(全表读写权限) - 打开你的Google Sheet,点击右上角「共享」,将服务账号邮箱(即
GOOGLE_SHEETS_CLIENT_EMAIL对应的值)的权限从「查看者」调整为「编辑者」
第二步 改造sheet.js代码
将公共鉴权逻辑抽离,新增写入功能函数,修改后完整代码如下:
import { google } from 'googleapis'; // 抽离公共鉴权逻辑,读写复用 async function getAuthSheets() { // 替换为可写权限范围 const scopes = ['https://www.googleapis.com/auth/spreadsheets']; const jwt = new google.auth.JWT( process.env.GOOGLE_SHEETS_CLIENT_EMAIL, null, (process.env.GOOGLE_SHEETS_PRIVATE_KEY || '').replace(/\\n/g, '\n'), scopes ); return google.sheets({ version: 'v4', auth: jwt }); } // 原有读取方法,修改为复用鉴权逻辑 export async function getDataFromSheets() { try { const sheets = await getAuthSheets(); const response = await sheets.spreadsheets.values.get({ spreadsheetId: process.env.SPREADSHEET_ID, range: 'sheet' }); const rows = response.data.values; if (rows.length) { return rows.map((row) => ({ title: row[0], description: row[1], })); } } catch (err) { console.log(err); } return []; } // 新增写入方法:追加行到表格末尾 export async function appendDataToSheets(data) { // data格式为 [title, description] try { const sheets = await getAuthSheets(); const response = await sheets.spreadsheets.values.append({ spreadsheetId: process.env.SPREADSHEET_ID, range: 'sheet', // 写入的表格范围 valueInputOption: 'RAW', // RAW为直接存储输入内容,USER_ENTERED会自动解析公式、日期等格式 resource: { values: [data] } }); return { success: true, data: response.data }; } catch (err) { console.log(err); return { success: false, error: err.message }; } }
第三步 新增Next.js API路由处理写入请求
不要直接在客户端调用写入方法,会暴露敏感环境变量,需要在pages/api目录下新建write-sheet.js文件,代码如下:
import { appendDataToSheets } from '../../libs/sheets'; export default async function handler(req, res) { // 只允许POST请求 if (req.method !== 'POST') { return res.status(405).json({ message: '仅支持POST请求' }); } const { title, description } = req.body; // 简单参数校验 if (!title || !description) { return res.status(400).json({ message: 'title和description不能为空' }); } const result = await appendDataToSheets([title, description]); if (result.success) { return res.status(200).json(result); } else { return res.status(500).json(result); } }
第四步 客户端添加写入触发逻辑
可以在index.js中加一个表单,提交时调用上面的API接口,示例代码如下:
// 在Home组件中新增表单逻辑 import { useState } from 'react'; export default function Home({ data }) { const [title, setTitle] = useState(''); const [description, setDescription] = useState(''); const handleSubmit = async (e) => { e.preventDefault(); await fetch('/api/write-sheet', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ title, description }) }); // 提交后清空输入框,需要即时更新列表可引入useRouter调用router.reload() setTitle(''); setDescription(''); }; return ( <div className={styles.container}> <Head> <title>Nextsheet 💩</title> <meta name="description" content="Connecting NextJS with Google Spreadsheets as Database" /> <link rel="icon" href="/favicon.ico" /> </Head> <main> <h1>Welcome to Nextsheet 💩</h1> <p>Connecting NextJS with Google Spreadsheets as Database</p> <ul> {data && data.length ? ( data.map((item, index) => ( <li key={index}> {item.title} - {item.description} </li> )) ) : ( <li>Error: do not forget to setup your env variables 👇</li> )} </ul> {/* 新增写入表单 */} <form onSubmit={handleSubmit} style={{ marginTop: '2rem' }}> <div> <input type="text" value={title} onChange={(e) => setTitle(e.target.value)} placeholder="输入标题" required /> </div> <div style={{ margin: '1rem 0' }}> <input type="text" value={description} onChange={(e) => setDescription(e.target.value)} placeholder="输入描述" required /> </div> <button type="submit">提交到Google Sheet</button> </form> </main> </div> ) } // 原有getStaticProps逻辑保持不变 export async function getStaticProps(context) { const sheet = await getDataFromSheets(); return { props: { data: sheet.slice(1, sheet.length), // 原注释是移除表头,所以索引应该从1开始而非0,这里顺带修正了原代码的小问题 }, revalidate: 1, // In seconds }; }
常见问题排查
- 检查服务账号的编辑权限是否配置正确,不要设置为仅查看
- 确认
valueInputOption参数是否符合你的写入需求 - 写入的range范围要和实际表格的结构匹配,避免写入到错误位置
内容的提问来源于stack exchange,提问作者Monu Patil
相关产品推荐
相关产品推荐

