1/**
2 * @title Context.Service
3 *
4 * The default way to define a service is to extend `Context.Service`,
5 * passing in the service interface as a type parameter.
6 */
7
8// file: src/db/Database.ts
9import { Context, Effect, Layer, Schema } from "effect"
10
11// Pass in the service class name as the first type parameter, and the service
12// interface as the second type parameter.
13export class Database extends Context.Service<Database, {
14 query(sql: string): Effect.Effect<Array<unknown>, DatabaseError>
15}>()(
16 // The string identifier for the service, which should include the package
17 // name and the subdirectory path to the service file.
18 "myapp/db/Database"
19) {
20 // Attach a static layer to the service, which will be used to provide an
21 // implementation of the service.
22 static readonly layer = Layer.effect(
23 Database,
24 Effect.gen(function*() {
25 // Define the service methods using Effect.fn
26 const query = Effect.fn("Database.query")(function*(sql: string) {
27 yield* Effect.log("Executing SQL query:", sql)
28 return [{ id: 1, name: "Alice" }, { id: 2, name: "Bob" }]
29 })
30
31 // Return an instance of the service using Database.of, passing in an
32 // object that implements the service interface.
33 return Database.of({
34 query
35 })
36 })
37 )
38}
39
40export class DatabaseError extends Schema.TaggedError<DatabaseError>()("DatabaseError", {
41 cause: Schema.Defect()
42}) {}
43
44// If you ever need to access the service type, use `Database["Service"]`
45export type DatabaseService = Database["Service"]