The REST API design problem
Most REST APIs are described in terms of the request/response formats they comprise using a schema definition language such as JSON Schema. A complete REST API schema provides the following information:
- Language-neutral type definitions
- Formal type and data constraints
- Comprehensive documentation
As a language neutral format, however, JSON Schema is not directly accessible from Java; the schema-defined type definitions are not visible to Java’s type system. The preferred, albeit decades-old, solution to this kind of problem involves wedging a code generator into the build to transform structured data to Java. But this trades one big problem for another — a code generation step most often hinders what could otherwise be a streamlined development experience. The problems involved with code generators include:
- No feedback: JSON schema changes are invisible to Java until you rebuild the API
- Stale generated classes
- Increased build times
- Scalability issues: code gen interdependencies, caching, integrations, etc.
- Difficult problems with custom class loaders, runtime agents, annotation processors
- Interrupts train of thought
- Poor IDE integration:
* No immediate feedback from changes
* No incremental compilation
* Can’t navigate from code reference to corresponding JSON schema element
* Can’t find code usages from JSON schema elements
* Can’t refactor / rename JSON schema elements
The REST API design solution
A programming language fantasy world would have the Java compiler directly understand JSON Schema and eliminate the code generation step, similar to the way metaprogramming magically connects code with structured data in dynamic languages. This is precisely what Manifold accomplishes, all without sacrificing type-safety. Manifold is a general-purpose framework to type-safely extend Java’s type system. Building on this framework Manifold also provides support for several specific data formats including JSON, JSON Schema, YAML, and others. With Manifold Java has full-spectrum JSON Schema vision:
- The Java compiler understands JSON Schema type definitions
- JSON Schema is the API Single Source of Truth (SSoT)
- Eliminates the code generation step in your build!
- Scalable: JSON files are source files
- No custom class loaders, no runtime agents, no annotation processors
- Top-notch IDE integration:
* Make JSON Schema changes and immediately use the changes in your code, no compiling
* Incremental, processes only the JSON Schema changes you’ve made, faster builds
* Navigate from a code reference to a JSON Schema element
* Perform usage searches on JSON Schema elements to find code references
* Rename / refactor Schema elements
* Hotswap debugging support
Using Manifold in your project is easy. Simply add the Manifold jar to your project and begin taking advantage of it.
The IDE experience is paramount as most professional Java developers live and breathe within the IDE. Manifold does not disappoint, it provides a complete, seamless dev experience with the IntelliJ IDEA Manifold plugin. You can install it directly from within IntelliJ IDEA:
Settings ➜ Plugins ➜ Browse repositories ➜ search: Manifold
Seeing is believing
Drop the JSON Schema file com/example/api/User.json
into your resources
directory:
{ "$id": "https://example.com/restapi/User.json", "$schema": "http://json-schema.org/draft-07/schema#", "title": "User", "description": "A simple user type to uniquely identify a secure account", "type": "object", "definitions": { "Gender": {"enum": ["male", "female"]} }, "properties": { "id": { "description": "Uniquely identifies a user", "type": "string", "format": "email", "readOnly": true }, "password": { "description": "The user's password", "type": "string", "format": "password", "writeOnly": true }, "name": { "type": "string", "description": "A public name for the user", "maxLength": 128 }, "dob": { "type": "string", "description": "The user's date of birth", "format": "date" }, "gender": { "$ref": "#/definitions/Gender" } }, "required": ["id", "password", "name"] }
Immediately begin using the com.example.api.User
type directly in your code, without a compilation step:
User user = User.builder("scott", "mypassword", "Scott") .withGender(male) .build();
This code uses the builder()
method available on all JSON Schema types. Parameters on builder()
reflect required types specified in the schema. The withGender()
method reflects the gender
property, which is not required.
You can make REST calls directly from User
:
Requesterreq = User.request("http://localhost:4567/users"); // Get all Users via HTTP GET IJsonList users = req.getMany(); // Add a User with HTTP POST User user = User.builder("scott", "mypassword", "Scott") .withGender(male) .build(); req.postOne(user); // Get a User with HTTP GET String id = user.getId(); user = req.getOne("/$id"); // string interpolation too :) // Update a User with HTTP PUT user.setDob(LocalDate.of(1980, 7, 7)); req.putOne("/$id", user); // Delete a User with HTTP DELETE req.delete("/$id");
All JSON Schema types have a request()
method that takes a URL representing a base location from which REST calls can
be made. As shown request()
provides methods to conveniently invoke HTTP GET, POST, PUT, PATCH, and DELETE. You can
also specify authentication, header values, etc. via request()
.
You can make changes to User.json
and immediately use the changes in your code. You can refactor/rename, change types, etc. and immediately see the changes take effect in your code. Easily navigate from code usages to the declarations inUser.json
. Find your code usages from any declared element in User.json
.
Watch this short screencast to see all of this in action. http://manifold.systems/images/json.mp4
Fluent API
JSON types are defined as a set of fluent interface APIs. For example, the User
JSON type is a Java interface and provides type-safe methods to:
- create a
User
- build a
User
- modify properties of a
User
- load a
User
from a string, a file, or a URL using HTTP GET - request Web service operations using HTTP GET, POST, PUT, PATCH, & DELETE
- write a
User
as formatted JSON, YAML, or XML - copy a
User
- cast to
User
from any structurally compatible type includingMap
, all without proxies
Additionally, the API fully supports the JSON Schema format including:
- Properties marked
readOnly
orwriteOnly
- Nullable properties
additionalProperties
andpatternProperties
- Nested types
- Recursive types
- External types
format
typesallOf
composition typesoneOf
/anyOf
union types
JSON Schema constraints are not enforced in the API, however, because:
- This is the purpose of a JSON Schema validator
- Validating constraints can be expensive — it should be optional
Learn more about Manifold’s JSON and JSON Schema support.
SEE ALSO: Manifold: Alien technology
Templatized responses
With Manifold, you can build templates with the full expressive power of Java. Manifold’s advanced, lightweight template engine, ManTL, supports the full Java language, type-safe arguments to templates, type-safe inclusion of other templates, shared layouts and custom base classes for application-specific logic, among other features.
<%@ import java.util.List %> <%@ import com.example.User %> <%@ params(Listusers) %> <% users.stream() .filter(user -> user.getDob() != null) .forEach(user -> { %> User: ${user.getName()}
DOB: ${user.getDob()}
<% }); %>
Then use the template type-safely in your code:
UsersDob.render(users)
Use the Manifold plugin for IntelliJ IDEA for a refined template authoring experience. Features like deterministic code completion, in-line help, usage searching, highlighting, etc. are all at your fingertips.
What about the API server?
You can use Manifold with SparkJava to build a nice REST service. SparkJava is a micro framework for creating web applications in Java with minimal effort. Here is an HTTP PUT request route to update a User
:
// Update existing User put("/users/:id", (req, res) -> UserDao.updateUser(req.params(":id"), User.load().fromJson(req.body())).write().toJson());
Clone the source code for the manifold-sample-rest-api project and experiment with Manifold yourself.
Additional features
There are plenty of other features you can use to develop your REST service. For instance, you may have noticed some of the code samples above make use of Manifold’s String Templates feature, also known as string interpolation. You can use the $
character to embed variables and Java expressions directly into any String literal:
int hour = 8; String time = "It is $hour o'clock"; // prints "It is 8 o'clock"
You can also add your own Extension Methods to any Java type including JRE classes like String
and List
and even your REST API interfaces:
@Extension public class MyUserMethods { public static Integer getAge(@This User thiz) { LocalDate dob = user.getDob(); return dob == null ? null : Period.between(user.getDob(), LocalDate.now()).getYears(); } // more extension methods ... }
Then you can use your methods directly like this:
User user = ...; int age = user.getAge();
These are just a sampling of the Manifold framework features you can use to refine and improve your project.
To sum up
In this post, I’ve introduced Manifold as a breakthrough Java framework you can use to streamline your REST API development process. With it, you can eliminate code generators from your build script along with the host of problems associated with them. Using the Manifold IntelliJ IDEA plugin you can further improve your development experience: directly navigate to your JSON Schema elements, find usages of them in your code, use deterministic refactor and rename tooling, compile changes incrementally, etc. I also covered some of the JSON API features such as the request()
method for direct HTTP functionality. Additionally, I briefly demonstrated how you can incorporate templatized responses, string interpolation, and extension methods into your project. Finally, I demonstrated how you can create a solid REST service using Manifold with SparkJava — a lightweight, powerful combination.
Manifold is a breakthrough technology for the Java ecosystem. It’s also a free, open source project on GitHub. Check it out!
This article was originally published on Medium.
The post REST API Vision with Manifold appeared first on JAXenter.
Source : JAXenter