-
-
Save adamw/6248904a39eb3fedb89d3c9a1752ba10 to your computer and use it in GitHub Desktop.
// Hello.java | |
public class Hello { | |
public String helloWorld() { | |
return "Hello World"; | |
} | |
} | |
// HelloEndpoints.java | |
public class HelloEndpoints { | |
private Endpoint helloWorldEndpoint(Hello hello) { | |
return Endpoint | |
.withPath("/hello") | |
.method(GET) | |
.produces(MediaType.TEXT_PLAIN) | |
.invoke(hello::helloWorld); | |
} | |
public List<Endpoint> endpoints(Hello hello) { | |
return Arrays.asList(helloWorldEndpoint(hello), ...); | |
} | |
} |
Btw. as for the specific example with extracting path parameters, I think a Java API could be sth like this:
return Endpoint
.withPath("hello")
.withPathSegment(Parsers.string)
.withPathSegment(Parsers.integer)
.withBody(Parsers.json)
.method(PUT)
.produces(MediaType.APPLICATION_JSON)
.invoke(Logic::helloWorld);
Here, the first invocation of withPathSegment
would be on Endpoint0
, and yield an Endpoint1<String>
(an endpoint, which requires a single string parameter - in this case, read from the path, but could be read from the query as well).
The second would be on Endpoint1<String>
and yield an Endpoint2<String, Integer>
, etc. Then, the invoke
method can be properly typed to expect a method reference with the right signature.
That's of course just a sketch, and would require the library author to define a number of EndpointN
classes (or maybe auto-generate them?). In Scala, you have have a single Endpoint
class which can accumulate the input/output parameters with some combinators (again, see Tapir for details).
Thanks for the comments!
First of all I think it's worth noting that with or without annotations, the approach is basically the same:
@Path
,@RequestMapping
etc.). In another, we use a "normal" language, such as Scala, Java or Kotlin. From a high level, the result in both cases is the same: a data structure containing information about your endpointsThere might be many interpreters, both when using annotations and when not. For example, you can expose endpoints based on annotations, or generate documentation. Without annotations, you can do the same; as someone pointed out, that's what Tapir is aspiring to do.
The big advantage of the "normal code" approach is that you have much more flexibility when creating the endpoint description, unlike being unnecessarily constrained with the limitation of annotations. However, annotations do have the advantage of being able to reference fields or methods - something that is not directly possible in Java (but is possible in Scala with shapeless/macros). But then again, you might not have to reference fields/methods in the first place - depend on how you define the API to create the descriptions.
Summing up: I wouldn't use annotations for creating endpoint descriptions. Normal code is much more flexible, allows operating on values in the base language (just like any other value) and can be interpreted to an actual endpoint in the same way as with the annotations approach. Annotations do have their usages, but mainly for compile time (for example, apart from things such as
@SuppressWarnings
, in Scala they are often useful for directing compile-time code generation via macros).