5. Golden service apis for ACME Store
In this exercise you can validate and practice your knowledge of the following concepts:
- Java microservice implementation with Helidon;
- API designing to meet specific criteria;
- RESTful services implementation with JAX-RS;
- Extense API documentation with OpenAPI;
- In-memory persistence with MicroStream;
- Validation with Bean Validation;
- Implicit and constant usage of CDI;
Getting familiar with the use case and the exercise goals described next is highly recommended, as a detailed step-by-step guide is not provided.
Friendly advice: consider leveraging this opportunity to upskill by making the best use of your knowledge and research skills to code the solution without replicating the provided solution.
5.1 Scenario
Your consultancy was requested by the Acme Store company. The company's technical team wants to learn good practices and recommendations for Java microservices built on top of the building blocks offered by specifications. They demonstrate interest in validating the experience of delivering Microprofile on top of the Helidon runtime.
Your task is to provide a Java backend service, with Helidon, to handle the basic operations of the store's products .
5.1.1 Goals
See below the solution's pre-requisites. (Easily track your progress by marking tasks as "done".)
- The service should be named
acme-store-rest
. It must rely on Microprofile 3.3 and Helidon. - The service handles operations for
Products
maintenance. AProduct
should have:- An ID, which is the
name
: mandatory and should be at max 100 chars. - A
description
: mandatory, should have at least 5 chars - A
quantity
: mandatory and should be higher than 0.
- An ID, which is the
- The following operations for a
Product
should be available:- List all the products
- Insert a new product
- Retrieve a product by ID
- Update a product based on its ID
- Delete a product using its ID
- It should be a RESTful application. The urls should follow these rules:
- To list all products: GET "/products"
- To insert a product: POST "/products"
- To find a product by ID: GET "/products/{productName}"
- To delete a product: DELETE "/products/{productName}"
- To update a product: PUT "/products/{productName}"
- The APIs should be properly documented:
- All APIs should have documentation for the
Operation
, includingsummary
anddescription
; - The responses (
APIResponse
) should be documented according to potential results, with properdescription
for returned HTTP codes (responseCode
, e.g. 200, 404). - The APIs should include information about release and stability through tags:
- The following apis are part of the first release, and tagged (
@Tag
) as1.0
:- List all
- Find by ID
- Save new product
- These APIs are still in BETA phase, and are tagged (
@Tag
) asBETA
:- Update
- Delete
5.1.2 Expected results
The solution should look like this:
5.2 Hands-on guidance
To save your time, the customer's team has created a basic project using the microprofile starter. They have provided domain related objects and boilerplate code. If you prefer, you can also start a project from scratch and use the quickstart project in case you need to check dependencies configuration and such.
5.2.1 The quickstart project: acme-store-rest
- Get started by cloning the project to your machine (if you haven't already).
- Open the project in your IDE of choice and give it an overall look. You can find multiple comments pointing to code that needs adjustment.
- If you are feeling confident, go ahead and get started with the requirements implementation. For general guidance, follow the next sections.
Persistence
The quickstart project is built on top of a ultra-fast in-memory persistence with MicroStream. Learn more about this persistence option by checking the implementation of the org.a4j.product.Inventory
class, enabled
by the dependency one.microstream:microstream-integrations-cdi:
.
5.2.2 Implementing the solution
5.2.2.1 REST Endpoints
The product's endpoints are partially implementated in the class ProductResource
.
-
Use a
ProductResource
to implement your REST APIs:- Scope: An appropriate CDI scope should be set for this bean. The
@RequestScope
should be enough. - Path: Remember to set the
javax.ws.rs.@Path
according to the goals of the project; - HTTP Methods: To differentiate APIs and map incoming requests to http methods, annotate the methods with:
@DELETE
,@PUT
,@POST
and@GET
fromjavax.ws.rs
. -
Custom paths: To create an endpoint like
/products/{id}
, you can annotate a method with@Path("{id}")
and add@pathParam
to the parameter. Example: -
Exception Handling: Avoid errors in case an invalid data is sent to your endpoints.
WebApplicationException
can be used for this purpose. Example: -
Responses with HTTP codes that make sense:
Response.Status
provides a list of possibilities. Example:
- Scope: An appropriate CDI scope should be set for this bean. The
5.2.3 Field validation
To implement the business rules related to the product attributes, you can use Bean Validation annotations on your Product.java
.
-
The following dependency was already added to the project's
pom.xml
-
In the class
Product.java
, use the multiplejavax.validation.constraints
options (like@NotBlank
,@NotNull
,@Size
). Configure each attribute's contraints accordingly along with the error message you want to associate with it. Example: -
In the
ProductResource.java
class, identify the methods where validation is needed, and use the@Valid
annotation for the parameters. Example:
5.2.4 APIs documentation with OpenAPI
5.2.4.1 Schema
The schema configuration can be done through annotations in the data objects, like Product
.
- Notice the class schema is already configured. No extra changes are needed here:
- Configure the
@Schema
for all the attributes. Don't forget to add anexample
attribute. - Later on, we will use these configured schemas on the
ProductResource
OpenAPI configurations.
5.2.4.2 The endpoints
- Configure the
@Tag
for each method as specified in the exercise goals. Example: - For each method, configure the
@Operation
details with asummary
anddescription
; - For each method, specify possible responses with
@APIResponse
, setting theresponseCode
and thedescription
that explains what this response means. Example: - Some
@APIResponse
might require extra information about the content being returned. This is where you can use the schema we configured previously in theProduct
class. Example of an APIResponse for the findById operation: - Input parameters may also require API documentation. For example, when saving a product, we can inform the user
what is the expected input data by using the
@RequestBody
annotation, and the attributesdescription
andcontent
. Thecontent
attribute points to our product schema. See an example below:
5.2.5 Running your application
-
To test your application, you can package it with Maven, run with Java, and use the OpenAPI UI, Postman, curl, or other tool of preference to invoke the rest APIs.
-
To package and run your application
- without Helidon CLI:
- or, using Helidon CLI:
-
By default it will run on port 8081, as configured in the
microprofile-config.properties
. - Validate your APIs documentation with OpenAPI UI at: http://localhost:8081/openapi-ui/
-
Check the service implementation with the following request examples:
- Insert a new product
- List all products
- Search a product by ID (name)
- Update a product using its ID (name)
- Delete a product by ID (name)
-
The next step is to try data that actually breaks the business rules and validate if your application is validating for example, the required fields.
- If you can successfully execute the above items, it means you acomplished all this challenge goals!
Congratulations!
You've finished your task for Acme Store with great success! You have created an examplar backend service that be used as a model, with good examples of usage of RESTFul concepts, beans validation, well-documented APIs, and much more! We're looking forward to seeing you upskilling by working on upcoming challenges!