OpenAPI specification drills it’s ways into becoming a standard in many software companies. Let’s say your organization wants to follow the API-first approach. It means that before implementing any cross-team (or 3d party integration) functionality, the parties first agree on the contract, and review it.

I had this approach in mind since I’ve started working in a small payment provider company. Contracts there meant everything, and negotiating the API/authentication process was basically 80% of the job. When I joined Zalando in 2017 I was very happy to see this approach being adopted company-wise.

So far my gut-feeling is that defining and negotiating the API before jumping to the implementation significantly reduces the communication overhead. Having statically defined models and abstractions used this days in the programming systems make it sometimes very hard to make new changes in the data models shaping the API.

In this post I’d like to cover a couple of tools that may help you in designing a Clojure application with API-first approach.

0. Compojure-API

github

My first library written in Clojure that enables easy setup for swagger. At that point after learning some Clojure syntax, concepts of ring HTTP server abstractions and developing a couple of applications with compojure router, having compojure-api felt like a natural choice for embedding swagger into your code.

Usage

lein new compojure-api +clojure-test newproject

++advantages++

  • Fits pretty well into existing ring-based ecosystem.
  • These days supports both clojure.spec and prismatic schema for schema validation.
  • Easy to start with, no need to leave Clojure editor.
  • Async requests support (core.async, manifold)

–disadvantages–

  • A DSL is used for generating the API specification. It can diverge from the features implemented by OpenAPI and it’s not very convenient to share in a polyglot environment/with the other teams.
  • Even though schema validation seems to be like a must-have feature, sometimes the expectations don’t match the reality. If you have some very specific requirements for schema validation and also agree on response messages, there are probably only two options - validate on your own or contribute to the project.

1. Swagger1st

github

lein new swagger1st newproject

Used this library when working in zalando, I am still quite happy with it. As it comes from the title, swagger1st encourages you to first write swagger definition, use operationId as the link to the handler function. Having a swagger definition at hand this library was the first choice for me when prototyping RESTful applications.

++advantages++

  • API-first approach for real. You may take the swagger.yaml file from another team and bootstrap the prototype in no time.
  • Very simple and pluggable into any other ring ecosystem.

–disadvantages–

  • No automatic schema validation. This sometimes becomes a burden, since writing custom validators requires interaction with path definitions and parameters. Since the latter are defined only in the spec, validating the parameters/response requires writing/defining additional logic in handlers that can diverge from the schema specification at some point.
  • A bit tricky logic when other libraries also enable some routing (prometheus)
  • Not actively maintained (which keeps it simple though).

2. Reitit

github

Tried it a couple of times, but still finding my perfect use case for it. It is now part of quite popular luminus template. I really want to give it a try, at the first glance, this is my pros/cons list for it:

Usage

sample code

++advantages++

  • Schema coercion. The extreme care and effort that was put in the development of the schema/spec coercion support is huge.
  • Fully data driven. Your API is fully represented as a data structure.
  • Pluggable extensions.
  • Maintained by Metosin.

–disadvantages–

  • Becomes quite heavy and requires a number of opinionated libraries. This is a bit too of a personal opinion, but what I absolutely enjoyed in the Clojure community from the very beginning was the ability to play with the components as with lego bricks - swapping them/changing the order of the handlers and so on.
  • Currently similar to compojure: couldn’t find a way to customize error messages in case of coercion failures.

Conclusion

Swagger ecosystem is very powerful, and I am always seeking the possibilities to leverage it’s power. Having swagger definition file, documentation, client libraries, server code and many other useful things may be simply generated with swagger-codegen. When writing the code, it’s crucial to keep this definition consistent with the currently running code. Hope this overview of 3 solutions that help achieving API-first approach with Clojure would help you in choosing the right tool.