如何在Next.js应用中部署含事件监听函数的Node脚本?
解决方案
在Next.js里处理合约事件监听,核心问题是Serverless环境没法长期驻留进程,得根据你的业务需求选不同方案:
1. 前端客户端直接监听(适合用户需实时接收事件的场景)
如果你的需求是让用户在打开页面时实时收到合约事件通知,直接在React组件里用thirdweb SDK监听就行。组件挂载时初始化监听,卸载时清理,避免内存泄漏。
示例代码:
import { useContract, useContractEvents } from "@thirdweb-dev/react"; import { useEffect, useState } from "react"; export default function EventListenerPage() { const { contract } = useContract("你的合约地址"); const [events, setEvents] = useState([]); // 用thirdweb的hooks监听所有事件 const { data: contractEvents, isLoading } = useContractEvents(contract, "*"); useEffect(() => { if (contractEvents) { setEvents(prev => [...prev, ...contractEvents]); } }, [contractEvents]); // 或者手动写监听逻辑(如果hooks满足不了需求) useEffect(() => { let unsubscribe; if (contract) { unsubscribe = contract.events.addListener("*", (event) => { console.log("收到事件:", event); setEvents(prev => [...prev, event]); }); } // 组件卸载时清理监听 return () => { if (unsubscribe) unsubscribe(); }; }, [contract]); if (isLoading) return <div>加载中...</div>; return ( <div> <h2>合约事件</h2> {events.map((event, idx) => ( <div key={idx}>{JSON.stringify(event)}</div> ))} </div> ); }
注意:这种方式只有用户打开页面时才会监听,页面关闭就停止,适合前端实时展示场景。
2. 用常驻服务+Next.js API路由(适合后台持续监听的场景)
如果需要后台24小时监听合约事件(比如触发数据库更新、发送邮件/通知),Next.js的Serverless环境做不到,得单独部署一个常驻Node服务,事件触发时调用Next.js的API路由处理业务逻辑。
步骤:
- 写一个独立的Node监听脚本:
// listener.js const { ThirdwebSDK } = require("@thirdweb-dev/sdk"); const axios = require("axios"); async function startListening() { const sdk = new ThirdwebSDK("你的链ID"); const contract = await sdk.getContract("你的合约地址"); console.log("开始监听合约事件..."); contract.events.addListener("*", async (event) => { console.log("捕获事件:", event); // 调用Next.js的API路由处理事件 try { await axios.post("https://你的Next.js域名/api/handle-contract-event", { eventData: event, }); console.log("事件已处理"); } catch (err) { console.error("处理事件失败:", err); } }); } startListening().catch(err => console.error("启动监听失败:", err));
- 把这个脚本部署到支持常驻进程的平台,比如Render、Heroku、AWS EC2,或者用Docker容器部署。
- 在Next.js里写对应的API路由:
// app/api/handle-contract-event/route.js import { NextResponse } from "next/server"; export async function POST(request) { const { eventData } = await request.json(); // 这里写你的业务逻辑:存数据库、发通知等 console.log("收到来自监听服务的事件:", eventData); return NextResponse.json({ status: "success" }); }
3. 定时轮询(适合准实时、非高频率事件的场景)
如果不需要严格实时,可定时轮询合约获取新事件。用Vercel Cron Jobs(如果部署在Vercel)或者其他调度工具定期执行函数查询事件。
示例(Vercel Cron):
- 在
vercel.json里配置定时任务:
{ "crons": [ { "path": "/api/fetch-contract-events", "schedule": "*/5 * * * *" // 每5分钟执行一次 } ] }
- 写API路由处理轮询:
// app/api/fetch-contract-events/route.js import { ThirdwebSDK } from "@thirdweb-dev/sdk"; import prisma from "@/lib/prisma"; // 假设用Prisma操作数据库 export async function GET() { const sdk = new ThirdwebSDK("你的链ID"); const contract = await sdk.getContract("你的合约地址"); // 查询最近的事件(比如最近5分钟) const events = await contract.events.getAllEvents({ fromBlock: "latest", toBlock: "latest", }); // 过滤未处理的事件(假设数据库存了已处理的事件ID) const processedEvents = await prisma.contractEvent.findMany({ select: { eventId: true }, }); const processedIds = new Set(processedEvents.map(e => e.eventId)); const newEvents = events.filter(e => !processedIds.has(e.transactionHash + e.logIndex)); // 处理新事件:存数据库等 if (newEvents.length > 0) { await prisma.contractEvent.createMany({ data: newEvents.map(e => ({ eventId: e.transactionHash + e.logIndex, data: JSON.stringify(e), })), }); console.log(`处理了${newEvents.length}个新事件`); } return NextResponse.json({ status: "done", processed: newEvents.length }); }
方案选择
- 前端监听:适合用户端实时展示事件,开发简单,但依赖用户页面打开。
- 常驻服务+API:适合后台持续处理事件,需要额外部署服务,但能保证24小时运行。
- 定时轮询:适合事件频率不高、不需要严格实时的场景,无需额外部署常驻服务,但有延迟。
内容的提问来源于stack exchange,提问作者arnaudjnn
相关产品推荐
相关产品推荐

