如何将GraphQL获取的数据填充到PostgreSQL做数据库种子初始化
完全可以将从AniList API取回的数据作为种子数据填充到PostgreSQL数据库,通过ORM实现的逻辑非常清晰。注意你当前的代码是前端React组件内的客户端数据拉取逻辑,仅用于UI渲染,种子数据填充属于服务端离线任务,不能写在前端组件中,否则会出现凭证泄露、跨域等问题。以下是具体实现思路和可落地的操作步骤,以Node生态适配PostgreSQL最常用的Prisma ORM为例,其他ORM的实现逻辑通用。
核心实现思路
种子数据填充是可重复执行的离线数据写入任务,整体流程分三个环节:
- 在Node服务端环境拉取全量AniList目标数据,禁止在浏览器端执行数据库操作
- 通过ORM定义和接口返回数据字段匹配的PostgreSQL表结构,完成建表
- 调用ORM的批量写入接口,将拉取到的数据格式化后写入数据库,同时做好幂等处理支持重复执行
具体操作步骤
1. 抽离独立的Node侧数据拉取逻辑
你当前用的@apollo/client同时支持浏览器和Node环境,不需要更换请求库,单独编写脚本拉取全量分页数据,不要依赖React的useQuery hook:
// scripts/fetch-anilist-data.js import { ApolloClient, InMemoryCache } from "@apollo/client"; import { QUERY } from "../graphql/schema.js"; // 初始化Node侧Apollo客户端 const client = new ApolloClient({ uri: "https://graphql.anilist.co", cache: new InMemoryCache(), }); async function fetchAllMedia() { const allMedias = []; let page = 1; let hasNextPage = true; // 循环拉取全部分页数据 while (hasNextPage) { const { data } = await client.query({ query: QUERY, variables: { page, perPage: 50 } }); allMedias.push(...data.Page.media); hasNextPage = data.Page.pageInfo.hasNextPage; page++; // 加间隔避免触发接口频率限制 await new Promise(resolve => setTimeout(resolve, 1000)); } return allMedias; }
2. 配置ORM连接,定义数据表模型
先完成ORM和PostgreSQL的连接配置,以Prisma为例,初始化完成后在配置文件中定义和接口返回字段匹配的模型:
// prisma/schema.prisma generator client { provider = "prisma-client-js" } datasource db { provider = "postgresql" url = env("DATABASE_URL") // 填你的PostgreSQL连接地址 } model Media { id Int @id // 直接用AniList返回的媒体ID作为主键 title String // 存储userPreferred字段的标题内容 coverImageUrl String // 存储coverImage.medium的封面地址 // 其他需要存储的字段可以对照接口返回结构按需添加 }
配置完成后执行npx prisma migrate dev,即可自动在PostgreSQL中创建对应的数据表。
3. 编写批量写入的种子逻辑
在拉取数据的脚本中引入ORM实例,将拉取到的数据格式化后批量写入,优先用批量写入接口不要单条循环插入,同时做好幂等处理避免重复执行报错:
import { PrismaClient } from "@prisma/client"; const prisma = new PrismaClient(); async function main() { console.log("开始拉取AniList数据..."); const medias = await fetchAllMedia(); console.log(`共拉取到${medias.length}条媒体数据,开始写入数据库...`); // 批量写入,遇到重复ID直接跳过,支持脚本重复执行 await prisma.media.createMany({ data: medias.map(item => ({ id: item.id, title: item.title.userPreferred, coverImageUrl: item.coverImage.medium })), skipDuplicates: true }); console.log("种子数据填充完成!"); } main() .catch(e => console.error(e)) .finally(async () => await prisma.$disconnect());
4. 配置执行命令完成填充
在项目的package.json中添加种子脚本执行命令:
{ "scripts": { "seed:anilist": "node scripts/fetch-anilist-data.js" } }
执行npm run seed:anilist即可自动完成数据拉取、格式化、写入数据库的全流程。
注意事项
- 如果你用的是其他ORM(比如TypeORM、Sequelize),逻辑完全一致:先在Node侧拉取全量目标数据,定义对应表模型,调用ORM的批量写入方法插入即可,没有本质差异
- AniList公共接口有明确的频率限制,拉取分页数据时一定要加请求间隔,不要发起高并发请求,否则会被临时封禁IP
- 种子脚本一定要做幂等处理:配置重复主键跳过/更新逻辑,这样脚本可以重复执行,后续需要同步最新数据时直接重跑即可
- 绝对不要把数据库写入逻辑放到你当前编写的React前端组件中:浏览器端不能直接连接数据库,不仅会存在跨域问题,还会泄露数据库账号密码,导致任何人都能随意操作你的数据库
内容的提问来源于stack exchange,提问作者PazzeG
相关产品推荐
相关产品推荐

