In a
previous post
I discussed using REST APIs to enrich records at the time of ingest. This post will cover building the corresponding REST API that I used in that post.
This will be a very simple REST API that exposes a single endpoint
GET /api
. This endpoint returns a 200 OK response with a small piece of JSON that changes based on the value of a URL parameter
param1
. Any other path or method will return a 404.
First, we need a web server that accept HTTP requests. For this, we can use the
HandleHTTPRequest
processor (behind the scenes this includes a Jetty web server).
Add the
HandleHTTPRequest
and double click it to enter the config window. You can customise the
Port
it listens on, but I will leave it at the default 80. You must add & enable a
HTTP Context Map
- simply select
Create new service…
from the drop down and create a new one with default settings (then enable it). Next is
Allowed Paths
that controls which paths the API will respond to - this is a regex pattern so can include many paths, but in my case I am only allowing one (/api). Lastly, set all methods except
GET
to
false
(e.g.
Allow Post, Allow PUT,
etc).
That’s all we need to accept HTTP Requests. Hit Apply.
Now, we need to return a response. Add a new
HandleHTTPResponse
. Set the
HTTP Status Code
to 200 and for
HTTP Context Map
select the same service you created for the HandleHTTPRequest. That’s it, hit Apply.
Now connect the
Success
relationship from
HandleHTTPRequest
to
HandleHTTPResponse
. Auto-terminate any other relationships (or add them to funnels if you want).
Now that we have a basic Request>Response flow, we want to add some logic in the middle. Let’s quickly demonstrate returning a different response based on a URL parameter.
Add a new
RouteOnAttribute
to the flow and enter the configuration.
Add a new property with the
+
icon and name it
val1.
The value of this property will be:
${http.param.param1:equals('val1')}
Do the same for
val2
as below.
Create a copy of the HandleHTTPResponse (click on it, Ctrl+C, Ctrl+V) and modify the config of the copy. Change the
HTTP Status Code
to
201
.
Now connect them all together. The
HandleHTTPRequest
should feed in to the
RouteOnAttribute
. From
RouteOnAttribute
, the
val1
relationship should go to the first
HandleHTTPResponse
with the 200 response code - the
val2
relationship goes to the second, with the 201 response code. See the screenshot below.
Start all the processors and test it with cURL. In this cURL we are passing a URL parameter
param1
with the values
val1
or
val2
. This parameter becomes an attribute on the FlowFile for this request (
http.param.param1)
. The
RouteOnAttribute
accesses this attribute and routes based on the value, giving us a
200
response for
val1
or a
201
response for
val2
.
In reality, we don’t really want to get different HTTP responses like this. The user of my API (the other NiFi flow) actually wanted some JSON in response. So let’s do that.
We want to return some JSON in the body of the resonse, so let’s add a
ReplaceText
. As the FlowFile content is currently empty (null), we need to set
Replacement Strategy
to
Always Replace
and
Evaluation Mode
to
Entire Text
(you will see mime-type errors without these settings).
Lastly, we need to set
Replacement Value
to the full content we want in our response, which is going to be a bit of JSON.
"result": "you sent val1"
We need to do the same thing for
val2
, so make a copy of the
ReplaceText
processor and change the JSON to say
val2
instead.
We don’t both
HandleHTTPResponse
’s anymore, so delete the second one. Now connect the
RouteOnAttribute
to the 2
ReplaceText
’s and then connect the 2
ReplaceText
’s to the single
HandleHTTPResponse
.
Start all the processors and test it wth curl.
$curllocalhost/api?param1=val1"result":"you sent val1"$curllocalhost/api?param1=val2"result":"you sent val2"
We now have a very basic REST API that is able to return different results based on user provided parameters. You can extend this with much more complexity - supporting more paths, allowing more HTTP methods, accepting more parameters, doing more complex routing logic and handling error with appropriate HTTP response codes.
Find the flow definition here if you’d like to import the whole thing.