Tom Lous
Tom Lous

Reputation: 2899

Generate Swagger / OpenAPI specification from scala source code (http4s)

So I'm no swagger expert, but all systems using swagger require you to have the swagger specification in JSON or YAML defining all endpoints (and such) of your API.

My question is: Are there know ways to generate these specification files based on the actual source code? I'm asking, because it seems very hard to keep endpoint code & documentation in sync when you start adding properties or returning slightly different results.

So when I have this code (using http4s & RhoService):

object HelloWorld {
  val service = new RhoService {
    GET / "hello" / 'name |>> { name: String =>
      Ok(Json.obj("message" -> Json.fromString(s"Hello, ${name}")))
    }
  }
}

It would be great if it could produce (in some way:)

/hello/{name}:
    get:
      tags:
      - "HelloWorld"
      summary: ""
      description: ""
      operationId: "getHellobyName"
      produces:         
      - "application/json"
      parameters:
      - name: "name"
        in: "path"
        description: ""
        required: true
        type: "string"
      responses:
        200:
          description: "successful operation"
          schema:
            $ref: "#/definitions/Hello"           
      security:
      - api_key: []

Upvotes: 9

Views: 5889

Answers (3)

Julien Richard-Foy
Julien Richard-Foy

Reputation: 9663

Disclaimer: I’m the author of endpoints4s.

Similar to tapir (mentioned in another answer) endpoints4s is a library that can produce http4s servers and OpenAPI documentation for HTTP endpoints.

You would write your example like the following:

// --- This is the description of your HTTP service

import endpoints4s.algebra

case class Hello(message: String)

trait HelloWorldEndpoints
  extends algebra.Endpoints
    with algebra.JsonEntitiesFromSchemas {

  implicit val helloJsonSchema: JsonSchema[Hello] =
    field[String]("message")
     .xmap(message => Hello(message))(hello => hello.message)

  val hello: Endpoint[String, Hello] =
    endpoint(
      get(path / "hello" / segment[String]("name")),
      ok(jsonResponse[Hello])
    )

}

// --- This is an OpenApi documentation of the endpoints defined
// --- in HelloWorldEndpoints

import endpoints4s.openapi
import endpoints4s.openapi.model.{ Info, OpenApi }

object HelloWorldDocumentation
  extends HelloWorldEndpoints
    with openapi.Endpoints
    with openapi.JsonEntitiesFromSchemas {

  val api: OpenApi =
    openApi(Info(title = "Hello API", version = "1.0"))(
      hello
    )

}

// --- This is an http4s server that implements the endpoints defined
// --- in HelloWorldEndpoints

import endpoints4s.http4s
import cats.effect.IO
import org.http4s.HttpRoutes

object HelloWorld
  extends http4s.server.Endpoints[IO]
    with http4s.server.JsonEntitiesFromSchemas
    with HelloWorldEndpoints {

  val service: HttpRoutes[IO] = HttpRoutes.of(
    routesFromEndpoints(
      hello.implementedBy(name => Hello(s"Hello, ${name}"))
    )
  )

}

// --- And this is an http4s service that publishes the OpenApi documentation

object OpenApiServer
  extends http4s.server.Endpoints[IO]
    with http4s.server.JsonEntitiesFromEncodersAndDecoders {

  val openApiService: HttpRoutes[IO] = HttpRoutes.of(
    routesFromEndpoints(
      endpoint(
        get(path / "open-api.json"),
        ok(jsonResponse[OpenApi])
      ).implementedBy(_ => HelloWorldDocumentation.api)
    )
  )

}

Upvotes: 1

Tom Lous
Tom Lous

Reputation: 2899

It is not documented well, but apparently http4s' RhoService adds middleware to generate a swagger.json based on your routes:

Fetch it by calling 'http://localhost:8080/swagger.json'

Git source: https://github.com/http4s/rho/blob/0c5aed48aeeea18b1d66d88b58cd3deea733f070/swagger/src/main/scala/org/http4s/rho/swagger/SwaggerSupport.scala#L30

Upvotes: 5

adamw
adamw

Reputation: 8606

Disclaimer: I'm the author of tapir.

rho is one possibility. Another approach is to completely separate the description of the API endpoint from the business logic.

Having a description of an endpoint, which is a regular Scala value, it can be then interpreted as either a server (given the "business logic" function), or documentation.

Two Scala libraries that can provide both http4s and OpenAPI interpreters are tapir and typeapi.

Upvotes: 7

Related Questions