How does one make use of the fluent interface methods in haxe.http.HttpBase?

I’m presently writing a program that makes a POST request to a RESTful API. I have to customize the headers and add a body, so am making use of haxe.Http constructor as opposed to making a request via sys.Http.requestUrl.

Now, the documentation at haxe.http.HttpBase - Haxe 4.3.7 API shows that a few of the methods state “This method provides a fluent interface”. Particularly, the setHeader and setPostData methods claim this (among others). So I would expect to be able to do something like:

final request = new haxe.Http(url)
  .setHeader("authorization", 'Bearer ${key}')
  .setHeader("content-type", "application/json")
  .setPostData(requestBody)
  .request(true);

However, the signatures for setHeader and setPostData show a return type of Void, which I wouldn’t expect in methods participating in a fluent interface. The compiler corroborates this when it fails to compile that snippet, noting that type Void doesn’t have a method called setHeader.

I’m realizing that per the docs, even if I could get away with doing that, that the onData and onError fields that need to be configured with a function don’t have the note indicating that they are exposing a fluent interface, so I would still have to do some additional configuration even if this methods were fluent-style (meaning I couldn’t make a request in one fell swoop with chaining anyway).

For reference, I accomplished what I needed with the following:

final request = new haxe.Http(url);
request.setHeader("authorization", 'Bearer ${key}');
request.setHeader("content-type", "application/json");
request.setPostData(requestBody);
request.onData = data -> trace(data);
request.onError = msg -> trace(msg);
request.request(true);

It’s no big deal to write it that way since I’ll be encapsulating it in wrapper methods/classes, but I enjoy the aesthetics and convenience of fluent interfaces. If it is possible to use them per the docs, how would one accomplish that? Searches of forums, the API docs, the Cookbook, and general inquiries haven’t turned up anything for me.

Thanks for any comments and assistance!

Looking at the sources, the fluent interface has been dropped with Haxe 4 for some reason.. it can be restored with -lib hx3compat (after a few tests, it seems was likely removed because it wasn’t working exactly as expected)

That mention should probably be removed from documentation now..

You can make your own fluent interface with static extension: Try Haxe!

using Test;

class Test {
  static function main() {
    var url = "https://example.com";
    var key = "foo";
    var requestBody = "bar";
        
    final request = new haxe.Http(url)
    .setHeaderFluent("authorization", 'Bearer ${key}')
    .setHeaderFluent("content-type", "application/json")
    .setPostDataFluent(requestBody)
    .setOnData(data -> trace(data))
    .setOnError(msg -> trace(msg))
    .requestFluent(true);
  }
  
  public static inline function setHeaderFluent(req:haxe.Http, name:String, value:String):haxe.Http {
    req.setHeader(name, value);
    return req;
  }
  
  public static inline function setPostDataFluent(req:haxe.Http, data:String):haxe.Http {
    req.setPostData(data);
    return req;
  }
  
  public static inline function requestFluent(req:haxe.Http, ?post:Bool):haxe.Http {
    req.request(post);
    return req;
  }
  
  public static inline function setOnData(req:haxe.Http, cb:String->Void):haxe.Http {
    req.onData = cb;
    return req;
  }
  
  public static inline function setOnError(req:haxe.Http, cb:String->Void):haxe.Http {
    req.onError = cb;
    return req;
  }
}

Or with abstracts: Try Haxe!

class Test {
  static function main() {
    var url = "https://example.com";
    var key = "foo";
    var requestBody = "bar";
        
    final request = new MyHttp(url)
    .setHeader("authorization", 'Bearer ${key}')
    .setHeader("content-type", "application/json")
    .setPostData(requestBody)
    .setOnData(data -> trace(data))
    .setOnError(msg -> trace(msg))
    .request(true);
  }
}

// TODO: might want to expose more with @:forward
abstract MyHttp(haxe.Http) {
  public inline function new(url) this = new haxe.Http(url);
  
  public inline function setHeader(name:String, value:String):MyHttp {
    this.setHeader(name, value);
    return abstract;
  }
  
  public inline function setPostData(data:String):MyHttp {
    this.setPostData(data);
    return abstract;
  }
  
  public inline function request(?post:Bool):MyHttp {
    this.request(post);
    return abstract;
  }
  
  public inline function setOnData(cb:String->Void):MyHttp {
    this.onData = cb;
    return abstract;
  }
  
  public inline function setOnError(cb:String->Void):MyHttp {
    this.onError = cb;
    return abstract;
  }
}
1 Like

Thank you Rudy - both are excellent solutions, and I’m leaning towards using the latter. Your insight is very much appreciated!