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

在NextJS+TypeScript中无需.proto文件发送gRPC请求的实现方法

无需本地.proto文件连接gRPC服务器的实现方案

方案一:利用Gitlab API动态加载.proto内容

你已经能通过Gitlab API获取.proto原始内容,可在运行时动态加载并生成gRPC客户端,无需将文件存到项目目录中。

服务端组件/API路由(使用@grpc/grpc-js)

适用于NextJS的服务端代码(如API路由、Server Components),直接用官方gRPC库:

import * as grpc from '@grpc/grpc-js';
import * as protoLoader from '@grpc/proto-loader';

// 动态创建gRPC客户端
async function createGrpcClient(serverAddr: string, serviceFullName: string) {
  // 从Gitlab API拉取.proto原始文本
  const protoRes = await fetch('你的Gitlab API地址');
  const protoContent = await protoRes.text();

  // 将proto字符串加载为package定义
  const packageDef = protoLoader.loadFromString(protoContent, {
    keepCase: true,
    longs: String,
    enums: String,
    defaults: true,
    oneofs: true
  });

  // 加载package并创建客户端
  const protoPackage = grpc.loadPackageDefinition(packageDef);
  // 注意替换为你的服务全限定名(如com.example.YourService)
  return new protoPackage[serviceFullName](serverAddr, grpc.credentials.createInsecure());
}

// 发送请求示例
async function callRpcMethod() {
  const client = await createGrpcClient('grpc-server:50051', 'com.example.YourService');
  return new Promise((resolve, reject) => {
    // 替换为你的RPC方法名和请求参数
    client.getResource({ id: 123 }, (err, resp) => {
      if (err) reject(err);
      else resolve(resp);
    });
  });
}

客户端组件(使用grpc-web)

如果需要在浏览器端(Client Components)调用,改用grpc-web配合protobufjs动态解析:

import { grpc } from '@improbable-eng/grpc-web';
import * as protobuf from 'protobufjs';

async function sendClientSideRequest() {
  // 拉取proto内容
  const protoRes = await fetch('你的Gitlab API地址');
  const protoContent = await protoRes.text();

  // 解析proto字符串为protobuf根对象
  const root = await protobuf.load(protobuf.parse(protoContent));
  // 替换为你的请求/响应消息全限定名
  const RequestMsg = root.lookupType('com.example.GetResourceRequest');
  const ResponseMsg = root.lookupType('com.example.GetResourceResponse');

  // 构造并验证请求消息
  const requestData = RequestMsg.create({ id: 123 });
  const verifyErr = RequestMsg.verify(requestData);
  if (verifyErr) throw new Error(`请求参数错误: ${verifyErr}`);

  // 编码请求并发送
  return new Promise((resolve, reject) => {
    grpc.invoke(
      // 格式:服务全限定名/RPC方法名
      'com.example.YourService/GetResource',
      {
        request: RequestMsg.encode(requestData).finish(),
        host: 'https://grpc-server-proxy', // 需配置grpc-web代理
        onMessage: (msgBytes) => resolve(ResponseMsg.decode(msgBytes)),
        onEnd: (code, msg) => {
          if (code !== grpc.Code.OK) reject(new Error(`请求失败: ${msg}`));
        }
      }
    );
  });
}

方案二:利用gRPC反射服务动态获取定义

如果服务器开启了反射功能,可通过反射客户端获取服务的.proto定义,再动态生成客户端:

import * as grpc from '@grpc/grpc-js';
import * as protoLoader from '@grpc/proto-loader';
import { ServerReflectionClient } from '@grpc/grpc-js/build/src/reflection';
import * as protobuf from 'protobufjs';

// 从反射服务获取proto内容
async function fetchProtoFromReflection(serverAddr: string, serviceFullName: string) {
  const reflectionClient = new ServerReflectionClient(
    serverAddr,
    grpc.credentials.createInsecure()
  );

  return new Promise<string>((resolve, reject) => {
    const stream = reflectionClient.serverReflectionInfo((err) => {
      if (err) reject(err);
    });

    // 请求指定服务的proto定义
    stream.write({ fileByService: { service: serviceFullName } });

    let fullProtoContent = '';
    stream.on('data', (resp) => {
      if (resp.fileDescriptorResponse) {
        resp.fileDescriptorResponse.fileDescriptorProto.forEach((fd) => {
          const root = protobuf.Root.fromDescriptor(fd);
          fullProtoContent += root.toString();
        });
      }
    });

    stream.on('end', () => resolve(fullProtoContent));
    stream.on('error', reject);
  });
}

// 基于反射内容创建客户端并调用
async function callRpcViaReflection() {
  const protoContent = await fetchProtoFromReflection('grpc-server:50051', 'com.example.YourService');
  const packageDef = protoLoader.loadFromString(protoContent);
  const protoPackage = grpc.loadPackageDefinition(packageDef);
  const client = new protoPackage['com.example.YourService']('grpc-server:50051', grpc.credentials.createInsecure());

  return new Promise((resolve, reject) => {
    client.getResource({ id: 123 }, (err, resp) => {
      if (err) reject(err);
      else resolve(resp);
    });
  });
}

关键注意事项

  • NextJS环境区分:服务端可直接用@grpc/grpc-js,客户端必须用grpc-web(浏览器不支持原生gRPC)。
  • 安全配置:生产环境不要用createInsecure(),改用credentials.createSsl()配置证书。
  • 性能优化:可缓存拉取到的proto内容,避免每次请求都重复拉取解析。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 13:20:42