Jack's blog

Elm OpenAPI codegen

I spent this weekend scratching a personal itch. I'm working on a little toy project that's using Elm on the frontend and Go on the backend.

I'm using an OpenAPI specification to define the contract between my frontend and backend.

On the backend I'm using oapi-codegen to generate Go types from my spec.

oapi-codegen -config oapi-codegen.yaml openapi.yaml

Initially I was hand writing the corresponding Elm types and decoders on the frontend, but this quickly became tideous.

I looked around for existing solutions, but instead of choosing one of the more popular ones I took a stab at trying to write my own, the result is:

Jackevansevo/elm-openapi-codegen

Now, given the following schema:

components:
  schemas:
    Client:
      type: object
      required: [id, firstName, lastName, createdAt]
      properties:
        id:
          type: integer
          format: int64
        firstName:
          type: string
        lastName:
          type: string
        crmId:
          type: integer
          format: int64
          nullable: true
        createdAt:
          type: string
          format: date-time

oapi-codegen will create:

// Package api provides primitives to interact with the openapi HTTP API.
//
// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.7.0 DO NOT EDIT.
package api

import (
	"time"
)

...

// Client defines model for Client.
type Client struct {
	CreatedAt    time.Time `json:"createdAt"`
	FirstName    string    `json:"firstName"`
	Id           int64     `json:"id"`
	crmId *int64           `json:"crmId,omitempty"`
	LastName     string    `json:"lastName"`
}

The elm-openapi-codegen tool will generate

-- Generated by elm-openapi-codegen. Do not edit.
module Api.Client exposing (Client, clientDecoder)

import Json.Decode as Decode exposing (Decoder)
import Json.Decode.Pipeline exposing (optional, required)


type alias Client =
    { createdAt : String
    , firstName : String
    , id : Int
    , crmId : Maybe Int
    , lastName : String
    }


clientDecoder : Decoder Client
clientDecoder =
    Decode.succeed Client
        |> required "createdAt" Decode.string
        |> required "firstName" Decode.string
        |> required "id" Decode.int
        |> optional "crmId" (Decode.nullable Decode.int) Nothing
        |> required "lastName" Decode.string

For a full list of features, see the Examples page