purescript-contrib/purescript-argonaut-core

`Requestable` instance for `Json`

cdepillabout opened this issue · 9 comments

I often have the desire to send Json to a REST API. I'd like to use post from Network.HTTP.Affjax, but there doesn't seem to be a Requestable instance for Json.

Is there a cannonical way to turn Json into something that is Requestable?

I guess I could use show or printJson on the Json object to get a String, and then pass that to post, but I'm wondering if there is a better way.

Maybe I'm overlooking some way to take a type that has an instance of Generic and just turn it directly into something that is Requestable...?

After thinking about this more, I really just want to have a datatype like the following and have it be both Requestable and Respondable as JSON:

newtype RegistrationInfo = RegistrationInfo { email :: String, password :: String }

What is the best way to accomplish this?

I think this is what is being discussed here: purescript-contrib/purescript-affjax#16

You can create custom Requestable / Respondable instances for any of your own types. Will that suffice?

Adding Requestable instances for arbitrary content is a bit of a tricky one, since XMLHttpRequest only supports the types of values that we provide instances for - it is possible to send JSON, but it has to be stringified first and then just sent with the right header, if you pass the unencoded JSON in it will just send a string "[object Object]".

I guess what I really want to know is the following question.

What is the best (recommended?) way to go from an arbitrary type like the following, to Requestable in order to send data to a JSON REST api?

newtype MyType = MyType { fielda :: Int
                        , fieldb :: String
                        }

Ideally I'd like to do it generically by using Data.Generic.

Currently I'm doing something like the following:

newtype Register = Register { email :: String
                            , password :: String
                            }

derive instance genericRegister :: Generic Register
instance showRegister :: Show Register where show = gShow
instance eqRegister :: Eq Register where eq = gEq
instance encodeJsonRegister :: EncodeJson Register
    where encodeJson = gEncodeJson

register :: forall eff. Register -> Aff (ajax :: AJAX | eff) (AffjaxResponse Foreign)
register registrationInfo = do
    result <- affjax $ defaultRequest { method = POST
                                      , url = "http://192.168.56.12:8081/user/register"
                                      , content = Just <<< show $ encodeJson registrationInfo
                                      , headers = [ ContentType applicationJSON
                                                  , Accept $ MimeType "*/*"
                                                  ]
                                      }
    return result

However, the Just <<< show $ encodeJson registrationInfo feels kind of dirty? Is there a better way to handle this?

I guess it would be convenient if there was a Requestable instance for Json, but:

Adding Requestable instances for arbitrary content is a bit of a tricky one, since XMLHttpRequest
only supports the types of values that we provide instances for - it is possible to send JSON, but it
has to be stringified first and then just sent with the right header

Maybe the Requestable instance for Json could do this? However, there doesn't seem to be any functionality in the Requestable instance to set the Content-Type header?

Yeah exactly, although we do have an issue with an idea about how we could improve Requestable to support it: purescript-contrib/purescript-affjax#8

I think a way we could quickly get this working is to change the instance as described in that issue but make the mime type a Maybe - that way we can override if needs be, but otherwise rely on the browser handing the header for us based on the content type (meaning we can avoid the tricky part of multipart/form-data mentioned in the issue).

Seems like this was fixed by purescript-contrib/purescript-affjax#53 ?

garyb commented

Yes indeed, thanks for the reminder!