Wrapper modules can lead to better APIs
Tags: elm, webdev
Published: 2023-08-09 11:39:57

---
title: Wrapper modules can lead to better APIs
published: true
description: Learn how wrapper modules can help you design better APIs for your web apps.
tags: elm, webdev
# cover_image: https://direct_url_to_image.jpg
# Use a ratio of 100:42 for best results.
# published_at: 2023-08-09 10:36 +0000
---

When building an Elm web app you may need to use a third-party package to help solve your problem. In some cases, your usage requirements may not perfectly align with the API provided by the package. In these cases, rather than scatter your use of the package throughout different modules of your application, it is usually better to **limit your use** of the package to a single module (which I will refer to as a **wrapper module**) with an API that better suits your application's needs.

## Why use a wrapper module?

1. It helps you reduce the coupling between different parts of your application. This in turn makes your application more flexible to changes. For e.g. if another package comes along that is better in some way, say it has better performance, then because the old package is used exclusively within a wrapper module it will be much easier to switch to the new package.
2. You can define the API of your wrapper module independently of the API of the package. This allows you to design an API that's a better fit for your application.
3. It can be easier to unit test your wrapper module.

Obviously, there are trade-offs and not every situation warrants it's use.

Learn more: [Using third-party libraries - always use a wrapper?](https://softwareengineering.stackexchange.com/q/107338).

## A practical example

The [Flight Booker](https://dwayne.github.io/elm-7guis/flight-booker.html) task from [7GUIs](https://7guis.github.io/7guis/) had certain date requirements that were easier to satisfy if I used a preexisting package. [justinmimbs/date](https://github.com/justinmimbs/date) package had support for everything I needed to do and more but it did things a little differently.

My requirements were straightforward:

- Be able to get today's date.
- Be able to create a date from a string of the form `DD.MM.YYYY`.
- Be able to determine if one date comes after another date.
- Be able to convert a date to a string of the form `DD.MM.YYYY`.

By writing a wrapper module I was able to contain my use of `justinmimbs/date` package and expose the precise API I needed to handle the date requirements.

```elm
module Task.FlightBooker.Date exposing
    ( Date
    , fromString
    , isLaterThan
    , toString
    , today
    )

import Char
import Date as JDate
import Parser as P exposing ((|.), (|=))
import Task
import Task.FlightBooker.Parser as P


type Date
    = Date JDate.Date


today : (Date -> msg) -> Cmd msg
today toMsg =
    JDate.today
        |> Task.perform (toMsg << Date)


fromString : String -> Maybe Date
fromString s =
    case P.run dateParser s of
        Ok iso ->
            case JDate.fromIsoString iso of
                Ok date ->
                    Just <| Date date

                Err _ ->
                    Nothing

        Err _ ->
            Nothing


dateParser : P.Parser String
dateParser =
    P.succeed (\dd mm yyyy -> yyyy ++ "-" ++ mm ++ "-" ++ dd)
        |= P.chompExactly 2 Char.isDigit
        |. P.chompIf ((==) '.')
        |= P.chompExactly 2 Char.isDigit
        |. P.chompIf ((==) '.')
        |= P.chompExactly 4 Char.isDigit
        |. P.end


isLaterThan : Date -> Date -> Bool
isLaterThan (Date date1) (Date date2) =
    JDate.compare date2 date1 /= LT


toString : Date -> String
toString (Date date) =
    let
        dd =
            JDate.day date
                |> String.fromInt
                |> String.padLeft 2 '0'

        mm =
            JDate.monthNumber date
                |> String.fromInt
                |> String.padLeft 2 '0'

        yyyy =
            JDate.year date
                |> String.fromInt
                |> String.padLeft 4 '0'
    in
    dd ++ "." ++ mm ++ "." ++ yyyy
```

Source: [Task.FlightBooker.Date](https://github.com/dwayne/elm-7guis/blob/67cb89fa24fc9b115546bad314e880b0e9cf9c62/src/Task/FlightBooker/Date.elm).

From the source code above you can see how I ended up with a smaller and focused API.

1. There's a custom `Date` type that wraps the `Date` type from the `justinmimbs/date` package. A client of the wrapper module can only use the functions that work on my  `Date` type. This is how usage of the external package is limited and controlled.
2. There's one constructor named `fromString` that takes a `String` and returns a `Date` if the `String` is of the form `DD.MM.YYYY`.
3. There's a function named `isLaterThan` , which is much [nicer to use](https://github.com/dwayne/elm-7guis/blob/67cb89fa24fc9b115546bad314e880b0e9cf9c62/src/Task/FlightBooker.elm#L62-L66) over direct use of the `compare` function that the `justinmimbs/date` package provides.
4. There's a converter named `toString` that converts my  `Date` type to a `String` of the form `DD.MM.YYYY`.
5. And finally, there's a command tailored to my `Date` type for getting today's date.

## Conclusion

Elm has great support for writing highly [cohesive](https://en.wikipedia.org/wiki/Cohesion_(computer_science)) modular code that's [loosely coupled](https://en.wikipedia.org/wiki/Loose_coupling) and easy to maintain. With Elm's modules it's simple to hide implementation details and expose an API that caters to the precise requirements of your application. I hope the simple example I shared above gets you thinking about ways to clean up your own applications with helpful wrapper modules.