diff --git a/PLAN.md b/PLAN.md deleted file mode 100644 index cde0c2d..0000000 --- a/PLAN.md +++ /dev/null @@ -1,144 +0,0 @@ -# Plan: Todo HTTP API with Effect and Bun - -## Goal - -Build a small HTTP JSON API for todos using Effect and the Bun platform. The service will keep todos in an in-memory database seeded with mock data and expose endpoints to list, get, add, update, and delete todos. - -## Current starting point - -- Runtime: Bun -- Main entrypoint: `index.ts` -- Existing dependencies include: - - `effect` - - `@effect/platform-bun` - - `@effect/experimental` - -## Todo model and schemas - -Define the domain model with Effect Schema and infer TypeScript types from those schemas rather than hand-writing separate types. - -Planned schemas: - -```ts -import { Schema } from "effect" - -const NonEmptyString = Schema.NonEmptyString.pipe(Schema.trimmed()) - -const Todo = Schema.Struct({ - id: Schema.String, - title: NonEmptyString, - completed: Schema.Boolean, - createdAt: Schema.String, - updatedAt: Schema.String, -}) - -type Todo = typeof Todo.Type - -const CreateTodoRequest = Schema.Struct({ - title: NonEmptyString, - completed: Schema.optional(Schema.Boolean), -}) - -type CreateTodoRequest = typeof CreateTodoRequest.Type - -const UpdateTodoRequest = Schema.Struct({ - title: Schema.optional(NonEmptyString), - completed: Schema.optional(Schema.Boolean), -}).pipe(/* refine to require at least one field */) - -type UpdateTodoRequest = typeof UpdateTodoRequest.Type -``` - -Use these schemas for both HTTP request decoding and repository method input types. - -## In-memory database - -Create an Effect service for todo storage, backed by `Ref>` so state changes stay inside Effect. - -Responsibilities: - -- Seed the database with several mock todos on startup. -- Provide methods: - - `list(): Effect>` - - `get(id: string): Effect` - - `create(input): Effect` - - `update(id: string, input): Effect` - - `delete(id: string): Effect` -- Keep all storage concerns isolated from the HTTP layer. - -## HTTP API - -Use Effect's HTTP APIs on the Bun platform. - -Add HTTP request/response logging middleware around the routes. It should log method, path, status, duration, and any failure cause for every request. - -Planned routes: - -| Method | Path | Description | -| --- | --- | --- | -| `GET` | `/todos` | List all todos | -| `GET` | `/todos/:id` | Get one todo by id | -| `POST` | `/todos` | Add a todo | -| `PATCH` | `/todos/:id` | Update a todo | -| `DELETE` | `/todos/:id` | Delete a todo | - -Responses: - -- Return JSON for all success and error responses. -- Use `200 OK` for list/get/update. -- Use `201 Created` for create. -- Use `204 No Content` or JSON success for delete. -- Use `400 Bad Request` for invalid input. -- Use `404 Not Found` for missing todos. - -## Validation and errors - -Define all request validation in Effect Schema: - -- `CreateTodoRequest` requires a non-empty `title` and accepts optional boolean `completed`. -- `UpdateTodoRequest` accepts optional non-empty `title` and optional boolean `completed`. -- Add a schema refinement/filter so update requests include at least one supported field. -- Decode JSON request bodies with the schemas at the HTTP boundary. -- Convert schema parse failures into `400 Bad Request` JSON responses. - -Model domain errors explicitly, for example: - -- `TodoNotFound` -- `InvalidTodoInput` - -Translate domain errors into HTTP responses at the route boundary. - -## Implementation steps - -1. Replace the current demo loop in `index.ts` with an Effect-powered HTTP server using `@effect/platform-bun`. -2. Add the todo domain types and error types. -3. Implement the in-memory todo repository/service with seeded mock todos. -4. Define HTTP routes for list, get, create, update, and delete. -5. Parse and validate JSON request bodies. -6. Map service results and failures to JSON HTTP responses. -7. Add HTTP request/response logging middleware so each request logs at least method, path, status, duration, and failures. -8. Add spans or structured logs around repository operations where useful. -9. Start the server with `BunRuntime.runMain`. -10. Verify locally with `bun run dev` or `bun run index.ts`. - -## Manual verification - -Example checks after implementation: - -```bash -curl http://localhost:3000/todos -curl http://localhost:3000/todos/1 -curl -X POST http://localhost:3000/todos \ - -H 'content-type: application/json' \ - -d '{"title":"Write Effect API"}' -curl -X PATCH http://localhost:3000/todos/1 \ - -H 'content-type: application/json' \ - -d '{"completed":true}' -curl -X DELETE http://localhost:3000/todos/1 -``` - -## Optional follow-ups - -- Add tests with `bun test`. -- Add response schemas for success and error payloads. -- Add pagination or filtering to `GET /todos`.