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 5 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 Jan 31, 2025 4:29pm
website-proxy ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jan 31, 2025 4:29pm

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.

```ts twoslash {2-5}
import { Effect } from "effect"
// ---cut---
const promise = Effect.promise(async () => "Hello, world!")
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 renaming this from promise to something else to avoid a user interpreting the variable as a Promise instead of an Effect.

Comment on lines +46 to +53
NodeRuntime.runMain(
Layer.launch(
Layer.provide(
HttpServer.serve(router),
NodeHttpServer.layer(() => createServer(app), { port: 3000 }),
),
),
)
Copy link
Member

Choose a reason for hiding this comment

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

Since you've already taught folks about .pipe, you could re-write this to make it flow sequentially:

HttpServer.server(router).pipe(
  Layer.provide(NodeHttpServer.layer(() => createServer(app), { port: 3000 })),
  Layer.launch,
  NodeRuntime.runMain
)

$ bun index.ts
```

And in other shell we can test the endpoint:
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
And in other shell we can test the endpoint:
And in another shell we can test the endpoint:

Comment on lines +179 to +195
const users = Effect.sync(() => {
return { users: db.prepare("SELECT * FROM users").all() }
}).pipe(
(body) => HttpServerResponse.json(body)
)

const router = HttpRouter.empty.pipe(
HttpRouter.get("/health", health),
HttpRouter.get("/users", users),
)
```

This no longer gives us a type error. We've split our computation into two
parts: fetching the data with `Effect.sync`, and serializing it with
`HttpServerResponse.json`. The call to `pipe` takes the result of the first
Effect and passes it to `HttpServerResponse.json`, flattening any nesting in
the process.
Copy link
Member

Choose a reason for hiding this comment

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

This is not correct - body in this case has the type Effect<{ users: Array<string> }>, which we're now passing to HttpServerResponse.json. Because HttpServerResponse.json takes data of type unknown, you're going to attempt to serialize an Effect instead of the body.

The reason you don't get a type error here is because the input to HttpServerResponse.json is unknown, which will allow any value.

At this point, you may choose to introduce the concept of sequencing Effects.

const users = Effect.sync(() => {
  return { users: db.prepare("SELECT * FROM users").all() }
}).pipe(
  // Similar to `Effect.map`, `Effect.flatMap` receives the value of the previous effect 
  // but instead of returning a new _value_, you return a new _Effect_
  Effect.flatMap((body) => HttpServerResponse.json(body))
  // Or alternatively `Effect.andThen((body) => ...)` if you prefer that naming
)
// `users` is type `Effect<HttpServerResponse, HttpBodyError>`

or you can introduce Effect.suspend, which is the same as Effect.sync, except instead of taking a side effect that returns a value, it takes a side effect that returns an Effect:

const users = Effect.suspend(() => {
  // Side effect
  const body = { users: db.prepare("SELECT * FROM users").all() }
  // return an effect
  return HttpServerResponse.json(body)
})
// `users` is type `Effect<HttpServerResponse, HttpBodyError>`

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.

2 participants