Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrating an endpoint that uses a database. #1046

Draft
wants to merge 18 commits into
base: feat/add-tutorials
Choose a base branch
from

Conversation

samwho
Copy link

@samwho samwho commented Jan 31, 2025

Type

  • Refactor
  • Feature
  • Bug Fix
  • Optimization
  • Documentation Update

Description

Currently still a work in progress.

Copy link

vercel bot commented Jan 31, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
website-landing ✅ Ready (Inspect) Visit Preview 💬 Add feedback Feb 21, 2025 5:06pm
website-proxy ✅ Ready (Inspect) Visit Preview 💬 Add feedback Feb 21, 2025 5:06pm

Copy link

vercel bot commented Jan 31, 2025

@samwho is attempting to deploy a commit to the Effect Team on Vercel.

A member of the Team first needs to authorize it.

Copy link
Member

@IMax153 IMax153 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a few minor comments for today! Great work @samwho - it's coming along very nicely!

Comment on lines +412 to +426
With this information we can ensure, and get TypeScript to verify for us,
that things do not go wrong in unexpected ways.

```ts twoslash
// @errors: 2375
import { Effect } from "effect"

const effect = Effect.succeed(1).pipe(
Effect.tryMap({
try: (n) => n + 1,
catch: () => new Error(),
}),
)

const noError: Effect.Effect<number, never, never> = effect
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This transition felt a bit abrupt to me. To me, it might feel more natural if you insert one of the "aside"-esque comments you've had previously (recall the pipe aside). Perhaps "An aside on errors in Effect" or something?

Also, before now Effect.tryMap has not even been mentioned but now we're using it all of a sudden.

Might be worth using the user fetch code from above for these samples instead of the random tryMap code, given that you also insert it into the final snippet later anyways.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did wonder about whether to explicitly call out Effect.tryMap and landed on not doing so. It felt like because we've already covered Effect.map, and we looked at Effect.try in the previous section, the reader wouldn't flinch at Effect.tryMap being casually used without explanation.

A small nod to it might be the way to go, though. I need to start being careful, I've not spent more time with Effect than the reader and am likely prone to thinking they'll understand things they might not. 😅

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My take reading through this is that this section would honestly be more clear to me if you just stick with the chunk of code from before:

Effect.tryPromise({
  try: () => db.query("SELECT * FROM users"),
  catch: (cause) => new UserFetchError("failed to fetch users", { cause }),
})

Comment on lines +465 to +467
This effectively recreates the behaviour of throwing an exception. Defects are
critical errors that you don't want to handle. If you were to create a defect in
an Effect handler, the result would be a 500 error returned to the user.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be worth one more sentence or something here providing a clear distinction between "expected" errors, which are part of the program's domain, and "defects" which are critical errors (as you explained).

Comment on lines +387 to +389
//---cut---
class UserFetchError extends Error {}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For a variety of reasons, I would advise against this pattern.

For one, the lack of a discriminator in the type (e.g. a _tag) means that another error which extends Error can be directly assignable to the UserFetchError: https://effect.website/play#55cd56ec464d.

My recommendation would be to either:

  1. Take the time to introduce creating your own domain-specific errors more properly here via Data.TaggedError https://effect.website/docs/data-types/data/#taggederror
  2. Just make the error an interface in this case for now if you want to keep it simple
interface UserFetchError {
  readonly _tag: "UserFetchError"
  readonly message: string
  readonly cause: unknown
}

I personally think it might be good to go for the first option given that Data.TaggedError will automatically attach a _tag to our error type and also extends Error for us by default, but I leave that decision to you.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agonised over this, because tags do feel like they deserve special attention and I'll soon get to them when I talk about services. I wanted to wait for a place I needed to use them in error handling to introduce them. I also don't want to wander too far away from the purpose of this section: to migrate an endpoint that calls an external API.

This section in its entirety is still very early, probably the earliest I've pushed a section up for review. I'm not tightly committed to any part of it. If you think we really should introduce tags here, I don't think it would be impossible to do so. I do think we'll have to cut something else to make room for them, though, and it might have to be services.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Totally understand, and honestly I don't want to impose my opinion too much on the direction you take here.

My only advice would be that if you want to avoid introducing Data.TaggedError, etc., maybe just add a _tag property or something to your custom error type and do another one of those "this is important but we'll discuss it later" things. Just to ensure you have a discriminator for the type in place, and you don't create an unintentional footgun with multiple error types that are assignable to one another.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants