Google Cloud Endpoints - Design Considerations

While designing of the Endpoints API is fairly straightforward, there are gotchas one might stumble upon. Especially when task at hand is slightly more involved than the proverbial "hello world" example. It took several iterations in my tiny little project to adjust the API according to my needs.

Don't get me wrong. I enjoy working with Endpoints, but there are always pitfalls to watch out for. Today, I'd like to focus on two limitations which affected the ultimate shape of my own API. These are primitive data types and references to the domain model.

Primitive data types (and void)

API methods cannot return primitive data types, only entities are allowed to be returned. Let me quote the docs:

A method return type as well as the request body of an API request must be an entity type.

The limitation probably stems from the fact that all the responses need to be clearly structured. Primitives, unlike entities, don't let themselves to an easy transformation into JSON objects.

Implications? Well, my suggestion is always think data: What happens to my model if method X is called? - that's the question you should constantly be asking yourself when shaping your API.

Forget about efficient response types:
// Return id of the persisted object  
public Long saveFoo(@Named("name") String fooName) {..}  

// True if the operation succeeds, false otherwise  
public Boolean deleteBar(@Named("id") Long barId) {..}  

Think "big" instead:
// Return a new entity  
public Foo saveFoo(@Named("name") String fooName) {..}  
 
// Return a list of remaining items  
public List<Bar> deleteBar(@Named("id") Long barId) {..}  

Using void as a way of skipping a meaningful response proved problematic too. Again, there is nothing essentially wrong with it, except for the fact it results into HTTP 204 (no content):
// Who cares about the output, let's just give it a shot  
public void addFoo(@Named("name") String fooName) {..}  

// Upon a successful(?) completion  
POST http://localhost:8080/_ah/api/helloworld/v1/addFoo/test  
204 No Content  

Domain model references

Splitting your API implementation into several classes might make a lot of sense. There are many reasons for why it sounds like a good idea. Separation of concerns, clearly defined responsibilities, high modularity to name but a few.

There is a slight problem though if a given domain class is shared among two or more API modules. Let me set a quick example:

Domain model - a single class called Foo
 package com.example.domain;  
 public class Foo {  
   private String name;  
   ..  
 }  

API comprises two independent classes FooApi and BarApi. Please note that both of them deal with Foo.
package com.example;

import com.example.domain.Foo;
  
 @Api(  
     name = "fooApi",  
     version = "v1",  
     scopes = {Constants.EMAIL_SCOPE},  
     clientIds = {Constants.WEB_CLIENT_ID, Constants.ANDROID_CLIENT_ID},  
     audiences = {Constants.ANDROID_AUDIENCE}  
 )  
 public class FooApi {  
   public List<Foo> listFoo() {..}  
 }  
package com.example;

import com.example.domain.Foo;
  
 @Api(  
     name = "barApi",  
     version = "v1",  
     scopes = {Constants.EMAIL_SCOPE},  
     clientIds = {Constants.WEB_CLIENT_ID, Constants.ANDROID_CLIENT_ID},  
     audiences = {Constants.ANDROID_AUDIENCE}  
 )  
 public class BarApi {  
   public List<Foo> listFooBecauseICan() {..}  
 }  

Once you go and import the generated jars to a client code (an Android project for ex.), all of the sudden, you end up with two different flavours of Foo:
 $ mvn appengine:endpoints_get_client_lib  
 ..  
 fooApi/target/fooApi-v1-1.19.0-SNAPSHOT.jar  
 barApi/target/barApi-v1-1.19.0-SNAPSHOT.jar  
 import com.appspot.phrasal_period_801.fooApi.model.Foo;  
 import com.appspot.phrasal_period_801.barApi.model.Foo;  

To sum up, there is no problem in splitting the API into as many standalone implementations as needed. However, I'd recommend not to refer to a given entity from more than a single API class.

This post is part of Google App Engine and Android - Pros and Cons, Challenges