Express Web Services

One of the key features of javaxt-express is the ability to create REST services via the WebService class.

Instead of defining routes in config files or annotations, the WebService class will automatically route HTTP requests to a method in your Java code using reflection.

Suppose we want to implement a REST endpoint that simply returns a JSON document. All we need to do is:

  1. Create a class that extends the WebService abstract class
  2. Implement a public method that accepts a ServiceRequest and returns a ServiceResponse

Example

In the following example, we will create a "AdminService" WebService class with a single web method called getUsers() that simply returns a list of names as a JSON document.

 public class AdminService extends WebService {

    public ServiceResponse getUsers(ServiceRequest request) throws Exception {
        JSONArray users = new JSONArray();
        users.add("peter");
        users.add("paul");
        users.add("mary");
        users.add("jane");
        return new ServiceResponse(users);
    }
 }
 

To host this service, we will create a simple web server with an HttpServlet. The processRequest() method of the HttpServlet is used to read HTTP requests and send a response to the client. Inside the processRequest() method we will route requests to the AdminService.


  //Start a web server with an HttpServlet
    int port = 9080;
    int numThreads = 50;
    new javaxt.http.Server(port, numThreads, new HttpServlet() {


      //Instantiate the AdminService
        private AdminService adminService = new AdminService();


      //Create an implementation of the processRequest method for the HttpServlet
        public void processRequest(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, java.io.IOException {

          //Wrap the HTTP request in a ServiceRequest
            ServiceRequest serviceRequest = new ServiceRequest(request);

          //Get response from the AdminService
            ServiceResponse serviceResponse = adminService.getServiceResponse(serviceRequest);

          //Send response to the client
            serviceResponse.send(response, serviceRequest);
        }

    }).start();
 
In this simple example, the AdminService service will be called whenever a new HTTP request is recieved. The getUsers() method in the AdminService class will be invoked automatically whenever the web server processes a
GET /users
request (e.g. http://localhost:9080/users). All other requests will return a 501 response.

Routing Logic

The logic for picking a method to call is quite simple. If the client performs a HTTP "GET" request, the server will try to find a suitable "get" method using the first path after the "servlet" path. If the client performs a HTTP "DELETE" request, the server will try to find a suitable "delete" method. And if the client performs a HTTP "POST" or "PUT" request, the server will try to find a suitable "save" method.

In our simple example, the following requests map to the web methods in the AdminService like this:

  • GET "http://localhost/user" returns "getUser"
  • DELETE "http://localhost/user" returns "deleteUser"
  • POST or PUT "http://localhost/user" returns "saveUser"

To support these routes, we will have to create new REST service endpoints. Example:

    public ServiceResponse getUser(ServiceRequest request) throws Exception {
        //TODO: Return response for HTTP "GET" request to http://localhost:9080/user?id=123
    }

    public ServiceResponse deleteUser(ServiceRequest request) throws Exception {
        //TODO: Return response for HTTP "DELETE" request to http://localhost:9080/user?id=123
    }

    public ServiceResponse saveUser(ServiceRequest request) throws Exception {
        //TODO: Return response for HTTP "POST" or "PUT" request to http://localhost:9080/user
    }
 

If a suitable method is not defined, the server will return a 501 response.

Models, Persistence, and CRUD

In the examples above we showed how javaxt-express routes HTTP requests to concrete methods in the AdminService. We can simplify this further by using javaxt.sql.Models generated by the javaxt-orm library.

With javaxt-orm, you can create a javaxt.sql.Model using a model spec (json or js file) and it will generate classes that you can incorporate into your project (e.g. User.java).

Assuming you add the models to your project and initialize them with a ConnectionPool, you can register the models with the WebService like this:

public class AdminService extends WebService {
    public AdminService(){
        addClass(User.class);
    }
}

If you register a model like this, you don't actually need to implement your own getUser(), getUsers(), saveUser(), and deleteUser() methods. These CRUD methods will be provided automatically by the WebService class.

The javaxt.express.Server command line interface takes this one step further and doesn't require concrete javaxt.sql.Model classes. Instead, all you have to do is pass in a model spec file (json or js) and it will create, initialize, and register the models in a completely abstract way.