Help with the result type of a Promise then/thenResolve/catch

Hello, I keep hitting a block with the result type of a promise. Based on the information provided by @ryyppy in the rescript-promise I added a Belt.Result.t type to the response of the mongodb find binding.

// Mongo.res
module Response = {
  type t
  @send external toArray: t => array<'a> = "toArray"
}

module Collection = {
  type t
  @send
  external find: (t, 'query) => Promise.t<Belt.Result.t<Response.t, Js.Nullable.t<Js.Exn.t>>> =
    "find"
}

module Db = {
  type t
  type collectionName = string
  @send external collection: (t, collectionName) => Collection.t = "collection"
}

module ClientInstance = {
  type t
  type dbName = string                                                                                    
  @send external db: (t, dbName) => Db.t = "db"                                                           
}                                                                                                         
                                                                                                          
module Client = {                                                                                         
  type t                                                                                                  
  @module("mongodb") @new external make: string => t = "MongoClient"                                      
  @send external connect: t => Promise.t<ClientInstance.t> = "connect"                                    
  @send external close: t => unit = "close"                                                               
}                              

and the corresponding

 let uri = "mongodb://localhost:27017"
  let dbName = "rescript"
  let collectionName = "documents"
  let query = {"isLearningRescript": true}
  
  open Mongo
  open Promise
  
  let client = Client.make(uri)
  client
  ->Client.connect
  ->then(instance => {
    instance->ClientInstance.db(dbName)->Db.collection(collectionName)->resolve
  })
  ->thenResolve(collection => {
>>  collection->Collection.find(query)->Response.toArray->Ok
  })
  ->catch(e => {
    switch e {
    | JsError(obj) =>
      switch Js.Exn.message(obj) {
      | Some(msg) => Error(msg)
      | None => Error("Unknown JS error")                                                                 
      }                                                                                                   
    | _ => Error("A non JS error")                                                                        
    }->resolve                                                                                            
  })                                                                                                      
  ->finally(() => {                                                                                       
    client.Client.close                                                                                   
  })                                                                                                      
  ->ignore     

I am returning a Belt.Result.t for both my catch and previous then. So what am I doing wrong? Help will be highly appreciated.

1 Like

Always post the error of the type-checker, otherwise we can only guess what’s wrong.

I am pretty certain the find function doesn’t return result types, but rather the original Promise.t<Response.t>.

Whay are you passing down the collection to the thenResolve? You are basically trying to run Response.toArray on a Promise.t there.

I guess it should look like this:

  ->then(instance => {
    let collection = instance->ClientInstance.db(dbName)->Db.collection(collectionName)
    collection->Collection.find(query)
  })
  ->thenResolve(findResult => {
      Response.toArray(findResult)->Ok
  })
2 Likes

Sorry for not positing the error. @ryyppy I did as you specified and changed the findExample.res to:

let uri = "mongodb://localhost:27017"  
let dbName = "rescript"  
let collectionName = "documents"  
let query = {"isLearningRescript": true}  
  
open Mongo  
open Promise  
  
let client = Client.make(uri)  
client  
->Client.connect  
->then(instance => {  
  let collection = instance->ClientInstance.db(dbName)->Db.collection(collectionName)
  collection->Collection.find(query)->resolve
})                                                                                                 
->thenResolve(findResult => {  
  Response.toArray(findResult)->Ok
})                                                         
->catch(e => {                                                                                           
  switch e {                                                                                             
  | JsError(obj) =>                                                                                      
    switch Js.Exn.message(obj) {                                                                         
    | Some(msg) => Error(msg)                                                                            
    | None => Error("Unknown JS error")                                                            
    }                                                                                              
  | _ => Error("A non JS error")                                                                   
  }->resolve                                                                                       
})                                                                                                 
->finally(() => {                                                                                  
  client.Client.close                                                                              
})                                                                                                 
->ignore   

and with the Mongo.res (unchanged):

module Response = {
  type t
  @send external toArray: t => array<'a> = "toArray"
}

module Collection = {
  type t
  @send external findOne: (t, 'query) => Promise.t<Response.t> = "findOne"
  @send
  external find: (t, 'query) => Promise.t<Belt.Result.t<Response.t, Js.Nullable.t<Js.Exn.t>>> =
    "find"
  @send external insertOne: (t, 'data) => Promise.t<Response.t> = "insertOne"
}

module Db = {
  type t
  type collectionName = string
  @send external collection: (t, collectionName) => Collection.t = "collection"
}

module ClientInstance = {
  type t
  type dbName = string                                                                                    
  @send external db: (t, dbName) => Db.t = "db"                                                           
}                                                                                                         
                                                                                                          
module Client = {                                                                                         
  type t                                                                                                  
  @module("mongodb") @new external make: string => t = "MongoClient"                                      
  @send external connect: t => Promise.t<ClientInstance.t> = "connect"                                    
  @send external close: t => unit = "close"                                                               
}      

I get the following error:

rescript: [3/3] src/examples/findExample.cmj
FAILED: src/examples/findExample.cmj

  We've found a bug for you!
  /home/hacker/rescript/rescript-mongodb/src/examples/findExample.res:17:20-29

  15 │ })
  16 │ ->thenResolve(findResult => {
  17 │   Response.toArray(findResult)->Ok
  18 │ })
  19 │ ->catch(e => {

  This has type:
    Promise.t<Belt.Result.t<Mongo.Response.t, Js.Nullable.t<Js.Exn.t>>>
      (defined as
      Js_promise.t<Belt.Result.t<Mongo.Response.t, Js.Nullable.t<Js.Exn.t>>>)
  Somewhere wanted: Mongo.Response.t

FAILED: cannot make progress due to previous errors.
(base) 

Just a couple of tweaks should fix it.

The find function just returns a Promise.t<Response.t>.

@send external find: (t, 'query) => Promise.t<Response.t> = "find"

With the find() function returning a promise, there is no need to use resolve.

collection->Collection.find(query)

And a small typo when calling close.

client->Client.close

That might fix it.

1 Like