Apollo 0.2.0: invitations, and TIL

Apollo 0.2.0 is on Maven Central πŸŽ‰

There are two new features:

The first is invitation-gated registration Γ  la DeviseInvitable, which exists because while toying with the multi-user branch I decided that an invitation system would be the right way to limit who's allowed to register. It's now part of Apollo, not Tertulia. Flip it on and registration requires a code β€” members can mint invitations (or anyone can, if you want a waitlist), the invitee gets an invite link to their e-mail, and following it allows you to register via the existing registration form. No template changes, invite codes ride their own short-lived cookie, and the user-facing messages travel through the existing flash middleware. Codes default to expiring after a week, but this is adjustable.

The second is more humbling. I announced 0.1.0 in the http4s Discord. Justin Reardon looked at my API and gently pointed out that since every service returns EitherT[F, Throwable, _] , over IO results in two error channels (IO has its own error channel, plus Either's Left) carrying the same type (Throwable). In my ignorance of this, and because the type checker didn't suggest to me this was an issue, when I wrote the original code I ended up splitting failures between them by the happy accident of liftFing my service code into the shape of an EitherT in some places, where in other places I'd simply wrap an Either-returning block of code in an EitherT . Depending on the lift, some errors are catchable, and some sail past every case Left in the codebase and hit the server-level error handler. (liftF lifts the success path only. The one that includes errors is .attemptT. Oops.)

0.2.0 fixes this. There's now a proper AuthError enum β€” EmailTaken, ResetNotFound, and friends β€” on the typed left, and infrastructure failures stay in F's own channel. I figure, essentially, that some errors are recoverable, and some aren't: the ones that are I want to match on, so they can be handled correctly in the UI, and the unrecoverable errors; e.g. the database is unavailable; just need to log correctly. The HTTP routes also no longer import java.sql to sniff SQLStates for error handling, which should have been a smell, but I'm a n00b.

It's a breaking release β€” every service trait changed β€” but that's what 0.x is for, and better now than after anyone besides me depends on it. Thank you for your code review, Justin. I know LLMs will one day probably be able to one-shot a lot of this code, and maybe it's silly to think the Scala/Http4s community needs this module, but it's useful to me!

At any rate:

mvn"io.github.joshuakfarrar::apollo-http4s:0.2.0"
mvn"io.github.joshuakfarrar::apollo-doobie:0.2.0"

https://github.com/joshuakfarrar/apollo β€” and mill init joshuakfarrar/apollo.g8 still gets you a working login box in one sitting.

πŸŽ‰