Creating Entities in a Spring Boot/Elide JSON API Server
Learn how to update the application to allow new entities to be created.
Join the DZone community and get the full member experience.
Join For FreeIn a previous article, I showed you how to create a Spring Boot application that integrated the Elide JSON API library to create a read-only REST API. In part 2, we’ll update the application to allow new entities to be created.
The JSON API supports the HTTP POST verb for creating new entities, and for creating new to-many relationships. We’ll add support for these two REST endpoints in our application.
First, though, we have some house keeping to do. As you saw in the first article, we have to do some work to prepare Elide once a request has been received: get the Hibernate Session Factory, find the complete request URL, create a logger, and then create the Elide object itself. This code is going to be shared between all Elide operations, so it makes sense to move it into a common location.
/**
* All our elide operations require similar initialisation, which we perform in this method before calling
* elideCallable with the elide object and the path that elide needs to know what it is supposed to do.
* @param request The request
* @param elideCallable A callback that is used to execute elide
* @return The response to the client
*/
private String elideRunner(final HttpServletRequest request, final ElideCallable elideCallable) {
/*
This gives us the full path that was used to call this endpoint.
*/
final String restOfTheUrl = (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
/*
Elide works with the Hibernate SessionFactory, not the JPA EntityManagerFactory.
Fortunately we san unwrap the JPA EntityManagerFactory to get access to the
Hibernate SessionFactory.
*/
final SessionFactory sessionFactory = emf.unwrap(SessionFactory.class);
/*
Elide takes a hibernate session factory
*/
final DataStore dataStore = new HibernateStore(sessionFactory);
/*
Define a logger
*/
final Logger logger = new Slf4jLogger();
/*
Create the Elide object
*/
final Elide elide = new Elide(logger, dataStore);
/*
There is a bug in Elide on Windows that will convert a leading forward slash into a backslash,
which then displays the error "token recognition error at: '\\'".
*/
final String fixedPath = restOfTheUrl.replaceAll("^/", "");
/*
Now that the boilerplate initialisation is done, we let the caller do something useful
*/
return elideCallable.call(elide, fixedPath);
}
The new elideRunner() method is almost a line for line copy of what used to be found in the method that responded to the GET request. The only real differences are that we don’t process the request parameters (because, as we’ll see, the Elide post() method doesn’t require these), and that we defer the actual execution of the Elide library to an interface that takes the newly created Elide object and the path of the request.
/**
* We'll implement this interface as a lambda to make working with Elide easier
*/
public interface ElideCallable {
String call(final Elide elide, final String path);
}
Before we handle the POST request, let’s look at what the GET request method now looks like:
@CrossOrigin(origins = "*")
@RequestMapping(
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE,
value={"/{entity}", "/{entity}/{id}/relationships/{entity2}", "/{entity}/{id}/{child}", "/{entity}/{id}"})
@Transactional
public String jsonApiGet(@RequestParam final Map<String, String> allRequestParams, final HttpServletRequest request) {
/*
Here we pass through the data Spring has provided for us in the parameters, then making
use of Java 8 Lambdas to do something useful.
*/
return elideRunner(
request,
(elide, path) -> elide.get(path, fromMap(allRequestParams), new Object()).getBody());
}
We have effectively reduced the method to a single line of code, making use of Java 8 lambdas to execute the Elide library’s get() method.
Not surprisingly, the method handling the POST request is very similar.
@CrossOrigin(origins = "*")
@RequestMapping(
method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE,
value={"/{entity}", "/{entity}/{id}/relationships/{entity2}"})
@Transactional
public String jsonApiPost(@RequestBody final String body, final HttpServletRequest request) {
/*
There is not much extra work to do here over what we have already put in place for the
get request. Our callback changes slightly, but we are still just passing objects
from Spring to Elide.
*/
return elideRunner(
request,
(elide, path) -> elide.post(path, body, new Object(), SecurityMode.SECURITY_INACTIVE).getBody());
}
Like its GET counterpart, the method handling the POST request does nothing more than pass the parameters supplied by Spring into Elide, making use of the common functionality now abstracted away by the elideRunner() method.
To create a new entity, POST the following JSON to the http://localhost:8080/parent endpoint:
{
"data": {
"type": "parent",
"attributes": {
"description": "James drives a racecar",
"name": "James"
}
}
}
The response will be something like this:
{
"data": {
"type": "parent",
"id": "12",
"attributes": {
"description": "James drives a racecar",
"name": "James"
},
"relationships": {
"children": {
"data": []
}
}
}
}
To define a relationship between a parent and a child, POST the following to http://localhost:8080/parent/12/children endpoint:
{
"data": { "type": "child", "id": "1" }
}
The response will be something like this:
{
"errors": [
"com.yahoo.elide.core.exceptions.ForbiddenAccessException: ForbiddenAccess not shared child#1"
]
}
This is because Elide has a security layer which prevents these relationships from being defined without some additional code. In the next article, we’ll take a look at these security annotations and give ourselves the ability to define relationships between entities.
Grab the source code for this project from GitHub.
Opinions expressed by DZone contributors are their own.
Comments