Skip to content

Instantly share code, notes, and snippets.

@rhyskeepence
Last active January 3, 2018 21:12
Show Gist options
  • Save rhyskeepence/4500e0a58a06b6735cfbba58d021e272 to your computer and use it in GitHub Desktop.
Save rhyskeepence/4500e0a58a06b6735cfbba58d021e272 to your computer and use it in GitHub Desktop.
doobie connections and stuff
If so, can you give me some advice on how to do standard http transaction boundary stuff. That is

* unpack request
* get stuff from db
* update db based on request
* return request

all this in a single transaction (succeed/fail).

it's the interleaving of ConnectionIO and IO that I'm struggling with. And I can't seem to find any good examples.

So the way we do this at our current project is simply by having a 1:1 mapping between endpoints and ConnectionIO's that perform the database operations necessary for that particular endpoint. Our endpoint might be something like:

  case req @ POST -> Root / "things" =>
    for {
      thing <- req.decodeJson[Thing]
      id <- things.createThing(thing)
      response <- Ok(id)
    } yield response

where things might be:

class Things(transactor: Transactor[IO]) {
  def createThing(thing: Thing): IO[ThingId] = {
    val dbOp = for {
      id <- sql"select nextval from ...".query[ThingId].unique
      _  <- sql"insert into things values ...".update.run
    } yield id
    
    dbOp.transact(transactor)
  }
}

(note: contrived example - in our particular app, we now extract SQL (values of type doobie.Query0) into a queries object, separate from where we compose them together)

With this approach, createThing runs composed database operations, which when executed up the stack, run in a single transaction.

Of course, that doesn't help when you want to do stuff between database operations. In our app, we typically only do pureish things within database transactions. So we do occasionally use connection.delay(...) source which essentially lifts a lazy value into a ConnectionIO

I haven't tried this, but to intersperse pure IO operations within a database transaction, then you might be able to use liftIO - I don't know there is an instance defined for ConnectionIO, or if the type even exists in the scala ecosystem (i'm assuming it does, because it's super handy for this type of thing in haskell).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment