Postman-powered testing of Akka Serverless gRPC APIs
Tags: postman, microservices, akka, grpc
Published: 2022-01-06 13:39:57

Over the holidays, 2021, [Postman](https://www.postman.com/) gifted a fine upgrade to its users: beta support for the gRPC protocol in its API platform. As a Product Manager for Lightbend and helping out on its new gRPC native PaaS for building and running APIs and microservices, I was excited, to say the least. In another, recent blog [post](https://dev.to/jpollock/grpc-test-and-try-with-akka-serverless-and-evans-5gcc), I mentioned my desire to leverage UI test-and-try tools for APIs (my time in the REST API world of Mashery and PubNub was the source of such desire). In that same post though, I noted the lack of several important gRPC features, like server reflection and more robust import capabilities, as blockers; hence, my deep dive, in that post, into the CLI tool, [Evans](https://github.com/ktr0731/evans).

But just because one builds APIs and services in Akka Serverless, using gRPC as a first class citizen, doesn't mean that one has to rely solely on gRPC tooling. When running your logic on this serverless platform, you can access your developed, and running, APIs through direct HTTP requests. The product has an "always-on" feature called *HTTP transcoding*, and its default behaviour is to expose every gRPC method as a `POST` request to a `URI` of `/<fully qualified service name>/<method name>` , with that resource accepting a JSON representation of the full protobuf message. One simply has to look at their API definitions, as captured in a `protobuf` to get the URIs that are needed. Let's check out what that really means!

---
**NOTE**

If you just want to go make some API calls in Postman, you can access the demo collection using the below:
[![Run in Postman](https://run.pstmn.io/button.svg)](https://app.getpostman.com/run-collection/34862-3e1b7880-536c-41f9-a97b-8b1bef5cd88e?action=collection%2Ffork&collection-url=entityId%3D34862-3e1b7880-536c-41f9-a97b-8b1bef5cd88e%26entityType%3Dcollection%26workspaceId%3Ddee50495-76e5-4399-afea-21035ae2759d)

---
## Parsing out URIs from a protobuf 

Ready to parse, human? To read through a possibly strangely constructed file and deduce `URIs` that match the above format of `/<fully qualified service name>/<method name>`? Well, even if you're not, let's learn!

Below is a `protobuf` file. If you have no idea what `protobuf` is, you might want to check out this site: https://developers.google.com/protocol-buffers.  For purposes of this post, it can be thought of as our API contract. WSDL? OpenAPI specification? Well, like those, yes. The below file defines what API requests can be made, and what the inputs and outputs are Knowing this, we can look at the API contract and divine an API request.

	syntax = "proto3";

	// This will be part of the fully qualified service name <1>
	package com.example;
	
	import "akkaserverless/annotations.proto";
	import "google/api/annotations.proto";
	import "google/protobuf/empty.proto";
	
	option java_outer_classname = "CounterApi";
	
	message IncreaseValue {
		string counter_id = 1 [(akkaserverless.field).entity_key = true];
		int32 value = 2;
	}
	
	message DecreaseValue {
		string counter_id = 1 [(akkaserverless.field).entity_key = true];
		int32 value = 2;
	}
	
	message ResetValue {
		string counter_id = 1 [(akkaserverless.field).entity_key = true];
	}
	
	message GetCounter {
		string counter_id = 1 [(akkaserverless.field).entity_key = true];
	}
	
	message CurrentCounter {
		int32 value = 1;
	}

	// This will be part of the fully qualified service name, appended to the package name <2>
	service CounterService {
		option (akkaserverless.codegen) = {
			value_entity: {
				name: "com.example.domain.Counter"
				entity_type: "counter"
				state: "com.example.domain.CounterState"
	
			}
		};

		// each of the below can be an URI API call, appended to the fully qualified service name (separated by a forward slash) <3>
		rpc Increase (IncreaseValue) returns (google.protobuf.Empty);
		rpc Decrease (DecreaseValue) returns (google.protobuf.Empty);
		rpc Reset (ResetValue) returns (google.protobuf.Empty);
		rpc GetCurrentCounter (GetCounter) returns (CurrentCounter);
	}

Going from top to bottom, let me call out the critical parts, respective of our `URI` generation exercise.

1. We pick up the first part of our `fully qualified service name` in the `package` line. In this case, as we start building our URI, `/com.example` starts us off!
2. The second important line is the `service CounterService` one; it contains the structure of the API requests that are supported by the API service. To our `fully qualified service name` string, we will append the `CounterService` to form `/com.example.CounterService`. And as such, we're done with that.
3. Onward to determining the `method` name. For each of the `rpc` lines, contained within the `service`, there will be an URI created, appending `method` to our `fully qualified service name`. What this looks like, would be a set of partial URIs:

		/com.example.CounterService/Increase
		/com.example.CounterService/Decrease
		/com.example.CounterService/Reset
		/com.example.CounterService/GetCurrentCounter

We're ready to go! Get me to Postman!

Well, not so fast. We know that the `Always-on HTTP Transcoding` feature in Akka Serverless is expecting a `POST` with a `body`, in JSON format, representing the API inputs. Where do we find these?

As you look back at the part of the `protobuf` file that had the method names, as marked by the `rpc` designation, you will notice that immediately after the `method` name, there is additional text, wrapped in parentheses. This is the input that the API method in question is expecting. Those input names, e.g. `IncreaseValue`, map to `messages` defined further up in the `protobuf`. And from those `messages`, we can determine the appropriate JSON to send in our API requests. We simply create a JSON attribute, with values matching the data type specified in the `message`.  For example, message `IncreaseValue`:

	message IncreaseValue {
		string counter_id = 1 [(akkaserverless.field).entity_key = true];
		int32 value = 2;
	}

The following JSON could be used as an input to our API request:

	{"counter_id": "foo", "value": 1}
	

Putting the two (`URI` and `body`) together, adding the hostname of the running service, we can build a `curl` command:

	curl -XPOST -H "Content-Type: application/json" -d '{"counter_id": "foo", "value": 1}' https://nameless-thunder-9740.us-east1.akkaserverless.app/com.example.CounterService/Increase

Now we're ready for Postman!

## Creating HTTP Requests in Postman

Postman has great documentation and is a very intuitive application. So really, I'm not sure I need to lay out the steps to do this. But given that this approach is all about making everyday "normal" HTTP requests to a gRPC service/API, perhaps a quick run-through will be good.

---
**NOTE**

When you have to make a choice, blue pill or red...Wait. This isn't The Matrix! But with the recent beta release of `gRPC` support in Postman, you'll be tempted to pick that path when creating a new request. Don't! Choose HTTP Request! 

---

Let's do this!

1. If you don't have Postman installed on your machine, head [here](https://www.postman.com/).
2. Once you're ready, open up the Postman application.
3. Click on **New**.

    ![Creating a new request in Postman](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dgn1toradhk0vnyal2l3.png)

4. Click on **HTTP Request**.

    ![Choosing request type](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6lfpc5wxxpf38ad0n2ji.png)

5. Change HTTP Method from **GET** to **POST**.
6. Fill in the `Enter request URL` text field with our API URI: 
	
		https://nameless-thunder-9740.us-east1.akkaserverless.app/com.example.CounterService/Increase


	Your request should look like this now:
	

    ![Entering URI](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2ffhtkb05ww26sqj1vtr.png)

7. Click on **Body**.
8. Click on **raw**.

    If you're not finding the above two, they're here:

    ![Adding body](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s5tjdqm8p8cb8klyle3q.png)

9. Fill in the large text area with the above `POST` body:

		{"counter_id": "foo", "value": 1}
		
10. Change **Text** type to **JSON**.

    ![Changing content type](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/whsgtwu3pg4x1nieyh7f.png)

11. Click **Send**.
12. Profit!

Your Postman app should looking something like this. `200 OK`, in green, means success!

![Completed request](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9grzt4hnmn3tdc8du6nj.png)

You can rinse-and-repeat the above steps for each of the four API requests defined the `protobuf` file. You can also access a fully-finished Postman Collection (a set of API requests) by clicking on the below button.

[![Run in Postman](https://run.pstmn.io/button.svg)](https://app.getpostman.com/run-collection/34862-3e1b7880-536c-41f9-a97b-8b1bef5cd88e?action=collection%2Ffork&collection-url=entityId%3D34862-3e1b7880-536c-41f9-a97b-8b1bef5cd88e%26entityType%3Dcollection%26workspaceId%3Ddee50495-76e5-4399-afea-21035ae2759d)

###Where to go next
If you're wanting to build out your first `gRPC` API (or just build more!), head to [Akka Serverless](https://console.akkaserverless.lightbend.com/p/register#) to get a free account. Or if you want to read first, the docs are at https://developer.lightbend.com/docs/akka-serverless/quickstart/index.html.