{"id":20611459,"url":"https://github.com/theunderscorer/functional-cqrs","last_synced_at":"2025-07-01T14:31:44.951Z","repository":{"id":49553328,"uuid":"237652209","full_name":"TheUnderScorer/functional-cqrs","owner":"TheUnderScorer","description":"CQRS concept in JavaScript/Typescript","archived":false,"fork":false,"pushed_at":"2022-12-14T06:00:45.000Z","size":44077,"stargazers_count":5,"open_issues_count":2,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-06-19T14:55:09.991Z","etag":null,"topics":["command-handlers","events-bus","functional-cqrs","queries-bus"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/TheUnderScorer.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-02-01T17:37:32.000Z","updated_at":"2022-10-21T18:25:47.000Z","dependencies_parsed_at":"2023-01-28T17:46:04.204Z","dependency_job_id":null,"html_url":"https://github.com/TheUnderScorer/functional-cqrs","commit_stats":null,"previous_names":[],"tags_count":34,"template":false,"template_full_name":null,"purl":"pkg:github/TheUnderScorer/functional-cqrs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TheUnderScorer%2Ffunctional-cqrs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TheUnderScorer%2Ffunctional-cqrs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TheUnderScorer%2Ffunctional-cqrs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TheUnderScorer%2Ffunctional-cqrs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/TheUnderScorer","download_url":"https://codeload.github.com/TheUnderScorer/functional-cqrs/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TheUnderScorer%2Ffunctional-cqrs/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":262981059,"owners_count":23394458,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["command-handlers","events-bus","functional-cqrs","queries-bus"],"created_at":"2024-11-16T10:20:28.303Z","updated_at":"2025-07-01T14:31:44.924Z","avatar_url":"https://github.com/TheUnderScorer.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Functional Cqrs\n\nThis library provides functional approach to CQRS (Command Query Responsibility Segregation).\n\nIt includes all basic components - Commands Bus, Queries Bus and Events Bus with advanced typescript support.\n\n![CI](https://github.com/TheUnderScorer/functional-cqrs/workflows/CI/badge.svg)\n[![Coverage Status](https://coveralls.io/repos/github/TheUnderScorer/functional-cqrs/badge.svg?branch=master)](https://coveralls.io/github/TheUnderScorer/functional-cqrs?branch=master)\n\n## ✨ Getting Started\n\n### Prerequsites\n\nYou will need:\n\n- [nodejs](https://nodejs.org/en/) (\u003e=12.13.1, not tested on older versions, but might work). It should also work in browsers (not tested).\n\n### Installation\n\n`npm install functional-cqrs` or `yarn add functional-cqrs`\n\n## 🚀 Usage\n\nLet's say that we have a simple to-do app:\n\n```ts\nimport {\n  Command,\n  CommandContext,\n  createCqrs,\n  Event,\n  EventContext,\n} from 'functional-cqrs';\nimport { connectToDatabse } from 'your-database-library';\n\ninterface Task {\n  id: string;\n  title: string;\n  done: boolean;\n}\n\n// Command name - required for type inference\nconst AddTask = 'AddTask' as const;\n\n// Your command for adding new Task\nclass AddTaskCommand implements Command\u003cTask\u003e {\n  readonly name = AddTask;\n\n  constructor(readonly payload: Task) {}\n}\n\n// Event emitted after Task have been created\nclass TaskAddedEvent implements Event\u003cTask\u003e {\n  constructor(readonly payload: Task) {}\n}\n\n// Your handler for adding new Task\nconst addTaskHandler = (connection: Connection) =\u003e async (\n  command: AddTaskCommand,\n  // CommandContext includes EventsBus for dispatching events\n  ctx: CommandContext\n) =\u003e {\n  await connection.saveTask(command.payload);\n\n  await ctx.eventsBus.dispatch(new TaskAddedEvent(command.payload));\n\n  // Note that we return created task here\n  return command.payload;\n};\n\nconst onTaskAdded = (\n  event: TaskAddedEvent,\n  // EventContext includes CommandsBus so we can execute commands from here\n  context: EventContext\n) =\u003e {\n  // Log created Task into console\n  console.log(event.payload);\n};\n\n// Let's bootstrap our app\n\nconst connection = connectToDatabse();\nconst cqrs = createCqrs({\n  commandHandlers: {\n    // Here we create and register our command handler\n    [AddTask]: addTaskHandler(connection),\n  },\n  eventHandlers: {\n    // Events can have multiple handlers\n    [TaskAddedEvent.name]: [onTaskAdded],\n  },\n});\n\nconst task = await cqrs.buses.commandsBus.execute(\n  new AddTaskCommand({\n    id: '1',\n    done: false,\n    title: 'Test task',\n  })\n);\n\n// Typescript knows that this is a \"Task\"!\nconsole.log(task.id);\n```\nYou can very quickly integrate above example with any kind of application.\n\nNow, you might ask:\n\u003e Why do we need to set command name as \"'AddTask' as const;\" and then use it to register the command handler, but we don't do the same for events?\n\nIn order to get the type inference from Typescript, we need a way to map command to handler - and we use it's name for it.\nWe have to define it \"as const\" so Typescript knows it's exact value.\n\nWe don't do that for events, because they always return `void | Promise\u003cvoid\u003e` :)\n\n## 📖 Learn more\n\n* [📥 Commands](docs/commands.md)\n* [📤 Queries](docs/commands.md)\n* [🗓 Events](docs/commands.md)\n\nNeed more? Check [examples](examples) 😎.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftheunderscorer%2Ffunctional-cqrs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftheunderscorer%2Ffunctional-cqrs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftheunderscorer%2Ffunctional-cqrs/lists"}