Build a RESTful API in Go using AWS Lambda

In this post we will learn to design, build, and deploy a RESTful API in Go using AWS Lambda. Before starting, let me give you a brief introduction about AWS Lambda.

What is AWS Lambda?
AWS Lambda is a serverless compute service that runs our code in response to events and automatically manages the underlying compute resources for us. We can use AWS Lambda to extend other AWS services with custom logic, or create our own back-end services that operate at AWS scale, performance, and security. AWS Lambda can automatically run code in response to multiple events, such as HTTP requests via Amazon API Gateway, modifications to objects in Amazon S3 buckets, table updates in Amazon DynamoDB, and state transitions in AWS Step Functions.

Lambda runs our code on high-availability compute infrastructure and performs all the administration of the compute resources, including server and operating system maintenance, capacity provisioning and automatic scaling, code and security patch deployment, and code monitoring and logging. All we need to do is supply the code.

Now, lets’ start with building an API that will help a local movie rental shop in managing their available movies.

API architecture

The following diagram shows how the API Gateway and Lambda fit into the API architecture:

image1

AWS Lambda empowers microservice development. That being said, each endpoint triggers a different Lambda function. These functions are independent of one another and can be written in different languages, thereby leading to scaling at function level, easier unit testing, and loose coupling.

All requests from clients first go through the API Gateway. It then routes the incoming request to the right Lambda function accordingly.

Note that a single Lambda function can Handle multiple HTTP methods (GET, POST, PUT, DELETE, and so on). It’s advisable to create multiple Lambda functions for each functionality in order to leverage the power of microservices. However, building a single Lambda function to handle multiple endpoints could be a good exercise.

Endpoints design

Now that the architecture has been defined, it’s time to go through the implementation of the functionalities described in the above diagram. Instead of hard coding the HTTP status code, you can use the net/http Go package and use a built-in status code variables such as http.StatusOK, http.StatusCreated, http.StatusBadRequest, http.StatusInternalServerError, and so on.

The GET method

The first feature to implement is listing movies. That’s where the GET method comes into play. Lets’ start with it following steps:

Step 1: Create a Lambda function that registers a findAll handler. This handler transforms a list of movies to a string and then returns this string wrapped by the APIGatewayProxyResponse variable along with a 200 HTTP status code. It also handles errors in case of conversion failure. The handler implementation is as follows:


package main

import (
  "encoding/json"

  "github.com/aws/aws-lambda-go/events"
  "github.com/aws/aws-lambda-go/lambda"
)

var movies = []struct {
  ID int `json:"id"`
  Name string `json:"name"`
}{
    {
      ID: 1,
      Name: "Avengers",
    },
    {
      ID: 2,
      Name: "Ant-Man",
    },
    {
      ID: 3,
      Name: "Thor",
    },
    {
      ID: 4,
      Name: "Hulk",
    }, {
      ID: 5,
      Name: "Doctor Strange",
    },
}

func findAll() (events.APIGatewayProxyResponse, error) {
  response, err := json.Marshal(movies)
  if err != nil {
    return events.APIGatewayProxyResponse{}, err
  }

  return events.APIGatewayProxyResponse{
    StatusCode: 200,
    Headers: map[string]string{
      "Content-Type": "application/json",
    },
    Body: string(response),
  }, nil
}

func main() {
  lambda.Start(findAll)
}

Instead of hard coding the HTTP status code, you can use the net/http Go package and use a built-in status code variables such as http.StatusOK, http.StatusCreated, http.StatusBadRequest, http.StatusInternalServerError, and so on.

Step 2: Create a script file with the following content to build a Lambda function deployment package, a .zip file consisting of your code and any dependencies, as follows:

#!/bin/bash

echo "Build the binary"
GOOS=linux GOARCH=amd64 go build -o main main.go

echo "Create a ZIP file"
zip deployment.zip main

echo "Cleaning up"
rm main

Step 3: Execute the following commands to build the deployment package as .zip file:

$ chmod +x build.sh
$ ./build.sh

Step 4: Configure AWS CLI using steps mentioned here. Once configured, create an AWS role with name as FindAllMoviesRole following the steps mentioned here and verify if it is successfully created:

$ aws iam get-role --role-name FindAllMoviesRole

Above command should give the response as shown in a screenshot below:

Image15

Step 5: Next, create a new Lambda function using the AWS CLI as follows:

aws lambda create-function --function-name FindAllMovies \
     --zip-file fileb://deployment.zip \
     --runtime go1.x --handler main \
     --role arn:aws:iam::ACCOUNT_ID:role/FindAllMoviesRole \
     --region us-east-1

Once function is created it will give us the output same as shown in a screenshot below:

Image16.PNG

Step 6: Heading back to the AWS Lambda Console, you should see that the function has been created successfully:

image2

Step 7: Create a sample event with an empty JSON, as the function doesn’t expect any argument, and click on the Test button:

image3.png

You will notice in the previous screenshot that the function returns the expected output in a JSON format.

Step 8: Now that the function has been defined, you need to create a new API Gateway in order to trigger it:

image4

Step 9: Next, from the Actions drop-down list, select Create resource and name it movies:

image5

Step 10: Expose a GET method on this /movies resource by clicking on Create Method. Choose Lambda Function under the Integration type section and select the FindAllMovies function:

image6.png

Step 11: To deploy the API, select Deploy API from the Actions drop-down list. You’ll be prompted to create a new deployment stage:

image7

Step 12: Once the deployment stage is created, an invocation URL will be displayed:

image8

Step 13: Point your browser to the URL given or use a modern REST client like Postman or Insomnia. You can go with the cURL tool as it is installed by default on almost all operating systems:

curl -sX GET https://51cxzthvma.execute-api.us-east-1.amazonaws.com/staging/movies | jq '.'

The above command will return a list of movies in a JSON format:

image9

When calling the GET endpoint, the request will go through the API Gateway, which will trigger the findAll handler. This returns a response proxied by the API Gateway to the client in a JSON format.

Now that the findAll function has been deployed, you can implement a findOne function to search for a movie by its ID.

The GET method with parameters

The findOne handler expects the APIGatewayProxyRequest argument that contains the event input. Then, it uses the PathParameters method to get the movie ID and validate it.

If the ID provided is not a valid number, the Atoi method will return an error, and a 500 error code will be returned to the client. Otherwise, a movie will be fetched based on the index and returned to the client with a 200 OK status wrapped in APIGatewayProxyResponse:


...

func findOne(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {

  id, err := strconv.Atoi(req.PathParameters["id"])

  if err != nil {

    return events.APIGatewayProxyResponse{

      StatusCode: 500,

      Body:       "ID must be a number",

    }, nil

  }

  response, err := json.Marshal(movies[id-1])

  if err != nil {

    return events.APIGatewayProxyResponse{

      StatusCode: 500,

      Body:       err.Error(),

    }, nil

  }

  return events.APIGatewayProxyResponse{

    StatusCode: 200,

    Headers: map[string]string{

      "Content-Type": "application/json",

    },

    Body: string(response),

  }, nil

}

func main() {

  lambda.Start(findOne)

}

Similar to the FindAllMovies function, create a new Lambda function for searching for a movie:

aws lambda create-function --function-name FindOneMovie \
    --zip-file fileb://deployment.zip \
    --runtime go1.x --handler main \
    --role arn:aws:iam::ACCOUNT_ID:role/FindOneMovieRole \
    --region us-east-1

Go back to API Gateway console, create a new resource, expose the GET method, and then link the resource to the FindOneMovie function. Note the use of the {id} placeholder in the path. The value of id will be made available via the APIGatewayProxyResponse object. The following screenshot depicts this:

image10.png

Redeploy the API and use the following cURL command to test the endpoint:

curl -sX https://51cxzthvma.execute-api.us-east-1.amazonaws.com/staging/movies/1 | jq '.'

The following JSON will be returned:

image11

When the API URL is invoked with an ID, the movie corresponding to the ID is returned if it exists.

The POST method

Now you know how the GET method works with and without path parameters. The next step is to pass a JSON payload to a Lambda function through the API Gateway. The code is self-explanatory. It converts the request input to a movie structure, adds it to the list of movies, and returns the new list of movies in a JSON format:


package main

import (
  "encoding/json"
  "strconv"
  "github.com/aws/aws-lambda-go/events"
  "github.com/aws/aws-lambda-go/lambda"
)

type Movie struct {
  ID int `json:"id"`
  Name string `json:"name"`
}

var movies = []Movie{
  Movie{
    ID: 1,
    Name: "Avengers",
  },
 ...
}

func insert(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
  var movie Movie
  err := json.Unmarshal([]byte(req.Body), &movie)
  if err != nil {
    return events.APIGatewayProxyResponse{
      StatusCode: 400,
      Body: "Invalid payload",
    }, nil
  }

  movies = append(movies, movie)

  response, err := json.Marshal(movies)
  if err != nil {
    return events.APIGatewayProxyResponse{
      StatusCode: 500,
      Body: err.Error(),
    }, nil
  }

  return events.APIGatewayProxyResponse{
    StatusCode: 200,
    Headers: map[string]string{
      "Content-Type": "application/json",
    },
    Body: string(response),
  }, nil
}

func main() {
  lambda.Start(insert)
}

Next, create a new Lambda function for InsertMovie with the following command:

aws lambda create-function --function-name InsertMovie \
     --zip-file fileb://deployment.zip \
     --runtime go1.x --handler main \
     --role arn:aws:iam::ACCOUNT_ID:role/InsertMovieRole \
     --region us-east-1

Next, create a POST method on the /movies resource and link it to the InsertMovie function:

image12.png

To test it out, use the following cURL command with the POST verb and the -d flag, followed by a JSON string (with the id and name attributes):

curl -sX POST -d '{"id":6, "name": "Spiderman:Homecoming"}' https://51cxzthvma.execute-api.us-east-1.amazonaws.com/staging/movies | jq '.'

The above command will return the following JSON response:

image13.png

As you can see, the new movie has been inserted successfully. If you test it again, it should work as expected:

curl -sX POST -d '{"id":7, "name": "Iron man"}' https://51cxzthvma.execute-api.us-east-1.amazonaws.com/staging/movies | jq '.'

The preceding command will return the following JSON response:

image14.png

As you can see, it was successful and the movie was again inserted as expected, but what if you wait few minutes and try to insert a third movie? The following command will be used to execute it again:

curl -sX POST -d '{"id":8, "name": "Captain America"}' https://51cxzthvma.execute-api.us-east-1.amazonaws.com/staging/movies | jq '.'

Once again, a new JSON response will be returned:

image15

You will find that the movies with IDs 6 and 7 have been removed; why did this happen?  It’s simple. Lambda functions are stateless.

When the InsertMovie function is invoked for the first time (first insert), AWS Lambda creates a container and deploys the function payload to the container. Then, it remains active for a few minutes before it is terminated (warm start), which explains why the second insert passed. In the third insert, the container is already terminated, and hence Lambda creates a new container (cold start) to handle the insert.

This is why the previous state is lost. The following diagram illustrates the cold/warm start issue:

image16.png

This explains why Lambda functions should be stateless and why you should not make any assumptions that the state will be preserved from one invocation to the next.

If you found this article helpful and want to learn more about Go, you can explore Hands-On Serverless Applications with Go written by Mohamed Labouardy, an AWS solution architect, and tech-reviewed by yours truly, this book will take you on a journey of learning the serverless architecture with Go and help you build highly modern, scalable, and efficient software applications.

Complete source code is hosted on github.

Advertisements

Working with Glide – Vendor Package Management for Go

In this post, we will use Glide to manage the dependencies of a Go project. Before starting, let me give you a brief introduction about Glide.

What is Glide?
Glide is a package manager for Go that is conceptually similar to package managers for other languages such as NPM for Node.js, Pip for Python, and so forth which records dependency information in a glide.yaml file and utilizes vendor/ directories, so that different projects can have different versions of the same dependencies.

Now, lets’ start with creating a small project with a single Go file which will give colourized outputs, following steps:

Step 1: Install Glide executing the following command:

$ curl https://glide.sh/get | sh

Above command will add glide binary in $GOPATH/bin.

Step 2: Move to $GOPATH/src to create a directory – golang-glide-example executing the following command:

$ cd $GOPATH/src && mkdir golang-glide-example

Step 3: Move to $GOPATH/src/golang-glide-example to create a Go file which will give colourized outputs executing the following command:

$ cd $GOPATH/src/golang-glide-example && touch color.go

Step 4: Copy the following code to color.go:

package main

import (
	"github.com/fatih/color"
)

func main() {
	color.Red("We have red")
	color.Blue("Prints %s in blue.", "text")
}

Step 5: Move to $GOPATH/src/golang-glide-example and execute the following command:

$ cd $GOPATH/src/golang-glide-example && glide init --non-interactive

Above command will create glide.yaml which contain all the dependencies to run the project.

Step 6: Move to $GOPATH/src/golang-glide-example, install all dependencies and build the project executing the following command:

$ cd $GOPATH/src/golang-glide-example && glide install && go build

Above command will download and export the dependencies and the output logs will look like as shown in a screenshot below:

Screen Shot 2018-04-14 at 12.20.06 PM

Step 7: Move to $GOPATH/src/golang-glide-example and Run golang-glide-example executing following command:

$ cd $GOPATH/src/golang-glide-example && ./golang-glide-example

which will give us the colourized output same as shown in a screenshot below:

Screen Shot 2018-04-14 at 12.20.24 PM

The complete source code is hosted on GitHub.

Optimizing Angular Application Using Template Cache

In one of my projects we were experiencing lot of server calls for the HTML files which we optimized it using Angular Template Cache and in this post I will showcase a thin example of our problem and how Angular Template Cache solved it using a Cache object.

As creating an application is not a part of this post, I already created angular-grunt-example which is an angular application with Grunt as a JavaScript task runner and available for clone from GitHub.

In an image below, one can see a server call each time we access .jsp file:

all-calls

to avoid all these server calls we will be implementing angular template cache, following below steps:

Step 1: Install grunt-html2js Grunt plugin with npm, as follows:

npm install grunt-html2js --save-dev

Above command will add grunt-html2js as devDependencies in package.json.

Step 2: Create html2js Grunt task in Gruntfile:

html2js: {
	  options: {
		base: 'src',
		module: 'myapp.template',
		singleModule: true,
		useStrict: true,
		htmlmin: {
		  collapseBooleanAttributes: true,
		  collapseWhitespace: true,
		  removeAttributeQuotes: true,
		  removeComments: true,
		  removeEmptyAttributes: true,
		  removeRedundantAttributes: true,
		  removeScriptTypeAttributes: true,
		  removeStyleLinkTypeAttributes: true
		},
		rename: function (url) {
				return url.replace('main/webapp/', '');
		}
	  },
	  main: {
		src: ['/pages/**/*.jsp'],
		dest: '/scripts/template.js'
	  }
}

module option specified above is the angular module name.

rename option specified above is used to make file paths match up exactly same as called from application, for example in application we are accessing .jsp pages as ‘pages/page-posts.jsp’, ‘pages/page-posts-list.jsp’ and ‘pages/page-authors.jsp’, etc and the file path generated is ​main/webapp/pages/angular-material.jsp`, so replace function strip ​main/webapp/ from each template path to produce a module identifier for the template.

Step 3: Load html2js task from the grunt-html2js Grunt plugin:

grunt.loadNpmTasks('grunt-html2js');

Step 4: Register html2js task with default task of grunt, so that when we run “default” task “uglify:dist” and “html2js” tasks run automatically, as follows:

grunt.registerTask('default', ['uglify:dist', 'html2js']);

On running the default task, template.js will be created in /webapp/scripts/ directory as a result of html2js task execution.

Step 5: Load tempalte.js generated by the html2js task as the first script after Angular and all related dependencies are loaded, as follows:

<spring:url value="/js/jquery-2.2.3.min.js" var="jQueryUrl"
<spring:url value="/js/angular.min.js" var="angularJsUrl"
<spring:url value="/scripts/template.js" var="templateJsUrl"
<spring:url value="/js/application.js" var="applicationJsUrl"

Step 6: Make myapp.template as the first dependency for the application module:

var myapp = angular.module('myapp', [ 'myapp.template', 'ui.router' ]);

Once done, browsing the application will not make call to the server for the .jsp files instead it fetch it from $templateCache which is a Cache object defined in template.js, as shown in the below image:

cache-calls.png

The complete source code is hosted on github.

Creating Your Own Package in Go

A package in Go is a namespace that organizes a set of related files. We can think of packages as being similar to different folders. Go standard library comes with a number of packages which can be used for building real-world applications. Moreover Go supports writing our own packages promoting code modularization and better composability of applications following certain rules, like all source files within the package must declare the same package-name. Identifiers, Function and Types will be exported to other packages if the first letter of the identifier name starts with an uppercase letter.

In post Creating HTTP Server in Go we already updated Path with GOROOT, GOPATH and GOBIN, so as part of this post we will simply start with creating a package in Go following below steps:

Step 1: Create a directory inside your workspace to keep source code of a package, for me it’s numberutil under $GOPATH/github.com/arpitaggarwal:

$ mkdir -p $GOPATH/github.com/arpitaggarwal/numberutil

Step 2: Move to directory we created in previous step and create a file named decimalTobinary.go inside it, as follows:

$ cd $GOPATH/github.com/arpitaggarwal/numberutil
$ touch decimalTobinary.go

Copy following Go code:

package numberutil

import "strconv"

//Convert Decimal to Binary
func DecimalToBinary(decimal int64) string {
	binary := strconv.FormatInt(decimal, 2)
	return binary
}

Above code contains a single go function which takes Decimal number as input and convert it to Binary using strconv.FormatInt function.

Step 3: Build numberutil package using go tool, as follows:

$ cd $GOPATH
$ go build github.com/arpitaggarwal/numberutil

Step 4: Next we will create number-util.go with a main() method to use DecimalToBinary function from numberutil package we created, as follows:

$ cd $GOPATH/github.com/arpitaggarwal/
$ touch number-util.go

Copy following Go code:

package main

import (
	"fmt"
	"github.com/arpitaggarwal/numberutil"
)

func main() {
	i23 := int64(23)
	fmt.Printf("Decimal to Binary for 23 is %s \n", numberutil.DecimalToBinary(i23))
}

Step 5: Install number-util.go using go tool:

$ go install $GOPATH/github.com/arpitaggarwal/number-util.go

Above command compile number-util.go and generate executable binary file of it in $GOROOT/bin or $GOBIN directory.

Step 6: Run number-util.go moving to golang directory:

$ cd golang
$ ./go/bin/number-util

Step 7: Now we will generate documentation of numberutil package we created which is as simple as running godoc tool with -http flag for the port number from the terminal, as follows:

godoc -http=:9999

Now, opening http://localhost:9999/pkg/github.com/arpitaggarwal/numberutil/ in browser will show us the documentation of numberutil package we have created.

Go also support using third party libraries or packages by installing them using go get command or copying it manually to $GOPATH/src or $GOPATH for example, If we want to use Reverse function from “github.com/golang/example/stringutil” package which is not available by default in go standard library then we can install it as:

$ cd golang
$ go get github.com/golang/example/stringutil

Or clone the package and copy it to $GOPATH/src or $GOPATH directory, then use it as:


package main

import (
	"fmt"
	"github.com/golang/example/stringutil"
)
func main() {
	fmt.Println(stringutil.Reverse("!olleh"))
}

Complete source code is hosted on github.

Creating HTTP Server in Go

In this post, we will create a simple HTTP server in Go following some simple steps. Before starting, let me give you a brief introduction about Go.

What is Go?

Go is an open source programming language which is case sensitive and statically-typed with syntax similar to that of C language created at Google in 2007. It compiles very quickly, supports concurrency at the language level and provides garbage collection, type safety, dynamic-typing capability, many advanced built-in types such as variable length arrays and key-value maps though there is no support for generic programming, type inheritance, method or operator overloading, pointer arithmetic and assertions.

Now, let’s start with setting up the environment to run our first HTTP Server, following below steps:

Step 1: Download and extract Go in any directory, for me it’s golang under directory /Users/ArpitAggarwal/ as follows:


$ mkdir golang
$ cd golang
$ wget https://storage.googleapis.com/golang/go1.8.3.darwin-amd64.tar.gz
$ tar -xvzf go1.8.3.darwin-amd64.tar.gz

Step 2: Update Path with GOROOT, GOPATH and GOBIN, as follows:

$ export GOROOT=/Users/ArpitAggarwal/golang/go
$ export GOPATH=$GOROOT/src
$ export GOBIN=$GOROOT/bin
$ export PATH=$PATH:$GOPATH:$GOBIN

GOROOT refers to the go installation directory.
GOPATH refers to our go workspace directory or where go build is done.
GOBIN refers to the directory where go generate executable binaries.

Step 3: Verify if GOPATH is set properly on Bash, executing following command:

$ env | grep -E "^GO"

Step 4: Create a directory inside workspace to keep source code:

$ mkdir -p $GOPATH/github.com/arpitaggarwal

Step 5: Move to directory we created in previous step and create a file named webserver.go inside it, as follows:

$ cd $GOPATH/github.com/arpitaggarwal
$ touch webserver.go

Copy following Go code:

package main

import (
	"fmt"
	"log"
	"net/http"
)

func helloWorld(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Hello World!") // send data to client side
}

func main() {
	http.HandleFunc("/", helloWorld)         // set router
	err := http.ListenAndServe(":8080", nil) // set listen port
	if err != nil {
		log.Fatal("Error while starting GO http server on port - 8080 : ", err) //log error and exit in case of error at server boot up
	}
}

Let’s understand what does each line mean.

package main – It defines the package name of the program.

import (“fmt”, “log” “net/http”) – is a preprocessor command which tells the Go compiler to include all files from fmt, log and net/http package.

func helloWorld(w http.ResponseWriter, r *http.Request) – is a go function.

main() – is the main function where the program execution begins.

Step 6: Install webserver.go using go tool, as follows:


$ go install $GOPATH/github.com/arpitaggarwal/webserver.go

Above command compile webserver.go and generate executable binary file of it in $GOROOT/bin or GOBIN.

Step 7: Run webserver moving to golang directory, as follows:


$ cd golang
$ ./go/bin/webserver

Now, opening http://localhost:8080/ in browser or executing command:

curl http://localhost:8080

will show us “Hello World!” as response.

Step 8: Optionally we can run binary file in background using nohup command, as follows:


$ cd golang
$ nohup ./go/bin/webserver &;

Complete source code is hosted on github.

Routing in React

In post Handling Events in React we extended react-app which we built as part of Rendering RESTful service with React to support add and delete employees operation.

Now as part of this post we will add Routing capabilities to it using react-router, by defining an additional column Details as a Link, click of which take you to the details of the employee selected, following below steps:

Step 1. Install react-router-dom as a dependency:

npm install react-router-dom --save

Step 2: Update react-handling-events/app/main.js to import HashRouter, Route and Switch from react-router-dom along with EmployeeDetail component which we will define later in the post.

import { HashRouter, Route, Switch } from 'react-router-dom';
import EmployeeDetail from './components/employee-detail.jsx'

Step 3: Instead of rendering the ReactApp component directly, we will use HashRouter and Route to render it:

ReactDOM.render(
	<HashRouter>
	 <Switch>
	    <Route exact path="/" component={ReactApp}/>
        <Route exact path="/employee/:id" component={EmployeeDetail}/>
     </Switch>
    </HashRouter>,
   document.getElementById('react')
)

Eventually, react-handling-events/app/main.js should look like:

'use strict';
const React = require('react');
const ReactDOM = require('react-dom')

import { HashRouter, Route, Switch } from 'react-router-dom';
import ReactApp from './components/react-app.jsx'
import EmployeeDetail from './components/employee-detail.jsx'

ReactDOM.render(
    <HashRouter>
      <Switch>
        <Route exact path="/" component={ReactApp}/>
        <Route exact path="/employee/:id" component={EmployeeDetail}/>
      </Switch>
    </HashRouter>,
  document.getElementById('react')
)

HashRouter tag specified above puts route information into the hash of the URL (after the #). I preferred HashRouter over BrowserRouter because to use BrowserRouter web server must be ready to handle real URLs. When the app first loads at / it will probably work, but as the user navigates around and then hits refresh at /employee/1 web server will get a request to /employee/1 then I have to handle that URL and include JavaScript application in the response.

Switch tag specified above renders the first child Route that matches the location.

As we defined two Route tag above, let’s understand what each one of them signify.

<Route exact path=/ component={ReactApp}/renders ReactApp component when the app loads at / .

<Route exact path=/employee/:id component={EmployeeDetail}/renders EmployeeDetail component when the app loads at /employee/{id} where id is a dynamic path variable.

Step 4: Create employee-detail.jsx component inside component directory, as follows:


cd react-handling-events/app/components/
touch employee-detail.jsx

And copy the below content:

import React, { Component } from 'react'

export default class EmployeeDetail extends Component {
  render(){
    if(this.props.match !== undefined && this.props.match.params !== undefined ){
      return(
        <div>
          <table style={{border: '1px solid black'}}>
            <tbody><tr>
              <td>Id</td>
              <td>Name</td>
              <td>Department</td>
            </tr>
            <tr>
              <td>{this.props.match.params.id}</td>
              <td>{this.props.location.query.employee.name}</td>
              <td>{this.props.location.query.employee.department}</td>
            </tr>
          </tbody>
        </table>
      </div>
    );
  }
  return (null);
}
}

{this.props.match.params.id} specified above is to access route params in the component.

{this.props.location.query.employee.name} specified above is to access query params in the component which React-router injects as a location property.

Step 5: Update EmployeeList component render method to have an additional Details column:

const React = require('react');
import Employee from './employee.jsx'

export default class EmployeeList extends React.Component{
    
    render() {
		var employees = this.props.employees.map((employee, i) =>
			<Employee key={i} employee={employee} deleteEmployee={() => this.props.deleteEmployee(employee.name)}/>
		);
		
		return (
			<table>
				<tbody>
					<tr>
						<th>ID</th>
						<th>Name</th>
						<th>Department</th>
						<th>Delete</th>
						<th>Details</th>
					</tr>
					{employees}
				</tbody>
			</table>
		)
	}
}

Step 6: Update Employee component to import Link from react-router-dom along with render method to have an additional column Details as a Link, click on which take you to the details of the employee:

const React = require('react');
import DeleteEmployee from './delete-employee.jsx'
import { Link } from 'react-router-dom';

export default class Employee extends React.Component{
	render() {
		return (
			<tr>
				<td>{this.props.employee.id}</td>
				<td>{this.props.employee.name}</td>
				<td>{this.props.employee.department}</td>
				<td><DeleteEmployee deleteEmployee={this.props.deleteEmployee}/></td>
				<td><Link to={{ pathname:'/employee/'+this.props.employee.id, query: {employee: this.props.employee } }}>Details</Link></td>
			</tr>
		)
	}
}

With all in place directory structure should look like:

Screen Shot 2017-05-28 at 12.27.17 am

Now re-run the application and visit http://localhost:8080it should look like as shown in below screenshot, once you add few employee.

Screen Shot 2017-05-28 at 12.30.17 am
Complete source code is hosted on github.

Disabling Caching at Runtime if Couchbase Connection Failed

In our application we have number of REST API calls out of which some of them are flagged with @Cacheable to cache the response. Cache is backed by Couchbase and whenever application fails to connect Couchbase node then application goes down.

Which is what we are not expecting, we expect data should be served from the source system whenever connection failed, basically bypassing the cache and as soon as the Couchbase node is up or connection established response should be served from cache.

To achieve the same mechanism, I returned “null” whenever connection fails or there is a Cache miss overriding CouchbaseCacheManager, as follows:


import java.util.Map;
import org.springframework.cache.Cache;
import com.couchbase.client.spring.cache.CacheBuilder;
import com.couchbase.client.spring.cache.CouchbaseCacheManager;

public class CouchbaseCustomCacheManager extends CouchbaseCacheManager {

	public CouchbaseCustomCacheManager(final Map<String, CacheBuilder> initialCaches) {
		super(initialCaches);
	}

	@Override
	public Cache getCache(String name) {
		return new CouchbaseCacheWrapper(super.getCache(name));
	}

	protected static class CouchbaseCacheWrapper implements Cache {

		private final Cache delegate;

		public CouchbaseCacheWrapper(Cache couchbaseCache) {
			this.delegate = couchbaseCache;
		}

		@Override
		public String getName() {
			try {
				return delegate.getName();
			} catch (Exception e) {
				return null;
			}
		}

		@Override
		public Object getNativeCache() {
			try {
				return delegate.getNativeCache();
			} catch (Exception e) {
				return null;
			}
		}

		@Override
		public ValueWrapper get(Object key) {
			try {
				return delegate.get(key);
			} catch (Exception e) {
				return null;
			}
		}

		@Override
		public <T> T get(Object key, Class<T> type) {
			try {
				return delegate.get(key, type);
			} catch (Exception e) {
				return null;
			}
		}

		@Override
		public void put(Object key, Object value) {
			try {
				delegate.put(key, value);
			} catch (Exception e) {
				try {
					handleErrors(e);
				} catch (Exception e1) {
				}
			}
		}

		@Override
		public ValueWrapper putIfAbsent(Object key, Object value) {
			try {
				return delegate.putIfAbsent(key, value);
			} catch (Exception e) {
				return null;
			}
		}

		@Override
		public void evict(Object key) {
			try {
				delegate.evict(key);
			} catch (Exception e) {
				try {
					handleErrors(e);
				} catch (Exception e1) {
				}
			}
		}

		@Override
		public void clear() {
			try {
				delegate.clear();
			} catch (Exception e) {
				try {
					handleErrors(e);
				} catch (Exception e1) {
				}
			}
		}

		protected <T> T handleErrors(Exception e) throws Exception {
			if (e instanceof Exception) {
				return null;
			} else {
				throw e;
			}
		}
	}
}

And used it as CacheManager, as follows:

@Bean
public CacheManager cacheManager() {
	final Map<String, CacheBuilder> cache = new HashMap<>();
	for (final String appCache : "127.0.0.1,127.0.0.2,127.0.0.3".split(",")) {
			cache.put(appCache, CacheBuilder.newInstance(CouchbaseCluster
					.create().openBucket("default", "")));
	}
	return new CouchbaseCustomCacheManager(cache);
}

Complete source is available on github.