Tutorial2 - A simple web server, part 2

Requirements

Part2 - Creating a web service.

In this final part, we will create a web server and invoke the JSON data filtering logic we built during the previous part.

Step1 - Create a server flow - Understanding triggers

In this step, we will create a web server and send a simple "hello world"

server The above animation shows how to create an HTTP server using core/http package.

We will start by creating a new flow. Call it as server.flw. Next, we will use the core/http package to create a web server that will accept requests on the configured port. Click open the core/http package in the packages panel and drag the httpServer module into the canvas. Click OK in the warning dialog and httpServer is now the starting step.

httpServer is a special type of module called as a trigger module. A trigger module will always be the first step in a flow. Such a flow, called as trigger flows, cannot be invoked by other flows, instead, they will trigger new instances on their own. One example of a trigger module is the timer module, which will repeatedly trigger a new instance after the configured time interval.

The httpServer module will trigger a new instance when a request hits the configured port and/or the route(s). Let's begin by updating the port parameter in httpServer. For the tutorial, we will use a port value of 8005. Leave the rest of the properties as it is.

Every HTTP request should be terminated with a response. To send a response back, we will drag the httpResponse module from the core/http package place it as the outgoing step of httpServer. Click open the httpResponse's input tab and set the content property as "Hello world".

That's it. Start the flow as usual and a web server would be running on your localhost on the given port. Navigate to URL http://localhost:8005/ and you should see a "Hello world" printed in the browser window.

Step2 - Configuring the server

In this step, we will learn how to setup multiple routes in the httpServer.

adding routes The above animation shows configuring routes in the httpServer module

Routes are special regular expressions that specify the pattern of requests that should be accepted by the server. To add a route, click on the routes array in httpServer's input. Give a value "/" in the pattern field and "home" in the label field. Leave other fields as default. If you add a route, httpServer will only listen for requests matching the given pattern.

We will add another route with a pattern "/find" and name it as "find".

Step3 - Create conditional transitions for each route

In this step, we will add conditional transitions to handle each route separately.

server The above animation shows adding multiple transitions for each route.

As you noticed, when you add a route, the output schema of httpServer automatically gets updated to include the given route as a boolean flag. During runtime, when the request is triggered for this route, the flag will be set. This can be used in any expressions including conditions for transitions. We can use a feature called as the conditional transition to channel the data flow to separate branches for each route.

To add a conditional transition, click on the transition from httpServer to httpResponse. In the configuration panel, select the type dropdown and choose the option - "Conditional - The transition will happen only... ". After choosing conditional options, a field named Condition will appear below that. Focus on the expression field for Condition and the regular expression drop down will pop up. Select Data->httpServer->routes->home from the drop down. Now the transition will happen only if the condition evaluates to true.

To handle the second route, add another httpResponse from the core/http package. Rename the step to findResponse. Set its content field to value "You requested /find". Now add a transition from httpServer to findResponse. Repeat the transition condition, this time choose the output variable - Data->httpServer->routes->find.

If you execute the flow you can see that the requests are handled separately.

Step4 - Capturing HTTP query parameters

In this step, we will see how we can capture parameters from HTTP requests.

server The above animation shows using query parameters.

httpServer, like any step, provides an output as defined by the output schema. In this step, we are interested in a particular one - httpServer.output.query. Since the query parameter in the HTTP requests are dynamic, it is not available directly in the autocomplete drop down. Instead, the query object is defined as an object with extendable properties. That means you get to type whatever additional field name you expect as the HTTP query parameters.

For this tutorial, we will accept a query parameter named 'color'. Click open findResponse and set the value of the content field as shown below.

concat("You requested /find with color", httpServer.output.query.color)

Click run and enter a URL like http://localhost:8005/?color=red and you should see the color printed back in the response.

Step5 - Calling a sub flow and using the return value

In this step, we will pass the query parameter to the sub flow and pipe its results back to the browser.

server The above animation shows invoking the read.flw as a sub flow and piping the result back to the response.

First, we will invoke the flow read.flw created in the previous part. There are two ways to do it - Using the core/executeFlow and passing the sub flow's path or simply drag and drop the flow file from the project tree. It will automatically create the executeFlow module and set the path automatically.

call-subflow|width:550

As you can notice, the when a sub flow is called, the executeFlow module will automatically ask for the input based on the sub flow's input schema and the output of the step maps the output of the sub flow. This allows us to map the output of the sub flow in the subsequent steps inside the current flow.

The next step is to pass the query parameter color into the sub flow's input. We simply map the expression httpServer.output.query.color into the input field color of the sub flow.

Next, we will map the output of the read step to the content property in findResponse step.

Since the output of the read step is an object, we need to convert it into a string. We can use the built-in method stringify under JSON namespace to do that. The final expression would look like:

stringify(read.output)

map-response|width:500 Pipe the output from the read to the http response

Step6 - Starting trigger flows - Execute as project

We have covered how trigger flow works during the tutorial. Let's see how to execute trigger flows in different ways.

Start like regular flows.

Triggers can be started like regular flows. From the designer, you can simply click the Debug or the Run button. The command line version is also similar to running regular flows, except that the program will not end and return a result like a regular flow.

single flow

Start as the whole project.

If there are triggers in the project, the project can be started as a whole. To do that, Go to the main menu, Select Debug->Debug Project... The designer will try to start the whole project by scanning the given directory for any triggers and iff a trigger found, it is started automatically.

Command line version:

If you pass a folder instead of a flow file, the engine will start the whole project by starting all the trigger flows at once. This is useful for large projects with multiple background jobs and web servers.

codeflow run <project-folder>

run project

It will start all the trigger flows and keep running until the lifetime of the triggers.

Summary

Congrats on completing the tutorial. In this section, we have covered the following concepts:

  • Trigger modules.
  • Creating a simple web server using core/http package.
  • Configuring paths.
  • Conditional transitions.
  • Calling sub flows.
  • Executing a whole project.

We have only scratched the surface with the above basic tutorials. You can learn more about Codeflow in subsequent sections.

What's next?