{"_id":"@avratz/fp-toolkit-hono","_rev":"2-b92c1a9f37428e54d32ca678d6d2e108","name":"@avratz/fp-toolkit-hono","dist-tags":{"latest":"1.0.1"},"versions":{"1.0.0":{"name":"@avratz/fp-toolkit-hono","version":"1.0.0","keywords":["hono","functional-programming","fp","typescript","api","rest","toolkit","archetype","wrapper","helpers","clean-architecture","composable","light"],"license":"MIT","_id":"@avratz/fp-toolkit-hono@1.0.0","maintainers":[{"name":"avratz","email":"signos97@gmail.com"}],"dist":{"shasum":"4ea8d730805cd6b0dcd9cdc2ad8f70aac4a28d36","tarball":"https://registry.npmjs.org/@avratz/fp-toolkit-hono/-/fp-toolkit-hono-1.0.0.tgz","fileCount":32,"integrity":"sha512-JRza4hKZvHaRo7V8MQSRv5h/xGMFLF4UCgwdD5nOjUNrIIb0ihYHV4Pkj4BI+VAUEml1/1C8OHt7HiN70PM5Kw==","signatures":[{"sig":"MEUCIFzQIs590yB1w9lqdRfFn2pniLcA2sl77gB4jNy9eRCMAiEA4jdJMHLJjymEXNkUk+7IsBfIbYKNsuAQCWjnAfytfEg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":27471},"main":"dist/index.js","types":"dist/index.d.ts","exports":{".":"./dist/index.js"},"gitHead":"ba6f8615d6cf1ba654edb1860ec589978a934b4d","scripts":{"build":"tsc -p tsconfig.json","prepare":"tsc -p tsconfig.json"},"_npmUser":{"name":"avratz","email":"signos97@gmail.com"},"_npmVersion":"10.7.0","description":"Functional programming oriented wrapper for Hono, providing helpers and a clean archetype for building type-safe APIs.","directories":{},"_nodeVersion":"20.15.1","_hasShrinkwrap":false,"devDependencies":{"zod":"^3.23.0","hono":"^4.9.0","fp-ts":"^2.16.0","typescript":"^5.4.0"},"peerDependencies":{"zod":"^3.23.0","hono":"^4.9.0","fp-ts":"^2.16.0"},"_npmOperationalInternal":{"tmp":"tmp/fp-toolkit-hono_1.0.0_1755313523251_0.939727669429181","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@avratz/fp-toolkit-hono","description":"Functional programming oriented wrapper for Hono, providing helpers and a clean archetype for building type-safe APIs.","version":"1.0.1","keywords":["hono","functional-programming","fp","typescript","api","rest","toolkit","archetype","wrapper","helpers","clean-architecture","composable","light"],"main":"dist/index.js","types":"dist/index.d.ts","exports":{".":"./dist/index.js"},"license":"MIT","scripts":{"build":"tsc -p tsconfig.json","prepare":"tsc -p tsconfig.json"},"peerDependencies":{"hono":"^4.9.0","zod":"^3.23.0","fp-ts":"^2.16.0"},"devDependencies":{"typescript":"^5.4.0","hono":"^4.9.0","zod":"^3.23.0","fp-ts":"^2.16.0"},"_id":"@avratz/fp-toolkit-hono@1.0.1","gitHead":"baa0ae3cbf36ffbe8f47e8e4b128b32b082757ad","_nodeVersion":"20.15.1","_npmVersion":"10.7.0","dist":{"integrity":"sha512-74yV/7Ew5eI6vI4jR8Mfu0E3qmXz/NqNNonz9rNRscFVqtGeXcN6noUmUkLVnCJ2WDrVovQhRjjaL1x7KmolTg==","shasum":"9b9fa1d6063b548b05cc0a01ec71a76960961e60","tarball":"https://registry.npmjs.org/@avratz/fp-toolkit-hono/-/fp-toolkit-hono-1.0.1.tgz","fileCount":32,"unpackedSize":27536,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIEhGIYTe4nc4zy/WEwYxN3AGC37LmDAtTWIlHHqoGgGiAiAyynEqLrinYU0KfDLrmSi5ZaSBTzpR7SE39l+AEOYS2w=="}]},"_npmUser":{"name":"avratz","email":"signos97@gmail.com"},"directories":{},"maintainers":[{"name":"avratz","email":"signos97@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/fp-toolkit-hono_1.0.1_1755314425225_0.4369798322663525"},"_hasShrinkwrap":false}},"time":{"created":"2025-08-16T03:05:23.168Z","modified":"2025-08-16T03:20:25.564Z","1.0.0":"2025-08-16T03:05:23.444Z","1.0.1":"2025-08-16T03:20:25.400Z"},"license":"MIT","keywords":["hono","functional-programming","fp","typescript","api","rest","toolkit","archetype","wrapper","helpers","clean-architecture","composable","light"],"description":"Functional programming oriented wrapper for Hono, providing helpers and a clean archetype for building type-safe APIs.","maintainers":[{"name":"avratz","email":"signos97@gmail.com"}],"readme":"# @avratz/fp-toolkit-hono\n\nA functional-programming oriented wrapper for **Hono**, offering opinionated helpers and a clean archetype for building type-safe APIs. It promotes composability, explicit error handling with `fp-ts`, and a clean separation between routing, controllers, and services.\n\n---\n\n## Features\n\n- **Functional core, imperative shell** scaffolding\n- Minimal **Hono** wrapper with a tiny surface area\n- First-class **fp-ts** integration (`TaskEither`-driven handlers)\n- **Typed JSON validation** middleware (Zod-compatible)\n- Ergonomic helpers: `createApp`, `createRoute`, `validateJson`, `runTaskEither`, `AppError`\n- Encourages clear DI boundaries (controllers receive deps, services depend on ports)\n\n> Works great for teams that want predictable flows, strong typing, and consistent architecture across services.\n\n---\n\n## Installation\n\n```bash\nnpm install @avratz/fp-toolkit-hono hono fp-ts zod\n# or\npnpm add @avratz/fp-toolkit-hono hono fp-ts zod\n```\n\n**Peer assumptions**\n\n- Node.js 18+\n- TypeScript project\n- `fp-ts` for effects and `zod` (or compatible) for validation\n\n---\n\n## Quick start\n\n### 1) App bootstrap\n\nCreate your entrypoint and register controllers via routes:\n\n```ts\n// app.ts\nimport { createApp } from '@avratz/fp-toolkit-hono'\nimport { routes } from './routes'\n\nconst app = createApp()\nroutes.forEach(({ route, controller }) => app.route(route, controller))\nexport default app\n```\n\nA typical `routes` module exports something like:\n\n```ts\n// routes.ts\nimport { userController } from './user.controller'\nimport { makeUserRepo } from './adapters/user-repo'\n\nexport const routes = [\n\t{\n\t\troute: '/users',\n\t\tcontroller: userController({ userRepo: makeUserRepo() }),\n\t},\n] as const\n```\n\n### 2) Controller (routing + orchestration)\n\nControllers define endpoints and compose services. They remain thin and side-effect free except for wiring.\n\n```ts\n// user.controller.ts\nimport { createRoute, validateJson, runTaskEither } from '@avratz/fp-toolkit-hono'\nimport { createUserService } from './user.service'\nimport { CreateUserBody } from './domain/user.type'\nimport type { TCreateUserBody } from './domain/user.type'\nimport type { UserRepository } from './port/user-repository.port'\n\n// Add parsed body to typed env\ntype CreateUserEnv = { Variables: { body: TCreateUserBody } }\n\nexport function userController(deps: { userRepo: UserRepository }) {\n\tconst route = createRoute<CreateUserEnv>()\n\tconst service = createUserService(deps.userRepo)\n\n\troute.post('/', validateJson(CreateUserBody), (context) => {\n\t\treturn runTaskEither(service.registerUser(context.var.body.name))(context)\n\t})\n\n\troute.get('/:id', (context) => {\n\t\treturn runTaskEither(service.getUser(context.req.param('id')))(context)\n\t})\n\n\treturn route\n}\n```\n\n### 3) Service (business logic)\n\nServices expose pure, composable functions that return `TaskEither<AppError, A>`.\n\n```ts\n// user.service.ts\nimport { AppError } from '@avratz/fp-toolkit-hono'\nimport { TaskEither } from 'fp-ts/lib/TaskEither'\nimport type { UserRepository } from './port/user-repository.port'\nimport type { TUser } from './domain/user.type'\n\ninterface UserService {\n\tgetUser: (id: string) => TaskEither<AppError, TUser>\n\tregisterUser: (name: string) => TaskEither<AppError, TUser>\n}\n\nexport const createUserService = (repo: UserRepository): UserService => ({\n\tgetUser: (id: string) => repo.findById(id),\n\tregisterUser: (name: string) => repo.create(name),\n})\n```\n\n---\n\n## Concepts & Flow\n\n**Request lifecycle**\n\n1. `validateJson(schema)` parses and validates the request body (e.g., with Zod), storing the typed value in `context.var.body`.\n2. Controller calls a service method → returns `TaskEither<AppError, A>`.\n3. `runTaskEither(te)(context)` executes the effect and serializes a success or error HTTP response in a uniform format.\n\n**Why **``**?**\n\n- Encodes success/failure in the type system\n- Composable chains without `try/catch`\n- Predictable, testable logic\n\n---\n\n## API Reference (high-level)\n\n> The public API is intentionally small. Below are the core helpers.\n\n### `createApp()`\n\nCreates a Hono app pre-configured for the FP toolkit conventions.\n\n**Returns**: `Hono` instance\n\n---\n\n### `createRoute<Env = {}>()`\n\nCreates a typed route/controller instance.\n\n**Type params**\n\n- `Env`: extends Hono `Env` (use this to add `Variables` like `body`)\n\n**Returns**: `{ get, post, put, patch, delete, route, ... }` routing methods\n\n---\n\n### `validateJson(schema)`\n\nMiddleware that parses JSON and validates against a schema (Zod-compatible). On success, injects `context.var.body` with the inferred type.\n\n**Schema requirements**: an object with a `parse`/`safeParse` interface (e.g., Zod).\n\n---\n\n### `runTaskEither(te)`\n\nAdapter to execute a `TaskEither<AppError, A>` and send an HTTP response.\n\n**Usage**: `runTaskEither(service.someOp(args))(context)`\n\n**Behavior**\n\n- `Right<A>` → 2xx JSON body\n- `Left<AppError>` → mapped to structured error response (status + message)\n\n---\n\n### `AppError`\n\nDiscriminated union (or branded error) consumed by `runTaskEither` to standardize failures (e.g., `BadRequest`, `NotFound`, `Conflict`, `Internal`).\n\n> Tip: expose constructor helpers like `badRequest(msg)`, `notFound(msg)`, etc., to make error creation consistent.\n\n---\n\n## Validation example (with Zod)\n\n```ts\nimport { z } from 'zod'\n\nexport const CreateUserBody = z.object({\n\tname: z.string().min(1),\n})\nexport type TCreateUserBody = z.infer<typeof CreateUserBody>\n```\n\n```ts\nroute.post('/', validateJson(CreateUserBody), (c) =>\n\trunTaskEither(service.registerUser(c.var.body.name))(c),\n)\n```\n\n---\n\n## Suggested project layout\n\n```\nsrc/\n  app.ts\n  routes.ts\n  user/\n\n    user.controller.ts\n    user.service.ts\n    domain/\n      user.type.ts\n      user.core.ts\n    port/\n      user-repository.port.ts\n    adapters/\n      user.repository.ts\n```\n\n- **domain/**: types, schemas, core functions (pure)\n- **port/**: interfaces for external systems\n- **adapters/**: implementations (DB, HTTP clients, etc.)\n- **controllers**: wiring + http layer\n- **services**: business rules (pure/TE)\n\n---\n\n## Error handling\n\n- Define an `AppError` algebra for your app\n- Map domain errors to HTTP statuses in one place (inside `runTaskEither` or a custom encoder)\n- Prefer small, composable `TaskEither` chains over large try/catch blocks\n\n---\n\n## Testing\n\n- **Services**: test as pure functions returning `TaskEither` (use in-memory ports)\n- **Controllers**: test route handlers by invoking them with a mock `context`\n- **Validation**: test Zod schemas independently\n\n---\n\n## Versioning\n\nFollows **SemVer**. Breaking changes will bump the **major** version.\n\n---\n\n## Contributing\n\nPRs welcome! Please discuss large changes via issues first.\n\n---\n\n## License\n\nMIT © Avratz\n","readmeFilename":"readme.md"}