Strategies For API Dependency Management

Posted in

It’s increasingly uncommon for an app or digital product to only consume a single API. The odds are good that you’ll be consuming APIs for all sorts of abilities. To make matters even more complicated, every one of those APIs will have different permissions and security levels. To try and remember which ones are which with the equivalent of sticky notes around your monitor is quickly going to become unsustainable. Developers need to have a system in place to keep track of all of these particulars if they hope to make API consumption scalable across an entire enterprise.

A dependency is anything external that an application needs to function. Say you’re creating a messenger app and want to add a layer for privacy and security, so you introduce an encryption layer. That encryption layer would be a dependency as your app needs it to function.

The different aspects of managing dependencies for your APIs and API-related projects fall under the umbrella of Dependency Management — a quickly-growing sector of today’s development ecosystem.

Dependency management involves three stages:

  1. Identifying dependencies
  2. Resolving dependencies
  3. Patching dependencies

Let’s take a look at how Dependency Management works with APIs.

Dependency Management For APIs

To help you wrap your head around Dependency Management for APIs, let’s start by looking at Gradle, an open-source framework built specifically for this purpose. Using Gradle, you could handle dependencies in your development projects, or you could implement a similar approach in your stack.

Identify Dependencies

The first step in managing dependencies for your APIs and related projects is knowing what those dependencies are. For starters, check out this dependency graph from a chess clock app:

Credit: Niklas Baudy

Dependency Management is as much of a philosophy as it is a technical discipline. While dedicated Dependency Management tools will automate many of these processes for you, it’s also good to know how to think about them. That way, you can implement these practices in any environment you’re using.

The Dependency Identification stage reminds us that, sometimes, a whiteboard can be a programmer’s most powerful tool. For every API-related project you’re working on, you should keep tabs on all of the dependencies it needs to function, including third-party APIs and libraries.

If you end up using Gradle or some other Dependency Management solution, all of the dependencies will automatically be listed for all of your projects in a tree format:

Looking at these dependency trees will also help get you in the mindset of visualizing all of the various dependencies happening inside of your development projects.

Resolving Dependencies

The next step for managing dependencies in Gradle is to verify the resolutions. This can be customized in a variety of ways, but the basic concept is simply a ‘yes’ or ‘no’ checklist. This stage will last until all of the dependencies have been approved.

Of course, this can get a lot more involved, depending on the complexity of your API or API-consuming project. Say you’ve got different versions of your app, which might use various third-party APIs. You might have a different resolution stage for each iteration, which is one example of how Dependency Management for APIs can interact with other practices like API versioning. In Gradle, you’re able to create custom versioning schemes as well. You can set the current dependencies as a default and then create custom version paths for previous iterations. An example of what this might look like, in practice, can be seen here from the Gradle documentation:

configurations.all {
    resolutionStrategy.eachDependency { DependencyResolveDetails details ->
        if (details.requested.version == 'default') {
            def version = findDefaultVersionInCatalog(details.requested.group, details.requested.name)
            details.useVersion version.version
            details.because version.because
        }
    }
}

def findDefaultVersionInCatalog(String group, String name) {
    //some custom logic that resolves the default version into a specific version
    [version: "1.0", because: 'tested by QA']
}

Other potential issues you might run into could include relying on outdated APIs. You might include an HTTP redirect for certain assets pointing to the updated version so your users can update their projects.

Patching Dependencies

Dependency Management wouldn’t be such a big deal if things didn’t break. Patching dependencies is crucial as it means you’ll still be able to automate tasks that consume or monitor APIs. Patches also help you deduce if it’s a problem with the API, the application, or the connection, which will also help you during debugging.

Adding a patch creates a useful abstraction layer between your code and an API. This makes it far more difficult to break an app or API if something changes. It also means that if something does change, then you only need to change a few references instead of every single API call in your code. As far as APIs are concerned, one of the most common ways to create a patch is using an API wrapper. An API wrapper consolidates multiple API functions into one, making fewer calls necessary.

Regarding dependencies, it’s long been established that wrapping a dependency is one of the best practices for several reasons. For API developers, using an API wrapper means you don’t need to alert your API consumers every time you change your code.

There are extensive resources out there about how to write an API wrapper. If you’d like to try it out for yourself, here’s an excellent, concise post about how to write an API wrapper in Python and C#. This shows you how to create a simple API wrapper that lets you know the current location of the International Space Station, who’s on board, and when’s the next time it will pass a certain location.

Documenting API Dependencies In API Definitions

In Gradle, API dependencies are specified in the API itself. Every stage of a Gradle project has its own dependencies, which are defined in a Configuration function. Configurations offer a number of in-depth processes that can be displayed in a series of APIs.

Defining API dependencies in the code itself is beneficial for creators, consumers, and producers alike. For the API creator, it lets you specify what dependencies need to be satisfied. For consumers, it specifies what dependencies need to be resolved. For producers, it allows you to create a rich source of valuable endpoints for your consumers.

This method lets you specify different resources for different functions, as well. You could expose specific resources during the testing phase, for example, while reserving the full stack for runtime. Gradle offers a nearly endless array of different ways to create custom configurations, as well. You can even include a reason why a certain request may be denied, which is written into the code itself.

Of course, Gradle didn’t invent the concept of API dependency management. One developer was writing about it back in 2017, using a discovery specification named Pivio. In Pivio, every service contains an object that defines its dependencies, called depends_on. In Pivio, that looks like:

"service": {
  "depends_on": {
    "internal": [
      "print-service",
      "gateway-service"
    ],
    "external": [
      {
        "why": "Need to sync data with it.",
        "transport_protocol": "tcp",
        "target": "https://api.superdealz.me:443",
        "via": "proxy-service"
      },
      {
        "why": "Get the latest Dealz.",
        "transport_protocol": "tcp",
        "target": "mqtt://192.xxx.xxx.xxx:5028"
      }
    ]
  }

This specification allows you to define both internal and external dependencies for every service. You can also specify manual dependencies, which are stored in a separate dependencies.yaml file.

Apache Maven considers API dependencies, as well. Apache Maven is a powerful tool for project management and comprehension, which would make it a good choice for API dependency management as well. In Apache Maven, API dependencies are simply specified in a dedicated endpoint, which is a relatively simple and elegant solution. They are also specified in a project object model named pom.xml.

Their goals also speak to why API management is so integral in today’s development world:

  • Making the build process easy
  • Providing a uniform build system
  • Providing quality project information
  • Encouraging better development practices

Additional Tools for Verifying API Dependencies

Many of the techniques we’re mentioning in this article deal with internal APIs. But, most apps and API-related projects involve at least some sort of third-party API. What are some ways that you can test and verify your third-party APIs are functioning as they should be? There are a few dedicated tools you can work into your development stack that will help you out in this regard.

Postman Collections is a particularly classy and useful way to ensure APIs are working as intended. Postman Collections allows you to keep track of your API requests and commands, which can be stored in folders known as ‘collections.’ It’s not a complete solution, but it does let you keep all of your API call info in one place, which is far better from a housekeeping standpoint. It also lends itself to API testing, which is one component of API dependency management anyway. Even better, it’s easy to share and re-use collections, which means you can use your API collection in a microservice-like environment. It’s also easy to share collections with others using Postman, as you can easily export your collection folder.

Some API monitoring solutions could fit the bill, as well. These tools allow you to ping an API and make sure it’s functioning the way it should. There’s APIMetrics, for starters, which offers a full-bodied monitoring solution. API Expert also provides an overview of performances for common APIs. Both include in-depth dashboards, as well. These two may be overkill for someone looking for a simple API verification tool. Still, they could be a good choice if you’ve been looking for API monitoring or a visualization dashboard.

Some API testing tools also offer the ability to test third-party APIs as part of a CI/CD environment. API Fortress, for example, is a particularly extensive resource that allows you to test APIs in bulk, as well as offering a number of other useful features for API testing.

Finally, if you want a solution that does it all Moesif may be a good fit. Moesif is also a powerful, detailed API monitoring and management powerhouse. It offers everything from extensive logs of recent API calls to monitoring performance and usage.

Any of these solutions are more than adequate to test any third-party API, and automate the process, to boot. Keep in mind that each of the solutions mentioned in this section can cost money, but each offers at least a trial period, if not a full Free Tier, so you can try them out for yourself and see if they’re a fit.

If you’ve already got an API monitoring or testing solution in place, these may be a bit overkill. But if you happen to need additional API resources and the ability to test your third-party API dependencies, these could be a good choice!

Applying Dependency Management Principles to APIs

Integrating some of these principles into your own development process will help you to better understand and visualize the API dependencies in your projects and across your organization. It can also greatly streamline common API-intensive processes like API testing, monitoring, and security while providing a smoother experience for your customers and consumers at the same time.

We’ll end on a high note, with one final bonus of taking the time to understand API dependencies. These same principles can be applied to other development styles, as well. Dependencies are an important part of microservices, for instance, and tracking such dependencies could help inform developers when not to use APIs. One thing you must remember is that every API call will add processing time to your final product. If certain files are used commonly and extensively, it may be better to host them as local files to keep processing time to a minimum. You could use this as part of your solution to versioning, API wrapping, and probably too many other applications to name.