In this article, I describe the high-level steps to use the Jakarta RESTful Web Services specification to create a REST API. Although most people use a REST, or RESTful, API to indicate they’re transferring data over HTTP, the technology is also often used to connect the front end to the back end.
The high-level steps below demonstrate that Jakarta RESTful Web Services can be used with minimal configuration. Most tasks are centered around the Java methods that perform or delegate the actual work, and there’s no need to worry about the infrastructure parts of the request. The configuration and infrastructure are specified with annotations:
- · @ApplicationPath defines the URL that triggers the REST support.
- · @Path links the URL to the Java class that handles the response.
- · @GET, @POST, and other annotations define the HTTP method that is supported by the code.
- · @PathParam and @QueryParam retrieve information from the URL and pass it as parameters to the Java method.
- · @Consumes and @Produces indicate the type of the payload in the request and response.
Adding the Web API Dependency and Configuring the Project
The web API dependency gives you access to all classes, interfaces, and annotations you need to write a Jakarta EE application.
Payara Server includes all of the code and implementations, so you can have a lightweight web application resource (WAR) file that only contains your application code.
When using Maven as a build tool, add the dependency for your WAR application, as shown below.
<dependency> <groupId>jakarta.platform</groupId> <artifactId>jakarta.jakartaee-web-api</artifactId> <version>9.0.0</version> <scope>provided</scope> </dependency> |
When using Gradle, add the line below to the build.gradle file.
providedCompile 'jakarta.platform:jakarta.jakartaee-web-api:9.0.0' |
There are several options to configure the Jakarta REST framework, but most of the time, you only need to define the part of the URL that triggers REST engine processing. You can do this by defining the Java class in your project, as shown below.
@ApplicationPath("/api") public class DemoApplication extends Application { } |
The class extends jakarta.ws.rs.core.Application which is the base class for the configuration. The annotation, jakarta.ws.rs.ApplicationPath, identifies the application path that serves as the base uniform resource identifier (URI) for all resource URIs. In the first example, it will become clear where the /api part of the URL fits in the final URL of the endpoint.
Creating a Hello World Endpoint
A Java class, such as shown below, is enough to create a REST resource.
@Path("/hello") public class HelloResource {
@GET public String sayHello() { return "Hello World"; } } |
The jakarta.ws.rs.Path annotation defines the link between the URL entered by the user and the Java class that is responsible for handling that request. The jakarta.ws.rs.GET annotation indicates the endpoint must be called using the HTTP Get method. The return of the method, the "Hello World" string, is the response to the client.
Reading URL Information
It's important to be able to determine which parts of the specified URL were sent by the client when you write API endpoints as they hold important information related to the request.
In this example, we look at two ways to URL information: By reading part of the URL, and by reading query parameters.
To start, add the lines shown below to the HelloResource Java class.
@GET @Produces(MediaType.TEXT_PLAIN) @Path("/{name}") public String doGreeting(@PathParam("name") String someValue, @QueryParam("language") String language) { return "Hello " + someValue + " with language " + language; |
Note that an @Path annotation is specified and that it has curly braces. The curly braces indicate it is a placeholder, and that the actual value specified in the URL is transferred to the variable 'name'.
The variable name is also specified in the jakarta.ws.rs.PathParam annotation. This lets the Jakarta REST engine know that the matching URL part must be used as the value for the method parameter someValue.
The second method parameter has the annotation, jakarta.ws.rs.QueryParam, which transfers the value of the query parameter language to this parameter.
When the code is deployed, the call to the URL /api/hello/Payara?language=en results in calling the Java method with the parameters doGreeting("Payara","en").
Returning JSON Data
The previous examples used the content type text/plain as the return type for a response. In a production application, most of the communications are performed using the JSON data format. The next example demonstrates how easy it is to return this type of data.
Consider a Java class that holds information about a person.
public class Person { private String name; private int age;
// Jakarta JSON requires a no-argument constructor. // Setters and Setters omitted } |
The following Java resource class can be defined to return such a value:
@Path("/person") public class PersonResource {
@GET @Produces(MediaType.APPLICATION_JSON) public Person getPerson() { return // some way to return a Person instance } } |
The way you retrieve the Person instance is not important. In this example, only the @Produces value is important because the code explicitly indicates the response should contain a JSON payload. This is enough for the Jakarta REST system to know that it must convert the Person instance to JSON using the built-in support in Jakarta EE. Calling this endpoint results in the response shown below.
curl -v http://localhost:8080/rest/api/person * Trying ::1... * TCP_NODELAY set * Connected to localhost (::1) port 8080 (#0) > GET /rest/api/person HTTP/1.1 > Host: localhost:8080 > User-Agent: curl/7.64.1 > Accept: */* > < HTTP/1.1 200 OK < Server: Payara Server 5.2021.1 #badassfish < X-Powered-By: Servlet/4.0 JSP/2.3 (Payara Server 5.2021.1 #badassfish Java/Azul Systems, Inc./11) < Content-Type: application/json < Content-Length: 24 < X-Frame-Options: SAMEORIGIN < * Connection #0 to host localhost left intact {"age":42,"name":"Rudy"}* Closing connection 0 |
There is no need to configure the Person class for JSON serialization. By default, each property is inspected and added to the output. The property type is also used to determine the optimal encoding to ensure the integer value is written without quotes.
In addition to support for JSON, you can also indicate, through the media type value, that XML output is required.
Sending Data
To send information to be processed, create the method within the PersonResource Java Class, as shown below.
@POST @Produces(MediaType.TEXT_PLAIN) @Consumes(MediaType.APPLICATION_JSON) public String handlePersonRequest(Person person) { return person.toString(); } |
The jakarta.ws.rs.POST annotation specifies the HTTP method being used. When the Jakarta REST system receives a request matching that HTTP method on the URL specified by the @Path, it transfers control to this method.
The jakarta.ws.rs.Consumes annotation specifies a JSON value. This information is also used to match the Java method that must be executed for a certain request. If a POSTrequest is sent to the URL, but it has another content type, such as XML, the method is no longer considered as a candidate.
The media type information is also used to convert the request body to the method parameter. There can only be one method parameter that doesn't have any Jakarta REST annotations because you can only convert the body to one parameter.
Additional parameters with the @PathParam and @QueryParam annotations are allowed, and you can refer to the Reading URL Information section earlier in this article when sending data.
Taking Control of the HTTP Status Value in the Response
Until now, 200 (status OK) was always returned since the call was successful and included some payload.
If null is returned in any of the previous examples, instead of an actual string or person instance, the returned status is 204 (No content).
To control the returned status, follow the example below.
@Path("/evenValue") public class ResponseResource {
@GET @Produces(MediaType.TEXT_PLAIN) @Path("/{intVal}") public Response testValue(@PathParam("intVal") Integer value) { if (value % 2 == 0) { return Response.ok("Value is a correct even number") .build(); } else { return Response.notAcceptable(Collections.emptyList()).build(); } } } |
The example describes a Java resource that defines an endpoint that checks if the supplied number is an even value. If it is not, the returned status indicates the value is incorrect.
Most of the code looks familiar as annotations were discussed earlier. The new aspect is the method’s jakarta.ws.rs.core.Response return type. The response encapsulates all parts of the response, the status, and the payload, and can be created using the ResponseBuilder.
For a correct request, use the Response.ok() method. The parameter of the ok method defines the payload. In the example, it is a string, but when you specify a Java instance and have the appropriate media type, a JSON response is possible.
If the value is incorrect, Response.notAcceptable sends the required status back to the client.
Learn More
For more information about using Jakarta EE RESTful Web Services, see the specification documents.