Painless REST+JSON API with Jersey and RestyGWT
Posted by David Chandler on July 23, 2014
It’s been a while since I’ve been active in GWT programming, but I recently picked up an old GWT + App Engine project and dusted it off. This time around, I need a service layer that will work with native mobile apps as well as the GWT desktop app: in other words, a REST+JSON API.
The server side
I decided to use Jersey on the server, which makes it incredibly easy to create REST services, especially in combination with the fabulous objectify-appengine. Here’s one of my services:
@Path("api/user") @Produces(MediaType.APPLICATION_JSON) public class UserDao extends ObjectifyDao<User> { @GET @Path("me") public User getMe() { return AuthFilter.getUser(); // long live ThreadLocal! } }
All the other standard CRUD methods (list, get, delete, etc.) are inherited from a generic DAO. A servlet filter verifies user authentication, XSRF tokens, etc. At only one class per entity, I find this much more agreeable than the four classes per entity I used to write with gwt-dispatch (Action, Result, Handler, DAO).
The GWT REST+JSON client
On the client side, GWT developers have long needed something which combines:
- The convenience of GWT-RPC (automatic serialization of pretty much everything)
- JSON data format
- Simple verb-based service APIs
- Benefits of the command pattern such as request caching and batching
- Minimal boilerplate
RestyGWT delivers on all counts.
The interface is actually simpler than GWT-RPC because you don’t have to define a synchronous and asynchronous method, only async (although you can reuse the server interface and call it using DirectRestService if it helps you sleep better). Here’s a sample interface using RestyGWT. This is a generic CRUD API which I simply extend for each entity class.
public interface RestApi<T> extends RestService { @GET @Path("own") public void getForOwner(MethodCallback<T> callback); @GET @Path("get") public void get(@QueryParam("id")Long id, MethodCallback<T> callback); @GET @Path("all") public void listAll(MethodCallback<ListResponse<T>> callback); @POST @Path("save") public void save(T obj, MethodCallback<T> callback); ... }
Here’s a sample service API and the GWT code that calls it:
public class UserPrefsService { private static final UserPrefsRestService service = GWT.create(UserPrefsRestService.class); @Path("/api/userPrefs") public interface UserPrefsRestService extends RestApi<UserPrefs> { // just in case you missed it, all the CRUD methods are inherited } private UserPrefs prefs; public void loadUserPrefs() { service.getForOwner(new AppCallback<UserPrefs>() { @Override public void handleSuccess(UserPrefs result) { prefs = result; App.getEventBus().fireEvent(new UserPrefsLoadedEvent(prefs)); } }); } }
I really like the benefits of the Command pattern from the old gwt-dispatch framework such as the ability to do caching and queuing centrally. The downside of the Command pattern is the proliferation of classes associated with turning every API method into two or more classes (in the case of gwt-dispatch, an Action and Result class for every service method). GWT’s RequestFactory (the GWT team’s “final answer” to RPC after GWT-RPC and deRPC) used generators to remove some of the boilerplate, but I wasn’t in love with having to call someSingleton.getRequestFactory().someRequest().create(BeanProxy.class) each and every time I wanted a new Bean. And it uses a proprietary data format, not REST+JSON.
This is where RestyGWT really shines. It lets you define simple API interfaces using methods, but uses a Command pattern dispatcher under the covers with an ingenious filter mechanism for adding capabilities like XSRF protection, caching, and retries. The magic is possible thanks to RestyGWT’s generator classes which transform your annotated service methods into MethodRequest classes that get sent by a dispatcher. To add caching capability to all your APIs, for example, just put this in your GWT onModuleLoad():
DispatcherFactory factory = new DispatcherFactory(); org.fusesource.restygwt.client.Defaults.setDispatcher(factory.cachingDispatcher());
For a look at some of the other dispatcher options, have a look at the RestyGWT User Guide and the example DispatchFactory class.
Tips for success with Jersey + RestyGWT
Just a couple final notes to ease your migration to Jersey + RestyGWT.
If you’re using RestyGWT with Jersey + Jackson on the server, you’ll want to set the default date format to avoid Date serialization errors. Put this in your GWT’s onModuleLoad():
Defaults.setDateFormat(null);
Similarly, recent versions of Jersey complain about “self-referencing cycles” or some such with Objectify’s Ref properties, so you’ll probably want to annotate those in your server-side entities with @JsonIgnore. If you need the id of a related object, use a differently named getter instead. Jersey will create a corresponding JSON property. Example of an Objectified entity on the server:
package com.my.server.domain; @Entity public class UserPrefs { @Id private long id; @JsonIgnore private Ref<User> ownerKey; ... public long getOwnerId() { return ownerKey.get().getId(); } }
Then on the client you can reference the ownerId property:
package com.my.client.domain; public class UserPrefs { public Long id; public long ownerId; ... }
I would prefer to use the same object representation on client and server, but RestyGWT makes using DTOs about as painless as possible. The client-side object is a class not an interface, so you can create a new instance anywhere you need it vs. having to GWT.create(BeanProxy.class). I could probably hack Jersey and maybe objectify-appengine to automatically replace all Refs with a long id, but honestly, it’s easy enough just to copy the server entities and replace Refs with long ids on the client.
Also, here’s a pair of classes you may find useful for sending any type of list from server to client. For security purposes, a JSON response should always return a root object, not an array directly. The ListWrapper (server) and ListResponse (client) serve as this root object, having a single field containing the list.
package com.my.server.domain; /** * Wraps a List<T> in a JSON root object. */ public class ListWrapper<T> { private List<T> list; public ListWrapper(List<T> list) { this.list = list; } public List<T> getList() { return list; } }
Use it like this in one of your Jersey-annotated methods:
@Path("all") @GET public ListWrapper<Subscription> findAll() { User user = AuthFilter.getUser(); List<Subscription> userAll = this.listByOwner(user); return new ListWrapper<Subscription>(userAll); }
Using ListWrapper this way has the additional advantage of causing Objectify to fetch any lazily loaded properties such as Refs used in the getOwnerId() method above while producing the JSON. If you were to return the List directly, this would not occur!
Here’s the corresponding representation on the client:
package com.my.client.domain; public class ListResponse<T> { public List<T> list; }
See the RestApi example earlier for how it’s used.
Summary
Jersey and RestyGWT in combination make a powerful and easy way to create a REST API for your GWT client and mobile apps, combining all the benefits of a request dispatcher with straightforward service interfaces and minimal boilerplate.
Happy coding!
Rutger said
Nice! Will try that out myself.
Sam Edwards (@HandstandSam) said
Using Jersey on the Server side and Lombok during development, I’ve saved countless hours. Thanks for the GWT tips, those were new to me!
Arpit Tripathi said
Hey David, thanks for the blog. Could you help out by sharing a sample project with AuthFilter etc.?
David Chandler said
Yes, I definitely plan to at some point. I have actually thought of offering sample projects for $99 like MyApps does. Would you be willing to pay for access to the source code of a complete sample project?
Arpit said
Good idea! If it has features I need, then why not!
What I need right now is –
1. Custom Authentication
2. Objectify Support (this one should be easy to do for me as well but great if it is built in)
3. Get/Put/Post/Delete using Resty with some frontend code using GWT Editors.
It’s tough to find a Resty GWT sample on internet which runs out of the box.
I’ve tried creating a sample which includes 2 and 3 but it’s still far from perfect.
Bademus l. said
What you think about https://github.com/ArcBees/GWTP/wiki/Rest-Dispatch
Thank you.
Pooch said
Great stuff!
You wrote: “GWT’s RequestFactory (the GWT team’s “final answer” to RPC after GWT-RPC and deRPC) used generators to remove some of the boilerplate, but I wasn’t in love with having to call someSingleton.getRequestFactory().someRequest().create(BeanProxy.class) each and every time I wanted a new Bean. Also it’s been deprecated.”
Can you point me to the announcement stating that RequestFactory has been deprecated, please? I don’t see it as being deprecated here: http://www.gwtproject.org/javadoc/latest/deprecated-list.html and it is alive and well here: http://www.gwtproject.org/javadoc/latest/com/google/web/bindery/requestfactory/shared/RequestFactory.html.
Thanks
David Chandler said
Yikes, my bad! RequestFactory is not deprecated. It is less well supported because the two original authors (Ray Ryan, Bob Vawter) no longer work on GWT (nor at Google); however, as long as GWT community superstar Thomas Broyer continues to use it in his projects, RF should be fine 🙂
Pooch said
Thanks for the clarification and all the effort you put into your blogs.