How to throw an understandable error with Belt.Result.getExn?

I am trying to keep an app from launching if the dbHost isn’t supplied. The code below works, but the error is simply “Not found”. I don’t get the Error(message) I wrote.

Ideally, I just want to see the message “The environment variable for DB_HOST hasn’t been set”.

// Getting the dbHost environment variable  
let nodeEnv = Node.Process.process["env"]  
                                   
// Check if DB_HOST is available                                         
let dbHostResult = switch Js.Dict.get(nodeEnv, "DB_HOST") { 
| Some(value) => Ok(value)                                                     
| None => Error(`The environment variable for DB_HOST hasn't been set`)        
}                                                                               
let dbHost = Belt.Result.getExn(dbHostResult)                           
Js.log(dbHost)     

@el1t1st When you use Belt.Result.getExn it ignores value inside Error, because Error can store any generic type. Check the implementation below.

image

You can throw the passed error string by implementing your own getExn like below :point_down:t3:

let getExn = value =>
  switch value {
  | Ok(x) => x
  | Error(err) => err->Js.Exn.raiseError
  }

This code raises normal js error. If you are purely handling errors only in rescript code, then you can use pure rescript exceptions.

2 Likes

Now after re-reading your question, I don’t understand the problem you are trying to solve.
Do you want to throw an exception, or access the Error case of a result?

If you just want to access the Error message of a result, you should prefer a switch over Belt.Result.getExn.

If you are interested in raising an actual exception, you’d go with @praveen’s approach… but then I don’t understand why you’d want to convert the previous option value into a result value first?

2 Likes

@ryyppy What I want to do is to read the “DB_HOST” from Node.process.process[“env”] and assign it to a variable dbHost, and if it doesn’t exist throw an exception with DB_HOST not found so the error thrown in the exception is understandable.

What is the best way to do this?

You may do it like this?

exception ConfigError(string)

let readConfig = () => {
  let nodeEnv = Node.Process.process["env"]
  let dbHost = switch Js.Dict.get(nodeEnv, "DB_HOST") {
  | Some(value) => value
  | None =>
    raise(ConfigError(`The environment variable for DB_HOST hasn't been set`))
  }

  dbHost
}

try {
  let dbHost = readConfig()

  Js.log(`Got dbHost ${dbHost}`)
} catch {
| ConfigError(msg) => Js.log(msg)
}

Playground Link

3 Likes

Just a final question on this topic. By using the try-catch the definition of the variable is only valid inside the try-catch block. How do I get it out?

I’d like to connect to MongoDB using that dbHost.

There are a few possible choices,

First, try/catch can return an expression:

let result = try {
  // Try code, maybe return an Ok()
} catch {
  // Catch code, maybe return an Error()
}

Or you can just call your next function from within your try:

let main = (dbHost) => {
  // Main application code
}

try {
  let dbHost = readConfig()
  main(dbHost)
} catch {
| ConfigError(msg) => Js.log(msg)
}

Unless you specifically need to throw errors, you might like to just use Ok and Error for your logic branching.

type config = {dbHost: string}

let readConfig = () => {
  let nodeEnv = Node.Process.process["env"]
  switch Js.Dict.get(nodeEnv, "DB_HOST") {
  | Some(dbHost) => Ok({dbHost: dbHost})
  | None => Error(`The environment variable for DB_HOST hasn't been set`)
  }
}

let main = (config: config) => {
  Js.log(config)
}

let errorHandler = error => {
  Js.log(error)
}

switch readConfig() {
| Ok(config) => main(config)
| Error(reason) => errorHandler(reason)
}
3 Likes