Webhook with Spring Boot and H2 Database

What exactly is a webhook? A webhook is one of the best ways for real-time communication with your application. Unlike typical APIs, a webhook delivers data to the applications which means that you get data immediately when it occurs. This makes it much more efficient for both provider and consumer. In the webhook you can register […]

by Stoyan Ivanov

August 29, 2017

5 min read

hand 2208491 1920 - Webhook with Spring Boot and H2 Database

What exactly is a webhook?

A webhook is one of the best ways for real-time communication with your application. Unlike typical APIs, a webhook delivers data to the applications which means that you get data immediately when it occurs. This makes it much more efficient for both provider and consumer. In the webhook you can register your URLs which it will target when the information is being streamed. A webhook makes a simple HTTP request to your app and you receive data.

Creating your own webhook

For an implementation I use a Spring Boot framework and I store my webhook in H2 database. H2 is an in-memory DB which is recreated every time we start the application. For H2 DB you can find more information here.

In my code I use two fields which I can identify the company with. The first one is a company name. Companies which use a webhook are separated by this field. And the second field is for the type of information which the app will consume. I have added the second field because а company can use particular services which are offered by a webhook.

There is a dto object which represents table from DB. The dto class contains four fields:

  1. ID
  2. URL
  3. company name
  4. type.

The URL is the callback which the logic calls when it has finished.
Company name and type:

DTO Code:

package eu.dreamix.webhook.dto;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;



@Entity
@Table(name = "webhook", schema="webhook")
public class WebHook {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;
	
	@Column(name="url")
	private String url;
	
	@Column(name="company_name")
	private String companyName;
	
	@Column(name="type")
	private String type;

	public WebHook() {
		super();
		// TODO Auto-generated constructor stub
	}

	public WebHook(Long id, String url, String companyName, String type) {
		super();
		this.id = id;
		this.url = url;
		this.companyName = companyName;
		this.type = type;
	}

//getters and setters
}

My repository is simple and extends a Crud Repository. I have also added two bonus methods for filtering information easily:

  1. The first one finds a webhook both by name and by type of service it has been registered with;
  2. The second one is only for company name.

Repository Code:

package eu.dreamix.webhook.repository;

import java.util.List;

import org.springframework.data.repository.CrudRepository;

import eu.dreamix.webhook.dto.WebHook;

public interface WebhookRepository  extends CrudRepository<WebHook, Long>{

	public List<WebHook> findByCompanyNameAndType(String companyName, String type);
	
	public List<WebHook> findByCompanyName(String companyName);
}

In my controller I have created оne simple method for adding a webhook. It’s a simple POST which consumes a webhook in JSON format. I have also attached services for deletion of registration (both by company and by type) and for updating a webhook.

Controller code:

package eu.dreamix.webhook.controller;

import java.util.ArrayList;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import eu.dreamix.webhook.dto.WebHook;
import eu.dreamix.webhook.repository.WebhookRepository;


@RestController
@RequestMapping("/webhooks")
public class WebHookController {
	private final static Logger logger = LoggerFactory.getLogger(WebHookController.class);

	private WebhookRepository webHookRepository;

	@Autowired
	public WebHookController(WebhookRepository webHookRepository) {
		super();
		this.webHookRepository = webHookRepository;
	}
	
	@RequestMapping(method = RequestMethod.POST,
          consumes = MediaType.APPLICATION_JSON_VALUE, 
            produces=MediaType.TEXT_MARKDOWN_VALUE)
	public ResponseEntity<String> addWebHook(@RequestBody WebHook webHook){
		logger.info("New webhook for " + webHook.getCompanyName() + " is registered");
		List<WebHook> webhooks = webHookRepository.findByCompanyNameAndType(
                    webHook.getCompanyName(),
                    webHook.getType());
		if(webhooks != null && webhooks.contains(webHook)){
			return new ResponseEntity<>("Webhook already exists", HttpStatus.OK);
		}
		webHookRepository.save(webHook);
		return new ResponseEntity<>("Successfully", HttpStatus.CREATED);
	}
	
	@RequestMapping(method = RequestMethod.GET, produces=MediaType.APPLICATION_JSON_VALUE)
	public ResponseEntity<List<WebHook>> getAllWebHooks(){
		List<WebHook> webhooks = new ArrayList<>();
		webHookRepository.findAll().iterator().forEachRemaining(webhooks::add);
		return new ResponseEntity<List<WebHook>>(webhooks, HttpStatus.OK);
	}
	
	@RequestMapping(method = RequestMethod.GET, 
           value ="/comapnies/{companyName}/types/{type}", 
produces=MediaType.APPLICATION_JSON_VALUE)
	public ResponseEntity<WebHook> getWebHooksByCompanyNameAndType(
                @PathVariable String companyName, 
                @PathVariable String type){
		List<WebHook> webhooks = webHookRepository.findByCompanyNameAndType(
                   companyName, type);
		return new ResponseEntity<WebHook>(webhooks.get(0), HttpStatus.OK);
	}
	
	@RequestMapping(method = RequestMethod.DELETE,
           value ="/comapnies/{companyName}/types/{type}", 
produces=MediaType.TEXT_MARKDOWN_VALUE)
	public ResponseEntity<String> removeWebHook(
               @PathVariable String companyName, 
               @PathVariable String type){
		List<WebHook> webhooks = webHookRepository.findByCompanyNameAndType(
                   companyName, type);
		if(!webhooks.isEmpty()){
			webHookRepository.delete(webhooks.get(0));
			return new ResponseEntity<>("WebHook was successfully deleted.", HttpStatus.OK);
		}
		return new ResponseEntity<>("Webhook doesn't exist.", HttpStatus.OK);
	}
	
	@RequestMapping(method = RequestMethod.DELETE,value ="/ids/{id}", 
produces=MediaType.TEXT_MARKDOWN_VALUE)
	public ResponseEntity<String> removeWebHookById(@PathVariable Long id){
		WebHook webhook= webHookRepository.findOne(id);
		if(webhook != null){
			webHookRepository.delete(webhook);
			return new ResponseEntity<>("WebHook was successfully deleted.", HttpStatus.OK);
		}
		return new ResponseEntity<>("Webhook doesn't exist.", HttpStatus.OK);
	}
}

And now that you have your own webhook let’s talk about where it is appropriate to use it. Here are my suggestions:

  1. When you want to extend your core product with others custom processes which communicate to your business logic in real-time.
  2. When you want to improve user experience and you have an application with asynchronous architecture.

And finally some things you should consider when building a webhook:

  1. Security! That is one of the most important things that you should consider. You can require the URL to use https.
  2. Carefully consider the data that you share .
  3. Think about a fallback mechanism in case of a problem with internet traffic. If you don’t have a fallback mechanism a webhook can’t be used in critical business operations.

Do you have experience on the topic? Feel free to share it in the comments.

Java & Oracle Developer at Dreamix