# Connecting to gRPC APIs

Most custom connectors call HTTP REST APIs, but you can also build connectors for [gRPC](https://grpc.io/) APIs. This guide shows how to load a gRPC service definition in a custom connector, package it with your build, and call a remote procedure from an action.

The complete example is available in the [examples repo](https://github.com/prismatic-io/examples/tree/main/components/grpc-example). It calls the `SayHello` method of Postman's public gRPC echo service.

## Install the gRPC packages[​](#install-the-grpc-packages "Direct link to Install the gRPC packages")

Start with a [new custom connector project](https://prismatic.io/docs/custom-connectors/initializing.md), then install the gRPC client library and the `.proto` file loader:

Install gRPC dependencies

```bash
npm install @grpc/grpc-js @grpc/proto-loader

```

## Add the service definition[​](#add-the-service-definition "Direct link to Add the service definition")

gRPC APIs describe their methods and messages in [`.proto` files](https://grpc.io/docs/what-is-grpc/core-concepts/#service-definition). Get the `.proto` files for the API you're connecting to and place them in `src/protoFiles/`:

src/protoFiles/helloworld.proto

```protobuf
syntax = "proto3";

service HelloService {
  rpc SayHello (HelloRequest) returns (HelloResponse) {}
}

// The request message containing the user's name.
message HelloRequest {
  string greeting = 1;
}

// The response message containing the greetings
message HelloResponse {
  string reply = 1;
}

```

## Package .proto files with your connector[​](#package-proto-files-with-your-connector "Direct link to Package .proto files with your connector")

Your connector loads `.proto` files from disk when it runs, so they must be in the published bundle alongside your compiled code. Add a `copy` entry to `tsdown.config.mts` that copies `src/protoFiles/` into `dist/protoFiles/`:

tsdown.config.mts

```typescript
import { defineConfig } from "tsdown";

export default defineConfig({
  entry: { index: "src/index.ts" },
  format: "cjs",
  outDir: "dist",
  platform: "node",
  clean: true,
  dts: false,
  deps: {
    alwaysBundle: () => true,
    onlyBundle: false,
  },
  outExtensions: () => ({ js: ".js" }),
  outputOptions: { exports: "named" },
  loader: {
    ".md": "text",
  },
  copy: [
    { from: "assets/*", to: "dist" },
    { from: "src/protoFiles/*", to: "dist/protoFiles" },
  ],
});

```

Without this step, the connector builds successfully but fails at runtime when it can't find the `.proto` file. After you run `npm run build`, confirm that `dist/protoFiles/` contains your `.proto` files before you [publish](https://prismatic.io/docs/custom-connectors/publishing.md).

## Create a gRPC client[​](#create-a-grpc-client "Direct link to Create a gRPC client")

Load the service definition and create a client for the service. Resolve the `.proto` path relative to `__dirname` so that it points to `dist/protoFiles/` in the built connector.

gRPC sends credentials as request **metadata** rather than HTTP headers. This example builds a Basic authentication value from the connection's username and password and adds it to a `Metadata` object that each call sends:

src/client.ts

```typescript
import * as grpc from "@grpc/grpc-js";
import * as protoLoader from "@grpc/proto-loader";
import type { Connection } from "@prismatic-io/spectral";
import path from "path";

export const createClient = (connection: Connection, service: string) => {
  const PROTO_PATH = path.resolve(__dirname, "protoFiles/helloworld.proto");

  const packageDefinition = protoLoader.loadSync(PROTO_PATH, {
    keepCase: true,
    longs: String,
    enums: String,
    defaults: true,
    oneofs: true,
  });

  const helloProto = grpc.loadPackageDefinition(packageDefinition) as any;

  const client = new helloProto[service](
    "grpc.postman-echo.com",
    grpc.credentials.createInsecure(),
  );

  const auth =
    "Basic " +
    Buffer.from(
      `${connection.fields.username}:${connection.fields.password}`,
    ).toString("base64");

  const requestMetadata = new grpc.Metadata();
  requestMetadata.set("Authentication", auth);

  return { client, requestMetadata };
};

```

Adjust these details for your API:

* **Server address**: Replace `grpc.postman-echo.com` with your API's host and port. To let each customer connect to their own server, add a host input to your [connection](https://prismatic.io/docs/custom-connectors/connections.md) and read it from `connection.fields`.
* **Channel credentials**: The example uses `grpc.credentials.createInsecure()`, which sends traffic unencrypted. Use `grpc.credentials.createSsl()` for servers that accept TLS connections.
* **Metadata**: Use the metadata key and value format your API expects, such as `authorization` with a bearer token.

## Call a method from an action[​](#call-a-method-from-an-action "Direct link to Call a method from an action")

Methods on a `@grpc/grpc-js` client accept a callback. Wrap the call in a `Promise` so your action can `await` the response, and close the client when you're done:

src/actions.ts

```typescript
import { action, input } from "@prismatic-io/spectral";
import { createClient } from "./client";

export const sayHello = action({
  display: {
    label: "Say Hello",
    description: "Say hello to the person of the given name",
  },
  inputs: {
    connection: input({
      label: "Connection",
      type: "connection",
      required: true,
    }),
    name: input({
      label: "Name",
      type: "string",
      comments: "Your name",
      required: true,
    }),
  },
  perform: async (context, { connection, name }) => {
    const { client, requestMetadata } = createClient(
      connection,
      "HelloService",
    );
    const response = await new Promise((resolve, reject) => {
      client.sayHello(
        { greeting: name },
        requestMetadata,
        (err: any, response: any) => {
          if (err) {
            reject(err);
          }
          resolve(response);
        },
      );
    });
    client.close();
    return { data: response };
  },
});

```

Because the example loads the `.proto` file dynamically, the client has no generated TypeScript types. If you want type checking for requests and responses, generate types from your `.proto` files with the `proto-loader-gen-types` command that `@grpc/proto-loader` provides.

## Test the action[​](#test-the-action "Direct link to Test the action")

The example includes a [unit test](https://prismatic.io/docs/custom-connectors/unit-testing.md) that invokes **Say Hello** against the echo service:

src/index.test.ts

```typescript
import { createConnection, invoke } from "@prismatic-io/spectral/dist/testing";
import { sayHello } from "./actions";
import { myConnection } from "./connections";

describe("test my action", () => {
  test("verify the return value of my action", async () => {
    const { result } = await invoke(sayHello, {
      connection: createConnection(myConnection, {
        username: "myuser",
        password: "mypass",
      }),
      name: "John Doe",
    });
    expect(result.data).toEqual({ reply: "hello John Doe" });
  });
});

```

Run it with `npm run test`. The test makes a network call to `grpc.postman-echo.com`, so it requires internet access.

Unit tests run against your TypeScript source, so they don't confirm that `.proto` files are copied into `dist/`. Check the build output, or run the action in a test integration after you publish.
