basic.ts 3.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. import { SqliteClient } from "@effect/sql-sqlite-bun"
  2. import { eq } from "drizzle-orm"
  3. import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core"
  4. import * as Context from "effect/Context"
  5. import * as Effect from "effect/Effect"
  6. import * as Layer from "effect/Layer"
  7. import * as Schema from "effect/Schema"
  8. import { EffectDrizzleSqlite } from "../src"
  9. const users = sqliteTable("users", {
  10. id: integer().primaryKey({ autoIncrement: true }),
  11. name: text().notNull(),
  12. })
  13. type User = typeof users.$inferSelect
  14. const makeDatabase = EffectDrizzleSqlite.makeWithDefaults()
  15. type DatabaseShape = Effect.Success<typeof makeDatabase>
  16. const sqliteLayer = SqliteClient.layer({ filename: ":memory:", disableWAL: true })
  17. class Database extends Context.Service<Database, DatabaseShape>()("@opencode/example/Database") {
  18. static layer = Layer.effect(Database, makeDatabase).pipe(Layer.provide(sqliteLayer))
  19. }
  20. class UserStoreError extends Schema.TaggedErrorClass<UserStoreError>()("UserStoreError", {
  21. message: Schema.String,
  22. cause: Schema.optional(Schema.Defect()),
  23. }) {}
  24. const mapStoreError = (message: string) => (cause: unknown) => new UserStoreError({ message, cause })
  25. interface UserStoreShape {
  26. migrate(): Effect.Effect<void, UserStoreError>
  27. create(name: string): Effect.Effect<void, UserStoreError>
  28. rename(from: string, to: string): Effect.Effect<void, UserStoreError>
  29. list(): Effect.Effect<User[], UserStoreError>
  30. }
  31. class UserStore extends Context.Service<UserStore, UserStoreShape>()("@opencode/example/UserStore") {
  32. static layer = Layer.effect(
  33. UserStore,
  34. Effect.gen(function* () {
  35. const db = yield* Database
  36. return UserStore.of({
  37. migrate: Effect.fn("UserStore.migrate")(function* () {
  38. yield* EffectDrizzleSqlite.migrate(db, { migrationsFolder: `${import.meta.dirname}/migrations` }).pipe(
  39. Effect.mapError((cause) => new UserStoreError({ message: "Failed to migrate users", cause })),
  40. )
  41. }),
  42. create: Effect.fn("UserStore.create")(function* (name: string) {
  43. yield* db
  44. .insert(users)
  45. .values({ name })
  46. .pipe(Effect.asVoid, Effect.mapError(mapStoreError("Failed to create user")))
  47. }),
  48. rename: Effect.fn("UserStore.rename")(function* (from: string, to: string) {
  49. yield* db
  50. .transaction(
  51. Effect.fnUntraced(function* (tx) {
  52. yield* tx.insert(users).values({ name: from })
  53. yield* tx.update(users).set({ name: to }).where(eq(users.name, from))
  54. }),
  55. { behavior: "immediate" },
  56. )
  57. .pipe(Effect.asVoid, Effect.mapError(mapStoreError("Failed to rename user")))
  58. }),
  59. list: Effect.fn("UserStore.list")(function* () {
  60. return yield* db
  61. .select()
  62. .from(users)
  63. .pipe(Effect.mapError(mapStoreError("Failed to list users")))
  64. }),
  65. })
  66. }),
  67. ).pipe(Layer.provide(Database.layer))
  68. }
  69. const program = Effect.gen(function* () {
  70. const userStore = yield* UserStore
  71. yield* userStore.migrate()
  72. yield* userStore.create("Ada")
  73. yield* userStore.rename("Grace", "Grace Hopper")
  74. return yield* userStore.list()
  75. })
  76. const rows = await Effect.runPromise(program.pipe(Effect.provide(UserStore.layer)))
  77. console.log(rows)