Code Review Stack Exchange is a question and answer site for peer programmer code reviews. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I have a class with multiple methods which return a URL string of an API endpoint.

Each methods takes different parameters based on what the endpoint have to include.

Example,

  1. GET: http://api.mydomain.com/v1/shopping_lists/items/categories
  2. GET: http://api.mydomain.com/v1/shopping_lists/items?last_sync_date=2016-09-29T17%3A14%3A51Z&page_number=1&page_limit=10&include=categories%2Csubcategories
  3. POST: http://api.mydomain.com/v1/users/{user_guid}/shopping_lists/{shopping_list_guid}/items
  4. GET: http://api.mydomain.com/v1/users/guid?include=devices%2Cshopping_lists.items.images%2Cshopping_lists.occasions

Among others. As we can see in the above URLs, I have query parameters as well as the HTTP method in which the API is getting. Not to mention the body request in JSON or form-data.


This is my current approach on building those URL strings:

I'm using OkHttp as the HTTP client. Firstly, I've created an abstract class called BaseHttpCall. Inside, I put all of the HTTP methods with the Request Builder like so,

public abstract class BaseHttpCall {

    private static final String BASE_URL = "http://api.mydomain.com";
    private static final String VER = "/v1/";
    private static final String API_URL = BASE_URL + VER;

    public Request post(String endpoint, RequestBody requestBody, boolean auth) {
        return new Request.Builder()
                .url(API_URL + endpoint)
                .post(requestBody)
                .addHeader("Content-Type", "application/json")
                .addHeader("Authorization", (auth) ? "Bearer " + session.token().get() : "")
                .build();
    }

    public Request get(String endpoint, boolean auth) {
        return new Request.Builder()
                .url(API_URL + endpoint)
                .get()
                .addHeader("Content-Type", "application/json")
                .addHeader("Authorization", (auth) ? "Bearer " + session.token().get() : "")
                .build();
    }

    // other HTTP method builder ...

    public String buildQueryString(String endpoint, HashMap<String, String> keyValuePair) throws APIError {

        // process keyValuePair

        return mEndpointWithQueryString;
    }
}

For the endpoint management, I put it in a class called Endpoints. This class holds all of the endpoint.

public class Endpoints {

    public static final String DEVICES = "devices";
    public static final String USERS = "users";
    public static final String SHOPPING_LIST = "shopping_lists";
    public static final String SMS = "sms";
    // ... other endpoints


    /**
     * Build Shopping List endpoint for Create/View
     */
    public static String buildShoppingListEndpoint(String user_guid, String[] includes) {
        String mBaseEndpoint =
                USERS + "/" + user_guid + "/" +
                        SHOPPING_LIST;
        if (includes == null) {
            return mBaseEndpoint;
        }
        return mBaseEndpoint + include(includes);
    }

    /**
     * Build Shopping List endpoint for Update/Delete
     */
    public static String buildShoppingListEndpoint(String user_guid, String shopping_list_guid, String[] includes) {
        String mBaseEndpoint =
                USERS + "/" + user_guid + "/" +
                        SHOPPING_LIST + "/" + shopping_list_guid;
        if (includes == null) {
            return mBaseEndpoint;
        }
        return mBaseEndpoint + include(includes);
    }

    /**
     * Build User Shopping List Item endpoint
     *
     */
    public static String buildGroceryListEndpoint(String user_guid, String shopping_list_guid, String item_guid, String[] includes) {
        String mBaseEndpoint =
                USERS + "/" + user_guid + "/" +
                        SHOPPING_LIST + "/" + shopping_list_guid + "/" +
                        ITEMS + (StringUtils.isNotEmpty(item_guid) ? "/" + item_guid : "");
        if (includes == null) {
            return mBaseEndpoint;
        }
        return mBaseEndpoint + include(includes);
    }

    // ... other build methods

    /**
     * Construct the query parameter for requesting custom response
     */
    private static String include(String[] includes) {
        return "?include=" + StringUtils.join(includes, ",");
    }

    private static String slash() {
        return "/";
    }
}

In order to use these functions, I would do this - example for building Sub Category Item Endpoint (considering this function is inside a class which extends BaseHttpCall abstract method):

String[] includes = {"category", "items"};
HashMap<String, String> keyValuePair = new HashMap<>();
// ... key value pair builder here

Request request = 
    get(
        buildQueryString(
            Endpoints.buildSubCategoryItemEndpoint(user_guid, sub_category_id, null), keyValuePair), true);

Which I think smells terrible behind the scenes. Is there any improvements I can do to make this more readable and maintainable in the backend? Also making the usage of the Endpoint builder more easily?

share|improve this question

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Browse other questions tagged or ask your own question.