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

如何将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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 04:01:07