Build Status

servant-openapi

Hackage

Automatically derive OpenAPI definitions for your Servant API.

openapi

Hackage

Implements data types for OpenAPI 3.0.3 to specify your API and type serialization formats. Includes a type class to tie your haskell types to their schemas, and supports automatically deriving schemas together with the serialization directly using DerivingVia. This is accomplished by instances for the expressive type-level DSL of aeson-deriving.

Example Usage

type MyOpts =
  '[ FieldLabelModifier := SnakeCase
  ,  SumEncoding := TaggedObject "type" "contents"
  ,  TagSingleConstructors := 'True
  ]

newtype UserId = UserId Int64
  deriving stock (Show, Eq)
  deriving newtype (ToJSON, FromJSON, ToOpenAPISchema)

data CreateUser = CreateUser
  { firstName :: Text
  , lastName :: Text
  , dateOfBirth :: Day
  }
  deriving stock (Show, Generic)
  deriving (ToJSON, FromJSON, ToOpenAPISchema) via GenericEncoded MyEncodingOpts CreateUser

data User = User
  { userId :: UserId
  , createdAt :: UTCTime
  , firstName :: Text
  , lastName :: Text
  , dateOfBirth :: Day
  }
  deriving stock (Show, Generic)
  deriving (ToJSON, FromJSON, ToOpenAPISchema) via GenericEncoded MyEncodingOpts User

type UserAPI =
  "users" :>
    (    ReqBody '[JSON] CreateUser :> Put '[JSON] User
    :<|> Capture "user_id" UserId :> Delete '[JSON] NoContent
    )

writeUserOpenAPI :: FilePath -> IO ()
writeUserOpenAPI path =
  writeSchemaYaml path $
    toOpenAPI (Proxy @UserAPI) InfoObject
      { title = "Acme Users API"
      , description = Nothing
      , termsOfService = Nothing
      , contact = Nothing
      , license = Nothing
      , version = "1.0"
      }

The resulting schema, obtained by running writeUserOpenAPI is the following:

components:
  schemas:
    User:
      required:
      - type
      - user_id
      - created_at
      - first_name
      - last_name
      - date_of_birth
      title: User
      type: object
      properties:
        first_name:
          type: string
        date_of_birth:
          format: date
          type: string
        last_name:
          type: string
        created_at:
          format: date-time
          type: string
        type:
          type: string
          enum:
          - User
        user_id:
          format: int64
          type: integer
    CreateUser:
      required:
      - type
      - first_name
      - last_name
      - date_of_birth
      title: CreateUser
      type: object
      properties:
        first_name:
          type: string
        date_of_birth:
          format: date
          type: string
        last_name:
          type: string
        type:
          type: string
          enum:
          - CreateUser
openapi: 3.0.3
info:
  version: '1.0'
  title: Acme Users API
paths:
- - /users
  - put:
      responses:
        '400':
          description: Failure to parse request body or required parameter
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'
          description: Successful result response
      request_body:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateUser'
- - /users/{user_id}
  - delete:
      responses:
        '400':
          description: Failure to parse request body or required parameter
        '200':
          description: Empty response
      parameters:
      - allow_empty_value: false
        required: true
        schema:
          format: int64
          type: integer
        in: path
        name: user_id