Gradle Configurations Explained: What is the difference between API and Implementation?

Vladimír Oraný
Sep 3 · 6 min read
Available configurations when java-library plugin is applied to gradle. Source: Gradle Java Library Plugin

Introduction

Gradle dependencies are grouped into sets called configurations. Different configurations are used for building classpath for the major two tasks — compile classpath is used for compilation and runtime classpath is used for running the application. The usage of the new configurations — api, implementation, and runtimeOnly is still a bit of a mystery for many developers, so let’s explain it on a very trivial example.

TL;DR

In case of exposing dependencies to the consumers, you need to focus on the pink boxes. Configurations included in apiElements are exposed to the consumers for their compile classpath. Analogously, configurations included in runtimeElements are exposed to the consumers for their runtime classpath. Therefore

  • implementation configuration is used for compile and runtime classpath but it is only exposed to the consumers for their runtime classpath
  • runtimeOnly configuration is only used for the runtime classpath and it is also exposed to the consumers for their runtime classpath
  • compileOnly configuration is only used for the compile classpath and it is not exposed to any consumer
  • compileOnlyApi configuration is only used for the compile classpath and it is also exposed to the consumers for their compile classpath when Gradle metadata is consumed

Christmas Dinner (Application Project)

plugins {
id 'application'
}

dependencies {
api project(':dish')
api project(':cutlery')
api project(':potato-salad')
api project(':breaded-dish')

implementation project(':potato-salad-with-ham')
implementation project(':breaded-carp')

runtimeOnly project(':plate')
}

mainClassName = 'christmas_dinner.ChristmasDinner'
./gradlew :christmas-dinner:run# result:Serving:
[plate with potato salad with ham, breaded carp]
Cutlery:
[fork, knife]

Potato Salad with Ham (Library Project)

In the end-projects such as the application that are not used as a dependency to any other project, the way how configurations work is pretty clear. What is not so clear is how the different types of dependencies behave transitively. Let's dig deeper into one of the dishes — the potato salad with ham:

dependencies {
api project(':dish')
api project(':potato-salad')
api project(':ham')

implementation project(':boiled-potatoes')
implementation project(':mayo')
implementation project(':onion')
implementation project(':pickles')
implementation project(':peppers')
implementation project(':bowl')
implementation project(':cutting-board')
implementation project(':vegetable-knife')
implementation project(':potato-slicer')

runtimeOnly project(':fork')
}
  • second group contains everything else required to prepare the dish
  • the last group contains the kitchenware required to serve the dish
Serving:
[plate with potato salad with ham, breaded carp, ham]
Cutlery:
[fork, knife]

Shopping List (Runtime Classpath)

Let's imagine that the dinner should be served tonight in a brand new apartment. You want to be sure that you have everything ready for cooking as well as for serving the dish. You're planning to visit the department store nearby and you want to be sure that don't forget everything. This is how would the shopping list look like.

Summary

If you are creating Java library which then you should think twice which of your dependencies are required for the compilation of the depending projects. If any type is exposed publicly into the signature of your classes and interfaces then you need to declare it as api. The dependencies used internally within method bodes can be declared as implementation. The dependencies only required in runtime can be declared as runtime.

Stories by Agorapulse

Agorapulse is a leading Social Media Management platform.