Sending Text Messages With Bandwidth and Feign

Image for texting with bandwidth and feign

Many applications these days have to deal with service-to-service communication. Most of this communication happens via HTTP and RESTful APIs, which means many services need HTTP clients for communicating with other services. Bandwidth has many such interactions in its technology stack and has had great success with it.

Unfortunately, this often means that services talking to each other likely have extremely similar, albeit unique, code to facilitate this communication. There is a lot of boilerplate code being written over and over, which wastes developer time. It also allows for inconsistencies in how data is represented and how the requests are made.

We needed to find a way to solve this problem. One of the approaches my current team has taken is to build a client as a library that is published into our internal repository for other teams to consume. Most of our teams write in Java, so we decided to start there. Our hope in writing our own published client for our service was:

  1. Eliminating duplicate code
  2. Enforcing a “blessed” data representation
  3. Increasing application development speed for team consuming our service

However, we also wanted to avoid re-writing the same boiler-plate code that usually comes with writing an HTTP client. We had a few of those classes lying around, but they were all a little different and all had deep inheritance that we wanted to avoid.

Enter Feign

Feign is basically a declarative HTTP client that uses annotations to understand and build HTTP requests instead of forcing you to write boilerplate code for every new client you need to build. Its from the Netflix OSS stack which has a slew of tools aimed at making it easier to build services in the ever growing “service-mesh” paradigm.

We decided to give it a shot. Below is a prototype that we made to illustrate some (but certainly not all) of the patterns we chose to follow to create a client SDK with Feign.

Sending and Receiving Text Messages

Since Bandwidth developers and delivers the power to communicate, we thought it would be appropriate to show how to use Feign to make a simple HTTP client for sending a text message. Our current Java SDK uses a different strategy right now, but it’s honestly just as easy to use if you are a Java developer.

First Step: Creating the API Client

The documents for sending a text message with Bandwidth are available here. To start, we will focus on simply creating an interface to define what data is sent for sending a text message.

@Path("v1/users/{userId}/messages")
public interface BandwidthMessagingApiSpec {

    @POST
    @Consumes("application/json")
    Response sendMessage(@PathParam("userId") String userId, Message message);

    @GET
    @Produces("application/json")
    List<Message> getAllMessages(@PathParam("userId") String userId);
}

The annotations to tell Feign how to talk to Bandwidth’s API

Here, we are using the JAXRs Contract available in Feign. JAXRs is basically a huge specification for defining HTTP interactions that is under the covers of a lot of different libraries (Spring included). If it doesn’t make sense, take a look at their documentation here.

Second Step: Build a Bridge to GET Over It

Ok… bad joke. Regardless, the Bridge pattern is a great way for us to expose an interface to our consumers that totally hides away the Feign aspect of our client. Here is the interface for the BandwidthMessagingService:

public interface BandwidthMessagingService {

    String sendMessageAndGetId(String userId, String to, String from, String text);

    Collection<Message> getAllMessages(String userId);
}

Take great care when creating the interface that consumers of your client will use

This has several benefits to consuming applications:

  1. Easier mocking. This interface is much easier than a Feign specific representation.
  2. Hiding implementation details. Keep as much of the nitty-gritty away from the consumer (you could even make the ApiSpec package-private so the consumer never knows about it).
  3. Allows for composition later. Because you are hiding the details away, you are free to change them without breaking your exposed API.

Third Step: Create the Default Implementation

Now that we have a Service that is in a different hierarchy than our API Spec, we will actually make the backing implementation to “bridge” the two interfaces. Here we create the HttpBandwidthMessagingService that accepts the BandwidthMessagingApiSpec as dependency.

class HttpBandwidthMessagingService implements BandwidthMessagingService {

    private final BandwidthMessagingApiSpec api;

    HttpBandwidthMessagingService(BandwidthMessagingApiSpec api) {
        this.api = api;
    }

    @Override
    public String sendMessageAndGetId(String userId, String to, String from, String text) {
        Message message = new Message();
        message.setTo(to);
        message.setFrom(from);
        message.setText(text);

        Response response = api.sendMessage(userId, message);
        return extractLocationIdAsString(response);
    }

    @Override
    public Collection<Message> getAllMessages(String userId) {
        return api.getAllMessages(userId);
    }

    private String extractLocationIdAsString(Response response) {
        Optional<String> optionalLocation = response.headers().get("location").stream()
                .findFirst();

        if(!optionalLocation.isPresent()) {
            return "";
        }

        String location = optionalLocation.get();
        return location.substring(location.lastIndexOf("/") + 1);
    }

We utilize the bridge pattern to keep the Feign aspect hidden from consumers

We basically use Dependency Injection here to make the implementation of one interface take an object of the other treat is as an internal member. This allows us to have fine-grained control over how to handle responses from the Feign API and return the objects best suited for our Consumers.

Step Four: Generate the Feign Client and Inject It Into the Service

Now that we have the pieces together, the last step is using Feign to build the ApiSpec instance that we pass to the HttpBandwidthMessagingService. For simplicity, we will just use the Feign defaults where possible.

public class BandwidthMessagingServiceTest {

    private static final String TOKEN = "<YOUR TOKEN>";
    private static final String SECRET = "<YOUR SECRET>";

    private static BandwidthMessagingService messagingService;

    @BeforeClass
    public static void setupService() {
        BandwidthMessagingApiSpec api = Feign.builder()
                .contract(new JAXRSContract())
                .encoder(new JacksonEncoder())
                .decoder(new JacksonDecoder())
                .requestInterceptor(new BasicAuthRequestInterceptor(TOKEN, SECRET))
                .target(BandwidthMessagingApiSpec.class, "https://api.catapult.inetwork.com");

        messagingService = new HttpBandwidthMessagingService(api);
    }

    @Test
    public void testSend() {
        String messageId = messagingService.sendMessageAndGetId("<YOUR USER ID>",
                "<E.164 TN>", "<E.164 TN>", "Feign Test");
    }

    @Test
    public void testGet() {
        Collection<Message> messages = messagingService.getAllMessages("<YOUR USER ID>");
    }

This simple tests shows how to send a text message and get back the collection of messages you have sent. (The Message is a simple POJO based on API documentation found here)

The TOKEN and SECRET are generated by Bandwidth for interacting with the Bandwidth API as an application. Even though it’s called a token, basic authentication is all you need to use. See more here.

Now you can start sending messages and retrieving the ones you have sent via the Bandwidth API! For a quick review of how each of these components work together, check out the simple class diagram below:

Diagram for Feign SDK

Conclusion

Using Feign, we have created a simple HTTP client that hides the implementation and follows sound object-oriented principles with only a few classes. We haven’t written any HTTP-specific code either — we’ve simply used some very powerful annotations to tell Feign how to talk to our service over the protocol.

Obviously, we haven’t added every API call possible in this example – there are a lot more things the Bandwidth Messaging API can do. Check out the other possibilities and let us know what you think!