WSClient vs. StandaloneWSClient

I’m a bit confused about the relationship between play.api.libs.ws.WSClient and play.api.libs.ws.StandaloneWSClient.

The context is that I have a library where I want to use the ws client to connect to some web API, and I want to use this library both from a Play application and from a non-Play application, and I’m not sure what dependency to add to that library.

When I see WSClient and StandaloneWSClient I immediately think, ok, WSClient is the interface, and StandaloneWSClient is one implementation of this, so I’ll use the interface in the library and provide specific implementations in the application (i.e. the library would be exposing class SomeApiService(ws: WSClient)).

However, when I look at the source I see trait StandaloneWSClient extends Closeable and trait WSClient extends Closeable so there is appearently no relationship between the two (except that they can be closed).

Furthermore, there is trait WSRequest extends StandaloneWSRequest which is the opposite of what I expected.

The fact that they are both defined in play.api.libs.ws adds to the confusion.

So - which dependency should I add to my library, and which version of the ws client should I use there so that the library could be reused both in a Play and a non-Play application?

Basically what happened was Play used to come with WSClient and StandaloneWSClient didn’t exist. And then someone decided to make WSClient a standalone library independent of play, so StandaloneWSClient was created, which is basically a subset of WSClient that can be used without Play.

Personally I don’t recommend using either one of them. The reason is that the API of WSClient hasn’t been very stable at all. The breaking API changes over the years broke my code multiple times, so I just ended up replacing it completely.

What do you recommend using instead?

I use Apache HttpClient 5. For the same reason (too many breaking changes over the years), I’m cautious of using any play.xxx class in general, other than some of the fundamental classes you can’t avoid, but even for those classes, there have been too many breaking changes. It’s just my opinion based on my personal experience.

There are two different interfaces: WSClient, which returns instances of WSRequest containing Play-specific functionality, and StandaloneWSClient, which returns more minimal StandaloneWSRequest instances. I can see how the naming convention could be confusing. If you look at the default implementations, you’ll see AhcWSClient is just a wrapper for StandaloneAhcWSClient.

Specifically, the Play-specific WSRequest mixes in traits for play-json and xml support. In the standalone version you’ll need to add extra dependencies and imports for those things, since the goal of the standalone version is to modularize the API and minimize dependencies.

Your separate library should prefer StandaloneWSClient over WSClient so it can avoid the extra Play dependencies. Your Play application can use either one, though it will likely be more convenient to use WSClient. Play doesn’t provide access to a StandaloneWSClient instance by default, but there are some simple ways to do that described here: access to StandaloneWSClient · Issue #7638 · playframework/playframework · GitHub

There are certainly reasons why you might prefer other HTTP clients for various reasons, depending on your preferences and goals.

I believe most of the recent breaking changes in Play WS were associated with separating it into the standalone library. Going forward I’d expect the APIs to be relatively stable, but I suppose if you’re looking to build a widely distributed library you might want something more minimal.