{"id":21283232,"url":"https://github.com/inductjs/core","last_synced_at":"2025-12-30T21:05:08.858Z","repository":{"id":39702577,"uuid":"302704081","full_name":"inductjs/core","owner":"inductjs","description":"Induct core is an extensible framework built on top of ExpressJS used for quickly creating REST APIs based on an SQL or mongodb database.","archived":false,"fork":false,"pushed_at":"2023-01-11T22:29:41.000Z","size":874,"stargazers_count":1,"open_issues_count":23,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-10-23T21:46:45.363Z","etag":null,"topics":["expressjs","mongodb","nodejs","prototyping","sql","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/inductjs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-10-09T17:01:31.000Z","updated_at":"2020-10-11T21:07:20.000Z","dependencies_parsed_at":"2023-02-09T08:45:31.523Z","dependency_job_id":null,"html_url":"https://github.com/inductjs/core","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/inductjs/core","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/inductjs%2Fcore","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/inductjs%2Fcore/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/inductjs%2Fcore/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/inductjs%2Fcore/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/inductjs","download_url":"https://codeload.github.com/inductjs/core/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/inductjs%2Fcore/sbom","scorecard":{"id":487572,"data":{"date":"2025-08-11","repo":{"name":"github.com/inductjs/core","commit":"9ce60873bb8ce15c59c681be5efe65f8c504e293"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.5,"checks":[{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/ci.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:21: update your workflow using https://app.stepsecurity.io/secureworkflow/inductjs/core/ci.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:23: update your workflow using https://app.stepsecurity.io/secureworkflow/inductjs/core/ci.yml/master?enable=pin","Warn: npmCommand not pinned by hash: .github/workflows/ci.yml:27","Info:   0 out of   2 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   1 npmCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 1 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":0,"reason":"53 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-968p-4wvh-cqc8","Warn: Project is vulnerable to: GHSA-67hx-6x53-jw92","Warn: Project is vulnerable to: GHSA-93q8-gq69-wqmw","Warn: Project is vulnerable to: GHSA-qwcr-r2fm-qrc7","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg","Warn: Project is vulnerable to: GHSA-fj58-h2fr-3pp2","Warn: Project is vulnerable to: GHSA-pxg6-pf52-xh8x","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275","Warn: Project is vulnerable to: GHSA-gxpj-cx7g-858c","Warn: Project is vulnerable to: GHSA-w573-4hg7-7wgq","Warn: Project is vulnerable to: GHSA-rv95-896h-c2vc","Warn: Project is vulnerable to: GHSA-qw6h-vgh9-j6wx","Warn: Project is vulnerable to: GHSA-fjxv-7rqg-78g4","Warn: Project is vulnerable to: GHSA-ww39-953v-wcq6","Warn: Project is vulnerable to: GHSA-43f8-2h32-f4cj","Warn: Project is vulnerable to: GHSA-x55w-vjjp-222r","Warn: Project is vulnerable to: GHSA-qqgx-2p2h-9c37","Warn: Project is vulnerable to: GHSA-896r-f27r-55mw","Warn: Project is vulnerable to: GHSA-9c47-m6qq-7p4h","Warn: Project is vulnerable to: GHSA-4jv9-3563-23j3","Warn: Project is vulnerable to: GHSA-29mw-wpgm-hmr9","Warn: Project is vulnerable to: GHSA-35jh-r3h4-6jhm","Warn: Project is vulnerable to: GHSA-952p-6rrq-rcjv","Warn: Project is vulnerable to: GHSA-f8q6-p94x-37v3","Warn: Project is vulnerable to: GHSA-xvch-5gv4-984h","Warn: Project is vulnerable to: GHSA-vxvm-qww3-2fh7","Warn: Project is vulnerable to: GHSA-f825-f98c-gj3g","Warn: Project is vulnerable to: GHSA-h8hf-x3f4-xwgp","Warn: Project is vulnerable to: GHSA-9m93-w8w6-76hh","Warn: Project is vulnerable to: GHSA-m7xq-9374-9rvx","Warn: Project is vulnerable to: GHSA-vg7j-7cwx-8wgw","Warn: Project is vulnerable to: GHSA-p92x-r36w-9395","Warn: Project is vulnerable to: GHSA-45q2-34rf-mr94","Warn: Project is vulnerable to: GHSA-5fw9-fq32-wv5p","Warn: Project is vulnerable to: GHSA-hj48-42vr-x3v9","Warn: Project is vulnerable to: GHSA-9wv6-86v2-598j","Warn: Project is vulnerable to: GHSA-rhx6-c78j-4q9w","Warn: Project is vulnerable to: GHSA-hrpp-h998-j3pp","Warn: Project is vulnerable to: GHSA-p8p7-x288-28g6","Warn: Project is vulnerable to: GHSA-gcx4-mw62-g8wm","Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw","Warn: Project is vulnerable to: GHSA-m6fv-jmcg-4jfg","Warn: Project is vulnerable to: GHSA-cm22-4g7w-348p","Warn: Project is vulnerable to: GHSA-52f5-9888-hmc6","Warn: Project is vulnerable to: GHSA-jgrx-mgxx-jf9v","Warn: Project is vulnerable to: GHSA-72xf-g2v4-qvf3","Warn: Project is vulnerable to: GHSA-qgmg-gppg-76g5","Warn: Project is vulnerable to: GHSA-xx4c-jj58-r7x6","Warn: Project is vulnerable to: GHSA-j8xg-fqg3-53r7","Warn: Project is vulnerable to: GHSA-6fc8-4gx4-v693","Warn: Project is vulnerable to: GHSA-3h5v-q93c-6h6q","Warn: Project is vulnerable to: GHSA-c4w7-xm78-47vh"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-19T18:11:40.443Z","repository_id":39702577,"created_at":"2025-08-19T18:11:40.444Z","updated_at":"2025-08-19T18:11:40.444Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28132549,"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-12-30T02:00:05.476Z","response_time":64,"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":["expressjs","mongodb","nodejs","prototyping","sql","typescript"],"created_at":"2024-11-21T11:07:32.094Z","updated_at":"2025-12-30T21:05:08.842Z","avatar_url":"https://github.com/inductjs.png","language":"TypeScript","readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://imgur.com/JRp3or5.png\"\u003e\n   \u003c/br\u003e\n   \u003c/br\u003e\n  \u003cimg src=\"https://github.com/inductjs/core/workflows/CI/badge.svg?branch=master\"\u003e\n\u003c/p\u003e\n\nInduct is an extensible framework built on top of ExpressJS used for quickly creating REST APIs based on an SQL or mongodb database. Usage with typescript is fully supported and encouraged!\n\nInduct uses [Knex](https://knexjs.org/) to query SQL databases, and therefore will only support databases supported by Knex. Currently Induct supports the following SQL databases:\n\n-   Microsoft SQL Server\n-   MySQL\n\nOther Knex compatible databases (such as Postgres \u0026 Oracle)  will be supported in the future.\n\n## Getting Started\n\nInstall Induct Core as a dependency of your project:\n\n\u003e npm install @inductjs/core --save\n\nWith yarn:\n\n\u003e yarn add @inductjs/core\n\n### Define a schema\n\nDefine a class to provide Induct with type information. Class-validator decorators are supported for incoming schema validation.\n\n```javascript\n// src/models/user-model.ts\nexport class UserSchema {\n    @IsUUID()\n    uuid: string;\n    @IsString()\n    name: string;\n    @IsInt()\n    age: number;\n\n    constructor(val: UserSchema) {\n        Object.assign(this, val);\n    }\n}\n```\n\nIf you are using MongoDB define your schema as follows:\n\n```typescript\n// src/models/user-model.ts\nimport {prop} from \"@inductjs/core\"; // re-export from @typegoose/typegoose!\n\nexport class UserSchema {\n    prop();\n    uuid: string;\n    prop();\n    name: string;\n    prop();\n    age: number;\n\n    constructor(val: UserSchema) {\n        Object.assign(this, val);\n    }\n}\n```\n\n### Create an express router\n\nInitialize Induct and provide a database object, an object schema, the table to query, and the field to use as ID parameter.\n\n```javascript\n// src/routers/user-router.js\nimport {InductSQL} from \"@inductjs/core\";\n\nconst induct = new InductSQL({\n    db: knex, // Knex connection object to your database\n    schema: UserSchema,\n    tableName: \"dbo.users\",\n    idField: \"uuid\",\n});\n\nconst router = induct.router();\n\nexport default router; // make sure you export as default when using InductServer route autoloading!\n```\n\nThis method will create an express router with the following routes:\n\n-   GET / retrieve all records in the table\n-   GET /:idParam retrieve one record\n-   POST / create one record\n-   PATCH /:idParam update one record\n-   DELETE /:idParam delete one record\n\n### Setup a server\n\nFinally, create a server for your app. This can be an ordinary express entry point, or you can use Induct's server to benefit from a preconfigured express server with autoloading routers like this:\n\n```javascript\n// src/index.js\nimport {createServer} from \"@inductjs/core\";\n\n(async function () {\n    // InductServer auto mounts routers from src/routers ending in -router.{js, ts}\n    const server = await createServer();\n\n    server.start();\n})();\n```\n\nOr if using MongoDB:\n\n```javascript\nimport mongoose from \"mongoose\";\nimport {createServer} from \"@inductjs/core\";\n\n// Export mongoose connection object to use in models\nexport let con;\n\n(async function (): Promise\u003cvoid\u003e {\n    await mongoose.connect(\"\u003cyour connection string here\u003e\", {\n        useNewUrlParser: true,\n        useUnifiedTopology: true,\n        useFindAndModify: false,\n    });\n\n    con = mongoose.connection;\n\n    // InductServer auto mounts routers from src/routers ending in -router.{js, ts}\n    const server = await createServer();\n\n    server.start();\n})();\n```\n\n## Other usage options\n\nInduct exposes several levels of abstraction. The getting started example highlights the quickest way to expose a full table as a REST API, but the individual building blocks can be used separately too.\n\n### Generic route handlers\n\nYou can create generic express route handlers for InductModel methods using the `query` (for GET requests) and `mutation` (for POST, PATCH, DELETE requests) method:\n\n```javascript\nconst router = express.Router();\n\nrouter.get(\"/\", induct.query(\"findAll\"));\n// Second parameter accepts additional options that override class instance options\nrouter.post(`/`, induct.mutation(\"create\", {validate: true}));\n\nrouter.get(`/:${induct.idParam}`, induct.query(\"findOneById\"));\n\nrouter.patch(\n    `/:${induct.idParam}`,\n    induct.mutation(\"update\", {validate: true})\n);\nrouter.delete(`/:${induct.idParam}`, induct.mutation(\"delete\"));\n```\n\nThese handlers use the generic InductModel methods to query your database.\n\n### Use InductModel in custom route handler\n\nYou can use Inducts basic model in your own route handlers as follows:\n\n```javascript\nimport {ok, notFound, InductSQL} from \"@inductjs/core\";\n\n// Create an Induct instance\nconst induct = new InductSQL({\n    db: knex, // Knex connection object to your database\n    schema: UserSchema,\n    tableName: \"dbo.users\",\n    idField: \"uuid\",\n});\n\n// Create your custom route handler\nexport const routeHandler = async (req, res) =\u003e {\n    // Get a model instance and lookup using the id parameter\n    const model = await induct.model({uuid: req.params.id});\n    const result = await model.findOneById();\n\n    // Return a response based on the results of the query\n    if (result \u0026\u0026 result[0]) {\n        return ok(res, result);\n    }\n\n    return notFound(res);\n};\n\n// Create a router and mount custom handler\nconst router = induct.router()\nrouter.get(\"/custom/:id\", routeHandler)\n\nexport default router;\n```\n\n### Using custom models\n\nLets say we want to create a GET route that returns the current version of the product catalog, and a PATCH route that can update the version.\nSo we create a custom model that extends from InductModel, and add our `getCatalogVersion` and `updateCatalogVersion` methods:\n\n```javascript\nimport {SqlAdapter} from \"@inductjs/core\"\n\nexport class ProductModel extends SqlAdapter {\n    constructor(val, opts) {\n        super(val, opts);\n\n        this.updateCatalogVersion = this.updateCatalogVersion.bind(this);\n    }\n\n    getCatalogVersion() {\n        return \"1.0\";\n    }\n\n    updateCatalogVersion() {\n        // The values from the request body are stored in this.data\n        // So this.data.CatalogVersion is equivalent to req.body.CatalogVersion\n        return `Catalog version updated to: ${this.data.CatalogVersion}`;\n    }\n}\n```\n\nNext we can instantiate Induct, and register our methods for use in the generic handlers:\n\n```typescript\nconst induct = new InductSQL({\n    db: knex,\n    schema: ProductSchema,\n    tableName: \"dbo.users\",\n    idField: \"uuid\",\n    // Provide your custom model constructor\n    customModel: ProductModel,\n});\n\n// Create a router as normal\nconst router = induct.router();\n\n// Add additional handlers.\nrouter.get(\"/catalog/version\", induct.query\u003cProductModel\u003e(\"getCatalogVersion\"));\n\nrouter.patch(\n    \"/catalog/version\",\n    induct.mutation\u003cProductModel\u003e(\"updateCatalogVersion\")\n);\n\nexport default router;\n```\n\nA couple of things to take into account when using custom models:\n\n1. Returning _NULL_ from a model method will result in a `400 BAD_REQUEST` response. Unless this is intended, return a non-null value such as an empty string or array from the model function.\n2. Using arrow functions as class methods is **NOT_SUPPORTED**. Using arrow functions causes these methods to not be bound to the prototype of the custom model, which Induct needs for some runtime validations. Make sure to use ordinary method syntax, and bind methods that need to use the class' _this_ context.\n3. You can provide the `query` and `mutation` with your custom model as a type parameter, which will extend the method names typescript will accept with all the methods of your custom model.\n4. When using extra custom handlers in addition to induct.router, take into account that routes have already been mounted to /:id. This can potentially lead to conflicting paths.\n5. Induct exposes several adapters for you to extend from. Use `SqlAdapter` ff building a custom model on an SQL database, or `MongoAdapter` when using MongoDB. Alternatively you can use `InductAdapter` to inherit an abstract class that allows you to customize all the basic methods and add your own.\n\n## Examples\n\nSome example configurations can be found [here](https://github.com/inductjs/core/tree/master/examples).\n\n# License\n\nMIT\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finductjs%2Fcore","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Finductjs%2Fcore","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finductjs%2Fcore/lists"}