基于NestJS的GraphQL Mutation调用TMDB评分接口返回404错误
解决NestJS中调用TMDB电影评分Patch接口的404错误
问题概述
使用NestJS(Schema First模式)结合GraphQL和TMDB API开发电影网站,实现用户提交电影评分的Patch请求时,遇到GraphQL返回404 Not Found错误。错误详情显示请求URL为https://api.themoviedb.org/3/movie/370172/rating/8/?api_key=_______,TMDB返回状态码34(资源不存在)。
错误分析
- URL格式错误:错误地将rating值(如8)拼接到了接口路径中(
/rating/8/),同时RESTDataSource的patch方法第一个参数应为相对路径,不应重复拼接baseURL,导致TMDB无法识别请求资源。 - 请求体与请求选项混淆:错误地将请求体定义为继承
PatchRequest类型,PatchRequest是用于配置请求选项(如headers、params)的类型,而非请求体类型。 - 缺少必要请求头:TMDB的评分接口要求请求头包含
Content-Type: application/json,否则可能导致接口解析失败。
修复步骤
1. 修正URL路径
移除patch路径中的baseURL,使用相对路径,并通过统一配置传递API key:
- 已设置
baseURL = "https://api.themoviedb.org/3/",因此patch路径只需写movie/${id}/rating - 通过
willSendRequest方法统一添加API key到请求参数,避免重复拼接
2. 正确区分请求体与请求选项
单独定义请求体类型,将请求选项(如headers、认证参数)作为patch方法的第三个参数传递。
3. 添加必要请求头与认证信息
确保请求头包含Content-Type: application/json;TMDB电影评分接口要求用户会话验证,需传递session_id或guest_session_id(可通过TMDB创建会话接口获取)。
完整代码示例
ratings.service.ts
import { Injectable } from '@nestjs/common'; import { RESTDataSource } from '@apollo/datasource-rest'; // 定义评分请求体类型 interface RatingRequestBody { value: number; } @Injectable() export class RatingsService extends RESTDataSource { constructor() { super(); this.baseURL = 'https://api.themoviedb.org/3/'; } // 统一添加API key到请求参数 willSendRequest(request: any) { request.params.set('api_key', process.env.API_KEY); // 若需用户认证,添加会话ID(示例:从上下文获取用户会话) // request.params.set('session_id', this.context.sessionId); } async addMovieRating(id: string, rating: number) { // 验证rating范围(TMDB要求0.5-10,步长0.5) if (rating < 0.5 || rating > 10 || rating % 0.5 !== 0) { throw new Error('评分必须在0.5-10之间,且为0.5的整数倍'); } const requestBody: RatingRequestBody = { value: rating, }; // 发送Patch请求:相对路径 + 请求体 + 请求选项 const response = await this.patch( `movie/${id}/rating`, requestBody, { headers: { 'Content-Type': 'application/json', }, }, ); return response; } }
ratings.resolver.ts
import { Args, Mutation, Resolver } from '@nestjs/graphql'; import { RatingsService } from './ratings.service'; @Resolver() export class RatingsResolver { constructor(private readonly ratingsService: RatingsService) {} @Mutation() async addMovieRatings( @Args('id') id: string, @Args('rating') rating: number, ) { try { const tmdbResponse = await this.ratingsService.addMovieRating(id, rating); // 转换TMDB返回格式适配GraphQL Schema return { code: 200, success: tmdbResponse.success, message: tmdbResponse.status_message || '评分提交成功', rating: { id, rating }, }; } catch (error) { // 捕获错误并返回符合Schema的格式 return { code: error.response?.status || 500, success: false, message: error.message || '评分提交失败', rating: null, }; } } }
schema.graphql
type Rating { id: ID! rating: Float! } type AddMovieRatings { code: Int! success: Boolean! message: String! rating: Rating } type Mutation { addMovieRatings(id: ID!, rating: Float!): AddMovieRatings! }
额外注意事项
- 用户认证:TMDB电影评分接口必须验证用户会话,需提前通过TMDB接口获取
session_id或guest_session_id并传入请求。 - 参数校验:严格控制rating值范围,避免因参数无效导致接口返回错误。
- 错误捕获:在Resolver层统一处理服务端错误,返回友好的错误信息给前端。
内容的提问来源于stack exchange,提问作者coder_i_guess
相关产品推荐
相关产品推荐

