Effect CommunityEC
Effect Community2mo ago
2 replies
ludvig

Handling Client-Server Boundaries with Effect.ts and Error Serialization

I'm trying my hands with Effect.ts again but considering everything I've written so far is normal TypeScript, I feel I have trouble with the client and server boundaries. I use TanStack Start as a full-stack framework to handle both frontend and backend. Let's say I have a route where I request some user data from a database. Four things can happen:

- User has no session
- User has session but user is deleted
- Database query failed
- Database was queried successfully

Initially I would define
export class NotAuthenticatedError extends Data.TaggedError
export class UserNotFoundError extends Data.TaggedError
export class DatabaseError extends Data.TaggedError

but I'm not sure due to the client/server communication, if I'm better off using Schema.TaggedError instead. Basically what I want from Effect is just the either, but since errors are classes, I run into serialization issues. So what I try is this
export const getScanSchedule = createServerFn({ method: "GET" }).handler(
  async () => {
    // this part feels dirty
    const program = getScanScheduleEffect.pipe(Effect.either);
    const either = await Effect.runPromise(program);
    const schedule = Either.match(either, {
      onRight: (schedule) => schedule,
      onLeft: handleError,
    });
    return schedule;
  },
);

function handleError(
  error: NotAuthenticatedError | UserNotFoundError | DatabaseError,
): never {
  switch (error._tag) {
    case "DatabaseError":
      throw Schema.encodeSync(DatabaseError)(error);

    case "NotAuthenticatedError":
    case "UserNotFoundError":
      throw redirect({ to: "/auth" });
  }
}

I either get the query from database or I throw relevant redirect/error depending on tag. In the loader then I have happy path only
  loader: async () => {
    const [schedule, scans, globalConditionsData] = await Promise.all([
      getScanSchedule(), ..])

Apologize it's a vague question, it's because I have trouble understanding the pattern
Was this page helpful?