Postman-powered testing of Akka Serverless gRPC APIs
Jeremy Pollock
Posted on January 6, 2022
Over the holidays, 2021, Postman 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, 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.
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:
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.
- We pick up the first part of our
fully qualified service name
in thepackage
line. In this case, as we start building our URI,/com.example
starts us off! - 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 ourfully qualified service name
string, we will append theCounterService
to form/com.example.CounterService
. And as such, we're done with that. -
Onward to determining the
method
name. For each of therpc
lines, contained within theservice
, there will be an URI created, appendingmethod
to ourfully 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!
- If you don't have Postman installed on your machine, head here.
- Once you're ready, open up the Postman application.
-
Click on New.
-
Click on HTTP Request.
Change HTTP Method from GET to POST.
-
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:
Click on Body.
-
Click on raw.
If you're not finding the above two, they're here:
-
Fill in the large text area with the above
POST
body:{"counter_id": "foo", "value": 1}
-
Change Text type to JSON.
Click Send.
Profit!
Your Postman app should looking something like this. 200 OK
, in green, means success!
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.
Where to go next
If you're wanting to build out your first gRPC
API (or just build more!), head to Akka Serverless 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.
Posted on January 6, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.