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

如何让Express应用按独立用户速率调用Anilist API而非服务器统一速率?

问题:Anilist API速率限制共享问题及独立额度实现方案

我有一个部署在Render免费服务上的Express后端应用,为前端提供API服务。该应用通过anilist-node调用Anilist第三方API获取数据,Anilist的非商用速率限制为每个用户每分钟90次请求。

我发现当前所有前端用户的请求都会被Anilist视为来自Express服务器这单一用户(因服务器IP/域名固定),导致多用户共享90次/分的请求额度:若10人使用,每人仅能分到9次/分;若90人使用,每人仅1次/分。

请帮我确认这个理解是否正确,如果正确,请问如何让应用按独立用户的速率调用API,即每个用户独立享有90次/分的请求额度?

以下是我的Express应用结构与核心代码:

目录结构

.
└── Express root folder/
    └── src/
        ├── model.js
        ├── routes.js
        ├── controller.js
        └── index.js

index.js

const express = require("express")
const cors = require("cors")
const path = require("path")
const routes = require("./routes")
const app = express()
const PORT = process.env.PORT || 3000

app.use(cors())
app.set("trust proxy", true)

app.use((req, res, next) => {
    res.setHeader("Access-Control-Allow-Origin", "*")
    res.header(
        "Access-Control-Allow-Headers",
        "Origin, X-Requested-With, Content-Type, Accept"
    )
    next()
})
app.use("/api", routes)
app.listen(PORT, () => {
    console.log("Server is running", PORT)
})

routes.js

const express = require("express")
const controller = require("./controller")
const router = express.Router()

router.get("/", controller.getData)
module.exports = router

controller.js

const Model = require("./model")
class Controller {
    static async getData(req, res, next) {
        try {
            const data = await Model.getData()
            res.json({ success: true, data })
        } catch (err) {
            res
                .status(404)
                .send({ success: false, data: "Fail and get 404" })
        }
    }
}
module.exports = Controller

model.js

const anilist = require("anilist-node")
const Anilist = new anilist() // 我用来获取数据的扩展
class Model {
    static async getData() {
        // 这里调用Anilist扩展的操作,会占用服务器的请求额度,而非用户的
        return data
    }
}
module.exports = Model

回答

你的理解完全正确

Anilist的速率限制是基于请求发起方的身份标识(通常是IP地址,若用户有认证则是用户令牌)。当前你的后端作为统一代理发起请求,所有请求都来自服务器IP,自然会被Anilist判定为同一个用户,共享90次/分的额度。

实现每个用户独立享有速率额度的方案

要让每个前端用户独立占用Anilist的速率额度,核心思路是让请求直接关联用户身份,而非服务器身份。以下是两种可行方案:

方案1:前端直接调用Anilist API(推荐)

让前端应用直接集成anilist-node或通过GraphQL请求Anilist API,这样每个请求都来自用户的浏览器IP,Anilist会按每个用户的IP独立计算速率限制。

  • 优势:完全绕过后端的代理瓶颈,后端只需提供业务逻辑,无需承担API请求的速率压力。
  • 注意事项:需在前端处理Anilist的认证(若需访问用户私有数据)、错误处理及速率限制提示逻辑。

方案2:后端转发用户身份标识(适合需后端处理数据的场景)

如果必须通过后端转发请求,可以让前端用户提供自己的Anilist访问令牌,后端调用API时携带该令牌:

  1. 前端用户通过Anilist的OAuth流程获取访问令牌,并在请求后端API时将令牌放在请求头(如Authorization: Bearer <user-token>)中。
  2. 修改后端代码,传入用户令牌初始化Anilist客户端:
    // model.js 修改后的代码
    const anilist = require("anilist-node")
    class Model {
        static async getData(userToken) {
            // 使用用户的令牌初始化Anilist客户端
            const Anilist = new anilist(userToken)
            // 执行API请求,此时Anilist会按该用户的令牌计算速率限制
            const data = await Anilist.media.search("xxx")
            return data
        }
    }
    module.exports = Model
    
  3. 在controller中获取并传递令牌:
    // controller.js 修改后的代码
    const Model = require("./model")
    class Controller {
        static async getData(req, res, next) {
            try {
                const userToken = req.headers.authorization?.split("Bearer ")[1]
                if (!userToken) {
                    return res.status(401).send({ success: false, data: "Missing authorization token" })
                }
                const data = await Model.getData(userToken)
                res.json({ success: true, data })
            } catch (err) {
                res
                    .status(404)
                    .send({ success: false, data: "Fail and get 404" })
            }
        }
    }
    module.exports = Controller
    
  • 注意事项:需确保后端妥善处理用户令牌,避免泄露;同时要处理令牌过期等异常情况。

补充:后端层面的用户限流(可选)

如果不想让前端直接请求Anilist,可在后端对每个用户(按IP或用户ID)单独限流,确保每个用户请求频率不超90次/分,再统一转发给Anilist。但这种方式仅能限制后端接收的请求量,Anilist仍按服务器IP计算总额度,仅适合用户量不大的场景。可使用express-rate-limit包实现:

// index.js 中添加限流配置
const rateLimit = require("express-rate-limit")

const userLimiter = rateLimit({
    windowMs: 60 * 1000, // 1分钟
    max: 90, // 每个用户最多90次请求
    keyGenerator: (req) => req.ip // 按用户IP标识用户
})

app.use("/api", userLimiter, routes)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 00:11:04