Getting Started with Spring Data Rest & Spring Data

A Small Intro

OKAY! If you’ve never used Spring Data before, its really easy to setup and its even easier to get a REST API up and running in no time.

A Brief Tutorial

For this example we’ll be using Spring Data JPA and the H2 database. Head over to https://start.spring.io/ and download a new Spring Boot applicaiton with the following dependencies: Rest Repositories, H2, JPA

If you want to try your hand at a different platform, at the time of this writing, Spring Data Rest currently supports: JPA, MongoDB, Neo4j, Apache Solr, Apache Cassandra, and Gemfire

Spring Initializr

Defaults

Let’s take a look of what were working with right out of the box

> curl -i localhost:8080/

HTTP/1.1 200
Content-Type: application/hal+json;charset=UTF-8
Transfer-Encoding: chunked
Date: Sat, 13 Jan 2018 00:01:35 GMT

{
  "_links" : {
    "profile" : {
      "href" : "http://localhost:8080/profile"
    }
  }
}

Models

Lets create our Player object

package com.example.drt.model;

import javax.persistence.*;

@Entity
public class Player {

  @Id @GeneratedValue(strategy=GenerationType.AUTO)
  private long id;
  private String firstName;
  private String lastName;
  private String number;
  private String team;

  public Player() { } //empty constructor

  public Player(String firstName, String lastName, String number, String team) {
    this.firstName = firstName;
    this.lastName = lastName;
    this.number = number;
    this.team = team;
  }

  //getters and setters removed

  @Override
  public String toString() {
    return String.format("Player [id=%d, firstName=%s, lastName=%s, number=%s, team=%s]\n",
      id, firstName, lastName, number, team);
  }
}

The Repository

Here is our repository.

package com.example.drt.repo;

import org.springframework.data.jpa.repository.JpaRepository;
import com.example.drt.model.Player;

public interface PlayerRepository extends JpaRepository<Player, Long> {

}

Not much when you look at it. Spring Data does a lot of the heavy lifting for you. The JpaRepository already takes on the following methods: count, delete, deleteAll, deleteById, existsById, findById, save, findAll, findOne, getOne, and plenty more. You have a large arsenal to work with to build out your API.

Changing the Root Context

Most likely you’re making an API and want the prepend all your URLs with /api. Let’s make that happen

# src/main/resources/application.properties

spring.data.rest.basePath=/api

You could use server.contextPath=/api but that adds some funky behavior and the root of /api actually fails and only /api/ works. Best to stick with the Spring Data Rest specific properties.


Testing Out with CRUD Operations

Check out the stuff

Root URI

> curl -i localhost:8080/api

HTTP/1.1 200
Content-Type: application/hal+json;charset=UTF-8
Transfer-Encoding: chunked
Date: Sat, 13 Jan 2018 03:03:08 GMT

{
  "_links" : {
    "players" : {
      "href" : "http://localhost:8080/api/players"
    },
    "profile" : {
      "href" : "http://localhost:8080/api/profile"
    }
  }
}

Notice how Spring Data pluralized player for us automatically

Create a New Player

> curl -i -X POST -H "Content-type:application/json" -d '{ "firstName":"Sergei", "lastName":"Fedorov", "number":"19", "team":"Detroit Red Wings" }' localhost:8080/api/players

HTTP/1.1 201
Location: http://localhost:8080/api/players/1
Content-Type: application/hal+json;charset=UTF-8
Transfer-Encoding: chunked
Date: Fri, 12 Jan 2018 11:29:29 GMT

{
  "firstName" : "Sergei",
  "lastName" : "Fedorov",
  "number" : "19",
  "team" : "Detroit Red Wings",
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/api/players/1"
    },
    "player" : {
      "href" : "http://localhost:8080/api/players/1"
    }
  }
}

Retreive Players

> curl -i -H 'ContentType:application/json' localhost:8080/api/players

HTTP/1.1 200
Content-Type: application/hal+json;charset=UTF-8
Transfer-Encoding: chunked
Date: Sat, 13 Jan 2018 03:04:59 GMT

{
  "_embedded" : {
    "players" : [ {
      "firstName" : "Sergei",
      "lastName" : "Fedorov",
      "number" : "19",
      "team" : "Detroit Red Wings",
      "_links" : {
        "self" : {
          "href" : "http://localhost:8080/api/players/1"
        },
        "player" : {
          "href" : "http://localhost:8080/api/players/1"
        }
      }
    } ]
  },
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/api/players"
    },
    "profile" : {
      "href" : "http://localhost:8080/api/profile/players"
    }
  }
}

You have probably noticed by now that HAL is a bit different than what you are used to when working with an API. In our attempt to get all the players in the database, you probably expected something like:

[ {
  "firstName":"Sergei"
  //the rest is removed for space
} ]

Take notice of the _embedded in our response. This gives us embedded player resources with their own links and properties

Retrieve a Player

> curl -i localhost:8080/api/players/1

HTTP/1.1 200
Content-Type: application/hal+json;charset=UTF-8
Transfer-Encoding: chunked
Date: Fri, 12 Jan 2018 11:30:15 GMT

{
  "firstName" : "Sergei",
  "lastName" : "Fedorov",
  "number" : "19",
  "team" : "Detroit Red Wings",
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/api/players/1"
    },
    "player" : {
      "href" : "http://localhost:8080/api/players/1"
    }
  }
}

Update a Player

Oh NO! I made a mistake! Fedorov wasn’t number 19, that was Steve Yzerman. Okay let’s fix this and update Federov’s number to the correct one, 91.

> curl -i -X PUT -H "Content-type:application/json" -d '{ "firstName":"Sergei", "lastName":"Fedorov", "number":"91", "team":"Detroit Red Wings" }' localhost:8080/api/players/1

HTTP/1.1 200
Location: http://localhost:8080/api/players/1
Content-Type: application/hal+json;charset=UTF-8
Transfer-Encoding: chunked
Date: Sat, 13 Jan 2018 03:00:58 GMT

{
  "firstName" : "Sergei",
  "lastName" : "Fedorov",
  "number" : "91",
  "team" : "Detroit Red Wings",
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/api/players/1"
    },
    "player" : {
      "href" : "http://localhost:8080/api/players/1"
    }
  }
}

Delete a Player

> curl -i -X DELETE localhost:8080/api/players/1

HTTP/1.1 204
Date: Sat, 13 Jan 2018 02:58:33 GMT

And thats it :D

You have a fully functioning (albeit, very simplistic) REST API!~

In later posts I’ll talk about more customizations you can do with Spring Data and how to grow your API.


References

[1] Spring Data REST Documentation https://docs.spring.io/spring-data/rest/docs/3.0.2.RELEASE/reference/html/
[2] HAL Specification http://stateless.co/hal_specification.html
[3] Understanding HATEOAS https://spring.io/understanding/HATEOAS
[4] Network Working Group Draf https://tools.ietf.org/html/draft-kelly-json-hal-08

Comments