{"id":22474695,"url":"https://github.com/herbsjs/herbs2knex","last_synced_at":"2025-08-02T11:32:13.094Z","repository":{"id":37078179,"uuid":"278524834","full_name":"herbsjs/herbs2knex","owner":"herbsjs","description":"Create repositories using Knex to retrieve and store Entities","archived":false,"fork":false,"pushed_at":"2023-07-13T11:10:25.000Z","size":2208,"stargazers_count":7,"open_issues_count":5,"forks_count":13,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-07-30T01:17:16.582Z","etag":null,"topics":["database","hacktoberfest","herbsjs","knexjs","orm","query-builder"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/herbsjs.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":".github/CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-07-10T03:03:00.000Z","updated_at":"2024-04-18T15:11:30.000Z","dependencies_parsed_at":"2024-06-19T04:08:51.563Z","dependency_job_id":"214bfced-3864-46aa-a04e-29b94f5b6bf5","html_url":"https://github.com/herbsjs/herbs2knex","commit_stats":{"total_commits":158,"total_committers":13,"mean_commits":"12.153846153846153","dds":0.6835443037974683,"last_synced_commit":"b6d24e29e90229d4d3c071675f0e6842afe9463b"},"previous_names":[],"tags_count":32,"template":false,"template_full_name":null,"purl":"pkg:github/herbsjs/herbs2knex","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/herbsjs%2Fherbs2knex","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/herbsjs%2Fherbs2knex/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/herbsjs%2Fherbs2knex/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/herbsjs%2Fherbs2knex/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/herbsjs","download_url":"https://codeload.github.com/herbsjs/herbs2knex/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/herbsjs%2Fherbs2knex/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":268378965,"owners_count":24240907,"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","status":"online","status_checked_at":"2025-08-02T02:00:12.353Z","response_time":74,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["database","hacktoberfest","herbsjs","knexjs","orm","query-builder"],"created_at":"2024-12-06T13:09:56.603Z","updated_at":"2025-08-02T11:32:12.763Z","avatar_url":"https://github.com/herbsjs.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![CI build](https://github.com/herbsjs/herbs2knex/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/herbsjs/herbs2knex/actions/workflows/ci.yml)\n[![codecov](https://codecov.io/gh/herbsjs/herbs2knex/branch/master/graph/badge.svg)](https://codecov.io/gh/herbsjs/herbs2knex)\n\n# herbs2knex\n\nherbs2knex creates repositories to retrieve and store [Entities](https://github.com/herbsjs/gotu) using [Knex](http://knexjs.org).\n\n### Installing\n```\n    $ npm install @herbsjs/herbs2knex\n```\n\n### Using\n\n`connection.js` - Knex initialization:\n```javascript\nconst knex = require('knex')\nconst config = require('./config')\nmodule.exports = knex(config)\n```\n\n`itemRepository.js`:\n```javascript\nconst { Repository } = require('@herbsjs/herbs2knex')\nconst connection = require('connection')\nconst { Item } = require('../domain/entities/item')\n\nclass ItemRepository extends Repository {\n    constructor() {\n        super({\n            entity: Item,\n            table: 'aTable',\n            ids: ['id'],\n            knex: connection\n        })\n    }\n\n    excludedItemFromLastWeek() {\n        ...\n    }\n}\n```\n\n`someUsecase.js`:\n```javascript\nconst repo = new ItemRepository()\nconst ret = await repo.findByID(1)\n```\n\n### What is a Repository?\n\nA repository, by [definition](https://en.wikipedia.org/wiki/Domain-driven_design#Building_blocks), is part of the layer to retrieve and store entities abstracting the underlying implementation. By using repositories, details of these implementation such as relational database, document-oriented databases, etc, should not leak to the domain code. In other words, no raw SQL queries on your use case or entity files.\n\n### Herbs2knex Repository\n\nIn order to boost productivity Herbs2knex provides way to dynamically generate a repository class based on your Entities and other metadata. \n\nThese metadata are necessary to close the gap between OOP concepts and paradigms and those of relational databases. For example, it is necessary to specify primary keys and foreign keys as these information do not exist in the description of your domain.\n\nFollowing Herbs architecture principals it is not the intention of this lib to create yet another ORM or query builder but to create a bridge between your domain and an existing one (Knex).\n\n### Why Knex?\n\nHerbs2knex is just one of many bridges possible between Herbs and other packages.\n\nThe advantage of using Knex is that is a simple and flexible SQL query builder. It also supports Postgres, MSSQL, MySQL, MariaDB, SQLite3, Oracle, and Amazon Redshift. So you can build your system using these databases out of the box.\n\n### Repository setup\n\n```javascript\nconst { Repository } = require('@herbsjs/herbs2knex')\nconst connection = require('connection')  // Knex initialize instance\nconst { ProductItem } = require('../domain/entities/productItem')\n\nclass YourRepository extends Repository {\n    constructor() {\n        super({\n            entity: ProductItem,\n            schema: 'main',\n            table: 'product_items',\n            ids: ['id'],\n            foreignKeys: [{ customerId: String }],\n            knex: connection\n        })\n    }\n}\n```\n\n- `entity` - The [Entity](https://github.com/herbsjs/gotu) to be used as reference \n\n    ```javascript\n    entity: ProductItem\n    ```\n\n- `schema` - The schema to be used  \n\n    ```javascript\n    schema: 'production'\n    ```\n\n- `table` - The name of the table in database\n\n    ```javascript\n    table: 'product_items'\n    ```\n\n- `ids` - Primary keys\n\n    Format: `['fieldName', 'fieldName', ...]`\n\n    There must be corresponding fields in the entity.\n\n    ```javascript\n    ids: ['id']  // productItem.id\n    ```\n\n    or for composite primary key: \n\n    ```javascript\n    ids: [`customerId`, `productId`]  // productItem.customerId , productItem.productId\n    ```\n\n- `foreignKeys` (optional) - Foreign keys for the database table\n\n    Usually there is no corresponding fields declared in the entity for foreign keys. That is the reason it is necessary to inform the name and the type of the fields.\n\n    Format: `[{ fieldName: Type }, { fieldName: Type }, ...]`\n\n    ```javascript\n    foreignKeys: [{ customerId: String }]\n    ```\n\n    The field names will converted to a database names using conventions. Ex: `customer_id`\n\n- `knex` - Knex initialize instance\n    \n    Check Knex [documentation](http://knexjs.org/#Installation-client)\n\n## Retrieving and Persisting Data\n\n### `find`\nFind entities matched by the filter, or empty array `[]` if there is no matching entity.\n\nFormat: `.find(options)` where `options` is a optional object containing `{ limit, offset, orderBy, where }`\n\nReturn: Entity array\n\n```javascript\nconst repo = new ItemRepository(injection)\nconst ret = await repo.find()\n```\n\nOptions:\n\n- `limit`\nAdds a limit clause to the query.\n\n```javascript\nconst repo = new ItemRepository(injection)\nconst ret = await repo.find({ limit: 10 })\n```\n\n- `offset`\nAdds an offset clause to the query.\n\n```javascript\nconst repo = new ItemRepository(injection)\nconst ret = await repo.find({ offset: 5 })\n```\n\n- `orderBy`\nAdds an order by clause to the query. Column can be string, or list mixed with string and object.\n\n```javascript\n// order by collum\nconst repo = new ItemRepository(injection)\nconst ret = await repo.find({ orderBy: 'description'})\n\n// order by complex query\nconst repo = new ItemRepository(injection)\nconst ret = await repo.find({ orderBy: [{ column: 'nome', order: 'desc' }, 'email'] })\n```\n\n- `where`\nAdds a filter to the query with given values.\n\n```javascript\nconst repo = new ItemRepository(injection)\nconst ret = await repo.find({ where: { name: [\"Anne\"] } })\n```\n\n### `findByID`\nFind entities by IDs\n\nFormat: `.findByID(id)` where `id` is a value or an array.\n\nReturn: Entity array\n\n```javascript\nconst repo = new ItemRepository(injection)\nconst ret = await repo.findByID(10)\n```\n\n### `first`\nFinds the first entity matched by the filter, or empty array `[]` if there is no matching entity.\n\nFormat: `.first(options)` where `options` is a optional object containing `{ orderBy, where }`\n\nReturn: Entity\n\n```javascript\nconst repo = new ItemRepository(injection)\nconst ret = await repo.first({ orderBy: 'description'})\n```\n\n### `insert`\n\nInsert an Entity into a table.\n\nFormat: `.insert(entity)` where `entity` is a Entity instance with values to be persisted.\n\nReturn: The inserted entity with the values from database.\n\n```javascript\nconst repo = new ItemRepository(injection)\nconst ret = await repo.insert(aNewEntity)\n```\n\n### `update`\n\nUpdate an Entity.\n\nFormat: `.update(entity)` where `entity` is a Entity instance with values to be persisted.\n\nReturn: The updated entity with the values from database.\n\n```javascript\nconst repo = new ItemRepository(injection)\nconst ret = await repo.update(aModifiedEntity)\n```\n\n### `delete`\n\nDelete an Entity.\n\nFormat: `.delete(entity)` where `entity` is a Entity instance to be deleted.\n\nReturn: `true` for success or `false` for error\n\n```javascript\nconst repo = new ItemRepository(injection)\nconst ret = await repo.delete(entity)\n```\n\n## Conventions \n\n### Default implementation\n\n#### Fields\n\nCode: Camel Case - ex: `productName`\n\nDatabase: Snake Case - ex: `product_name`\n\n### Custom Convention\n\nYou can use the custom convention to configure the way herbs2knex creates your queries to read fields from your database. When using this option, the `ids` property must respect the format convention.\n\n```javascript\nconst toCamelCase = value =\u003e camelCase(value)\n\nconst userRepository = new UserRepository({\n    entity: User,\n    table,\n    schema,\n    ids: ['id'],\n    knex: connection,\n    convention: {\n        toTableFieldName: field =\u003e toCamelCase(field)\n    }\n})\n```\n\n### Object-Oriented versus Relational models - Relationships\n\nAn entity can define a reference for others entities but will not (and should not) define a foreign key. For instance:\n\n    +------------------+         +------------------+         +------------------+\n    |      Orders      |         |    OrderItems    |         |     Products     |\n    +------------------+         +------------------+         +------------------+\n    | id: int          |----\\    | id: int          |       --| id: int          |\n    | customer_id: int |     ----| order_id: int    |  ----/  | name: string     |\n    +------------------+         | product_id: int  |-/       +------------------+\n                                +------------------+                             \n\n```javascript\nconst Product = entity('Product', {\n    id: field(Number),\n    name: field(String),\n    ...\n})\n\nconst OrderItem = entity('Order Items', {\n    id: field(Number),\n    product: field(Product),    // optional\n    ...\n})\n\nconst Order = entity('Order', {\n    id: field(Number),\n    item: field([OrderItem]),     // optional\n    ...\n})\n```\n\nMore about: https://en.wikipedia.org/wiki/Object%E2%80%93relational_impedance_mismatch\n\n## TODO\n\n- [ ] Allow only scalar types for queries (don't allow entity / object types)\n- [ ] Allow to ommit schema's name\n\nFeatures:\n- [X] Be able to change the conventions (injection)\n- [ ] Exclude / ignore fields on a sql statement\n- [ ] Awareness of created/updated at/by fields\n- [X] Plug-and-play knex\n- [ ] Easy access knex structure\n\nRetrieving and Persist:\n- [X] insert\n    - [ ] batchs\n- [X] update\n    - [ ] batchs\n- [X] delete\n- [ ] persist (upsert)\n- [X] find (ID)\n    - [ ] deal with entities / tables with multiples IDs\n- [X] find by (any field)\n    - [ ] deal with entities / tables with multiples IDs\n    - [X] order by\n- [X] find All\n    - [X] order by\n- [X] find with pages\n- [X] first\n\nTests:\n- [X] Pure JS\n- [X] Postgress\n- [X] Sql Server\n- [X] MySQL\n- [X] Sqlite\n\n### Contribute\n\nCome with us to make an awesome *herbs2knex*.\n\nNow, if you do not have technical knowledge and also have intend to help us, do not feel shy, [click here](https://github.com/herbsjs/herbs2knex/issues) to open an issue and collaborate their ideas, the contribution may be a criticism or a compliment (why not?)\n\nIf you would like to help contribute to this repository, please see [CONTRIBUTING](https://github.com/herbsjs/herbs2knex/blob/master/.github/CONTRIBUTING.md)\n\n### License\n\n**herbs2knex** is released under the\n[MIT license](https://github.com/herbsjs/herbs2knex/blob/master/LICENSE.md).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fherbsjs%2Fherbs2knex","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fherbsjs%2Fherbs2knex","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fherbsjs%2Fherbs2knex/lists"}