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

NestJS+Prisma开发gRPC服务时Proto文件空返回处理问题

问题核心原因

gRPC 规范不允许 RPC 方法直接返回 null 作为完整响应体,你之前给 UserResponse 内部字段加 optional 仅支持单个字段为空,无法解决整个响应体为空的场景。你定义的 NullableUserResponse 本身没问题,未生效是三个遗漏点导致的:

  1. 未导入 Google 官方的空值定义依赖
  2. 未将 findOne 方法的返回值替换为自定义的可空类型
  3. 服务端返回数据未匹配 oneof 要求的结构

正确实现步骤

第一步:修改 proto 文件

syntax = "proto3";

package users;
// 新增:导入Google官方空值定义
import "google/protobuf/struct.proto";

service UsersService {
  rpc create (UserRequest) returns (UserResponse) {}
  rpc findAll (Empty) returns (UsersResponse) {}
  // 修改:将findOne返回值替换为可空类型
  rpc findOne (UserId) returns (NullableUserResponse) {}
  rpc update (UserRequest) returns (UserResponse) {}
  rpc remove (UserId) returns (RequestResponse) {}
}

// 原有其他message定义保持不变
message Empty {}
message UserId { string id = 1; }
message RequestResponse { string msg = 1; }

message UserRequest {
  string id = 1;
  string email = 2;
  string password = 3;
  enum Status {
    INACTIVE = 1;
    ACTIVE = 2;
    BLOCKED = 3;
    DELETED = 4;
  }
  Status status = 4;
}

message UserResponse {
  optional string id = 1;
  optional string email = 2;
  optional string password = 3;
  enum Status {
    INACTIVE = 1;
    ACTIVE = 2;
    BLOCKED = 3;
    DELETED = 4;
  }
  optional Status status = 4;
  optional string createdAt = 5;
  optional string updatedAt = 6;
}

message UsersResponse { repeated UserResponse users = 1; }
message Profile {
  string id = 1;
  string userId = 2;
  string name = 3;
  string birthdate = 4;
  string tin = 5;
  string photo = 6;
  string phone = 7;
}

// 定义可空用户响应
message NullableUserResponse {
  oneof kind {
    google.protobuf.NullValue null = 1;
    UserResponse data = 2;
  }
}

第二步:修改 NestJS 服务端返回逻辑

Prisma 查询后不能直接返回 null 或者用户对象,需要匹配 NullableUserResponse 的结构:

import { NullValue } from './your-proto-generated-types'; // 从proto生成的TS类型中导入NullValue

async findOne(userId: UserId) {
  const user = await this.prisma.user.findUnique({
    where: { id: userId.id }
  });
  if (!user) {
    // 用户不存在时返回空值结构
    return { null: NullValue.NULL_VALUE };
  }
  // 用户存在时返回data结构
  return { data: user };
}

第三步:客户端消费逻辑调整

客户端收到响应后先判断返回类型即可:

const userRes = await usersServiceClient.findOne({ id: 'xxx' }).toPromise();
if (userRes.kind === 'null') {
  // 处理用户不存在的逻辑
} else {
  const user = userRes.data;
  // 处理正常用户逻辑
}

可选替代方案

如果你的业务场景中「用户不存在」属于异常情况,也可以直接返回 gRPC 标准错误码,不需要修改 proto 定义,更符合 gRPC 常规设计规范:

import { status } from '@grpc/grpc-js';
import { RpcException } from '@nestjs/microservices';

async findOne(userId: UserId) {
  const user = await this.prisma.user.findUnique({
    where: { id: userId.id }
  });
  if (!user) {
    throw new RpcException({
      code: status.NOT_FOUND,
      message: '用户不存在'
    });
  }
  return user;
}

内容的提问来源于stack exchange,提问作者Seta-san

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 21:45:06