Techies.

Custom API endpoint with SpringBot

In this article, we will create an extra endpoint to an existing entity while enforcing Spring security and easily adding to the Swagger API docs.


SpringBot uses both GraphQL and REST API architectures.

We will be continuing with our Codebots Zoo Project from Custom SQL with SpringBot by creating the custom endpoint for the newly created SQL query.

User Story : As a Zoo Keeper I want to flag when a Tank enclosure is at capacity so that I can properly manage the locations of the animals.

We want to create a new custom endpoint that will only return enclosures with the enum Tank where capacity is set to true.

You will be required to interact with the following files on the server-side.

File Name Relative Path Purpose
enclosure.schema.graphql serverside/src/main/resources/graphql/schemas/enclosure.schema.graphql Defines the database schema objects, queries and mutations.
EnclosureService.java serverside/src/main/java/zoo/services/EnclosureService.java Contains the services (or business logic) that will queries the database
EnclosureQueryResolver.java serverside/src/main/java/zoo/graphql/resolvers/query/EnclosureQueryResolver.java QueryResolver is used to handle all GraphQL API operations. It is equivalent it’s REST counterpart being the Controller.

If you also want to support RESTful API, you will also need to interact with this additional file:

File Name Relative Path Purpose
EnclosureController.java serverside/src/main/java/zoo/controllers/EnclosureController.java Controller used to handle all REST operations. It contains the URL endpoints and the Swagger Docs

enclosure.schema.graphql

We first need to define a GraphQL query and what it will return. In this example, we will be adding a new query to the existing query object.

  1. Locate the query definition protected region: # % protected region % [Add any additional query definition here] off begin
  2. Edit the protected region by changing off to on
  3. Add the following in between the begin and end protected regions
getEnclosureByCapacityAndTank: [Enclosure!]!

In this line, we define that getEnclosureByCapacityAndTank will be returning an array of Enclosures.

The ! means the value is guaranteed. In this example, [Enclosure!]! means that the query guarantees a non-null array of non-null Enclosure entities.

EnclosureService.java

We will be creating a new service function which will have the sole purpose of querying the database for enclosure entries that are at capacity and tank. To learn more about custom business logic, visit Custom business logic with SpringBot.

  1. Locate the class methods protected region: // % protected region % [Add any additional class methods here] off begin
  2. Edit the protected region by changing off to on
  3. Add the following code in between the protected region:
/**
 * Return all the enclosure entities that are both at capacity and tank.
 *
 * @return all the enclosure entities that are both at capacity and tank
 */
@PreAuthorize("hasPermission('EnclosureEntity', 'read')")
public List<EnclosureEntity> findByCapacityAndTank() {
    List<EnclosureEntity> entities = Lists.newArrayList(repository.findByCapacityAndTank());
    return entities;
}

The @PreAuthorize annotation enforces Spring’s security and makes sure that only authorised users with read permission can run this query. There are other options for @PreAuthorize outlined in the Spring documentation.

EnclosureQueryResolver.java

The next step is to create the GraphQL endpoint.

  1. Locate the additional class methods protected region: // % protected region % [Import any additional class methods here] off begin
  2. Edit the protected region by changing off to on
  3. Add the following code inside the protected region:
/**
* Return a list of {@link EnclosureEntity} entities.
*
* @return a list of enclosure entities that are at capacity and have tank as the enum value.
*/
@PreAuthorize("hasPermission('EnclosureEntity', 'read')")
public List<EnclosureEntity> getEnclosureByCapacityAndTank() {
    List<EnclosureEntity> entities = Lists.newArrayList(this.enclosureService.findByCapacityAndTank());
    return entities;
}

getEnclosureByCapacityAndTank refers to the defined query name from enclosure.schema.graphql
and findByCapacityAndTank refers to the function in the EnclosureService.java.

By default, the name of GraphQL queries as defined in schema files will be matched to the same method name in any query resolver. Other matching rules can be found in the GraphQL documentation.

EnclosureController.java

You only need to modify this file if you wish to support RESTful APIs. In Spring framework, you can create a RESTful endpoint via normal methods in controllers.

What is special about these methods is that they contain Spring annotation to register themselves as endpoints.

  1. Locate the endpoints protected region: // % protected region % [Add any additional endpoints here] on begin
  2. Edit the protected region by changing off to on
  3. Add the following code inside the protected region:
/**
* Return all enclosures that are at capacity and are type tank
*
* @return Enclosures that are at capacity and are type tank
*/
@ApiOperation(
        value = "Returns tank enclosures that are at capacity",
        authorizations = {@Authorization(value = "bearerToken")}
)
@PreAuthorize("hasPermission('EnclosureEntity', 'read')")
@GetMapping(value = "/get-enclosure-capacity-tank", produces = "application/json")
public ResponseEntity<List<EnclosureEntity>> getEnclosureByCapacityAndTank() {
    List<EnclosureEntity> enclosure = enclosureService.findByCapacityAndTank();

    return new ResponseEntity<>(enclosure, HttpStatus.OK);
}

@ApiOperation is the annotation that will be used by Swagger for displaying the REST API documentation. If you are interested in learning about other available parameters, check out the
Javadoc.

The annotation @GetMapping sets the URL endpoint that will call the REST API. In this case, it registers itself as a RESTful GET endpoint at URL /get-enclosure-capacity-tank, which produces a JSON object.

Along with @GetMapping, Spring also provides other annotations to depict different HTTP request types such as @PostMapping and @PutMapping. Details on other annotations can be found in the Spring documentation.

Here we are making use of the custom service method that we just created in EnclosureService.java and simply returning the list of Enclosure entities wrapped in a response.

Conclusion

In this tutorial, we have defined a new GraphQL query definition, added a new service method to fetch for the Enclosure entities that match our criteria, and added a corresponding query resolver which makes use of the newly added service method.

After starting the server-side, you can use either GraphiQL or Altair as a web-based API tool to test out the new GraphQL query definition. You can also use Swagger and Voyager to check out the API documentation for both GraphQL and RESTful APIs.

Solution

Video

The following link is a loom video about how to add further steps in NgRx.
Custom NgRx

Last updated: 16 March 2020


Related articles