{"id":14989444,"url":"https://github.com/yrong/koa-neo4j","last_synced_at":"2025-10-09T06:33:24.045Z","repository":{"id":25691103,"uuid":"76250429","full_name":"yrong/koa-neo4j","owner":"yrong","description":"Rapidly create REST APIs, powered by Koa and Neo4j -- batteries included with built-in role based authentication via JWT and reusable backend components","archived":false,"fork":false,"pushed_at":"2023-01-23T23:01:19.000Z","size":1122,"stargazers_count":15,"open_issues_count":7,"forks_count":4,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-09-25T11:18:26.652Z","etag":null,"topics":["bolt","cypher","cypher-query","graphdb","jsonwebtoken","jwt","jwt-authentication","koa","koa-neo4j","koa2","koajs","lifecycle-hooks","neo4j","nosql","rest","rest-api","restful","server","server-backend","server-side"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","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/yrong.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":"2016-12-12T11:28:50.000Z","updated_at":"2025-03-06T18:02:04.000Z","dependencies_parsed_at":"2023-02-13T04:30:36.902Z","dependency_job_id":null,"html_url":"https://github.com/yrong/koa-neo4j","commit_stats":null,"previous_names":["assister-ai/koa-neo4j","satratech/koa-neo4j"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/yrong/koa-neo4j","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yrong%2Fkoa-neo4j","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yrong%2Fkoa-neo4j/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yrong%2Fkoa-neo4j/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yrong%2Fkoa-neo4j/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yrong","download_url":"https://codeload.github.com/yrong/koa-neo4j/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yrong%2Fkoa-neo4j/sbom","scorecard":{"id":213186,"data":{"date":"2025-07-07","repo":{"name":"github.com/yrong/koa-neo4j","commit":"8df8a0902f17d9a9aac5db9e645d4e77b81704cf"},"scorecard":{"version":"v5.2.1-18-gbb9c347d","commit":"bb9c347dff6349d986baab6578a46d68a5524c62"},"score":1.7,"checks":[{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/bb9c347dff6349d986baab6578a46d68a5524c62/docs/checks.md#token-permissions"}},{"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/bb9c347dff6349d986baab6578a46d68a5524c62/docs/checks.md#maintained"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/bb9c347dff6349d986baab6578a46d68a5524c62/docs/checks.md#dangerous-workflow"}},{"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/bb9c347dff6349d986baab6578a46d68a5524c62/docs/checks.md#packaging"}},{"name":"Code-Review","score":0,"reason":"Found 2/25 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/bb9c347dff6349d986baab6578a46d68a5524c62/docs/checks.md#code-review"}},{"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/bb9c347dff6349d986baab6578a46d68a5524c62/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/bb9c347dff6349d986baab6578a46d68a5524c62/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/bb9c347dff6349d986baab6578a46d68a5524c62/docs/checks.md#cii-best-practices"}},{"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/bb9c347dff6349d986baab6578a46d68a5524c62/docs/checks.md#fuzzing"}},{"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/bb9c347dff6349d986baab6578a46d68a5524c62/docs/checks.md#security-policy"}},{"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/bb9c347dff6349d986baab6578a46d68a5524c62/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/bb9c347dff6349d986baab6578a46d68a5524c62/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/bb9c347dff6349d986baab6578a46d68a5524c62/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 7 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/bb9c347dff6349d986baab6578a46d68a5524c62/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":0,"reason":"45 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-v88g-cgmw-v5xw","Warn: Project is vulnerable to: GHSA-93q8-gq69-wqmw","Warn: Project is vulnerable to: GHSA-fwr7-v2mv-hh25","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-cwfw-4gq5-mrqx","Warn: Project is vulnerable to: GHSA-g95f-p29q-9xw4","Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg","Warn: Project is vulnerable to: GHSA-x9w5-v3q2-3rhw","Warn: Project is vulnerable to: GHSA-h452-7996-h45h","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275","Warn: Project is vulnerable to: GHSA-9vvw-cc9w-f27h","Warn: Project is vulnerable to: GHSA-gxpj-cx7g-858c","Warn: Project is vulnerable to: GHSA-w573-4hg7-7wgq","Warn: Project is vulnerable to: GHSA-h6ch-v84p-w6p9","Warn: Project is vulnerable to: GHSA-434g-2637-qmqr","Warn: Project is vulnerable to: GHSA-49q7-c7j4-3p7m","Warn: Project is vulnerable to: GHSA-977x-g7h5-7qgw","Warn: Project is vulnerable to: GHSA-f7q4-pwc6-w24p","Warn: Project is vulnerable to: GHSA-fc9h-whq2-v747","Warn: Project is vulnerable to: GHSA-vjh7-7g9h-fjfh","Warn: Project is vulnerable to: GHSA-4q6p-r6v2-jvc5","Warn: Project is vulnerable to: GHSA-qh2h-chj9-jffq","Warn: Project is vulnerable to: GHSA-9c47-m6qq-7p4h","Warn: Project is vulnerable to: GHSA-8cf7-32gw-wr33","Warn: Project is vulnerable to: GHSA-hjrf-2m68-5959","Warn: Project is vulnerable to: GHSA-qwph-4952-7xr6","Warn: Project is vulnerable to: GHSA-593f-38f6-jp5m","Warn: Project is vulnerable to: GHSA-x2rg-q646-7m2v","Warn: Project is vulnerable to: GHSA-76p3-8jx3-jpfq","Warn: Project is vulnerable to: GHSA-3rfm-jhwj-7488","Warn: Project is vulnerable to: GHSA-hhq3-ff78-jv3g","Warn: Project is vulnerable to: GHSA-82v2-mx6x-wq7q","Warn: Project is vulnerable to: GHSA-952p-6rrq-rcjv","Warn: Project is vulnerable to: GHSA-f8q6-p94x-37v3","Warn: Project is vulnerable to: GHSA-vh95-rmgr-6w4m","Warn: Project is vulnerable to: GHSA-xvch-5gv4-984h","Warn: Project is vulnerable to: GHSA-v923-w3x8-wh69","Warn: Project is vulnerable to: GHSA-9wv6-86v2-598j","Warn: Project is vulnerable to: GHSA-h7cp-r72f-jxh6","Warn: Project is vulnerable to: GHSA-v62p-rq8g-8h59","Warn: Project is vulnerable to: GHSA-hrpp-h998-j3pp","Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw","Warn: Project is vulnerable to: GHSA-4wf5-vphf-c2xc"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/bb9c347dff6349d986baab6578a46d68a5524c62/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-17T01:07:58.440Z","repository_id":25691103,"created_at":"2025-08-17T01:07:58.440Z","updated_at":"2025-08-17T01:07:58.440Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279000832,"owners_count":26082950,"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-10-09T02:00:07.460Z","response_time":59,"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":["bolt","cypher","cypher-query","graphdb","jsonwebtoken","jwt","jwt-authentication","koa","koa-neo4j","koa2","koajs","lifecycle-hooks","neo4j","nosql","rest","rest-api","restful","server","server-backend","server-side"],"created_at":"2024-09-24T14:18:22.995Z","updated_at":"2025-10-09T06:33:24.000Z","avatar_url":"https://github.com/yrong.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![npm version](https://badge.fury.io/js/koa-neo4j.svg)](https://www.npmjs.com/package/koa-neo4j)\n[![Build Status](https://travis-ci.org/yrong/koa-neo4j.svg?branch=master)](https://travis-ci.org/yrong/koa-neo4j)\n[![Badge](https://img.shields.io/badge/link-996.icu-%23FF4D5B.svg)](https://996.icu/#/en_US)\n \n# koa-neo4j\n\n`koa-neo4j` is a framework for creating RESTful web servers. It's been built on top of the widely adapted\n[Koa](http://koajs.com/) library and the NoSQL graph database technology of [Neo4j](https://neo4j.com/). `koa-neo4j`\nenables one to create web servers scalable both in terms of code complexity and horizontal growth in deployment.\n\n![koa-neo4j logo](https://github.com/yrong/koa-neo4j/raw/master/images/logo.png \"koa-neo4j\")\n\n## Table of contents\n\n1. [Introduction](#introduction)\n2. [Install](#install)\n3. [Usage](#usage)\n    - [Defining an API](#defining-an-api)\n    - [Authentication](#authentication)\n    - [Lifecycle hooks](#lifecycle-hooks)\n        - [check lifecycle](#check-lifecycle)\n        - [preProcess lifecycle](#preprocess-lifecycle)\n        - [execution lifecycle](#execution-lifecycle)\n        - [postProcess lifecycle](#postprocess-lifecycle)\n        - [postServe lifecycle](#postserve-lifecycle)\n    - [Procedures](#procedures)\n4. [License](#license)\n\n## Introduction\n\nChoosing a NoSQL graph database for persistence is wise for\n[a number of reasons](https://github.com/yrong/koa-neo4j/blob/master/whygraphdb.md).\nWhile Neo4j provides the DB infrastructure for such choice, applications using Neo4j normally conduct queries directly\nfrom the client side, which might not be the best option:\n\n- Database is exposed to the client, unless some explicit security mechanism is in place; one can *see* the\ninnards of the database by `View page source`\n- There is no **one server to rule them all**, queries are `string`s, scattered around different\nclients (web, mobile, etc.)\n- Third-party developers might not be familiar with Cypher\n\n`koa-neo4j` addresses all of the above issues:\n\n- Stands as a middle layer between clients and the database \n- Gives structure to your server's logic in form of a file-based project; finally a home for Cypher!\nAll of the clients can then talk to an instance of this server\n- Converts Cypher files to REST routes, a cross-platform web standard that developers are familiar with, it does so\non top of the widely-adapted [**koa**](http://koajs.com/) server, ripe for further customization\n \nIn addition, it comes with *goodies*:\n\n- Hassle-free [authentication](#authentication) and non-opinionated user management, you describe (in Cypher) how your\nusers and roles are stored, the framework provides authentication and role-based access management\n- [Lifecycle hooks](#lifecycle-hooks), enabling one to tweak incoming and outgoing data based on one's needs, allowing\nutilisation of the full power of `nodejs` and `javascript` ecosystem in the process\n- [Procedures](#procedures) as a means for creating reusable blocks of backend code \n\n## Install\n\n```bash\nnpm install koa-neo4j --save\n```\n\n## Usage\n\nTo get started quickly you can clone [koa-neo4j-starter-kit](https://github.com/yrong/koa-neo4j-starter-kit)\n\n```javascript\nvar KoaNeo4jApp = require('koa-neo4j');\n\nvar app = new KoaNeo4jApp({\n    // Neo4j config objects, mandatory\n    neo4j: {\n        boltUrl: 'bolt://localhost',\n        user: 'neo4j',\n        password: '\u003cYOUR_NEO4J_PASSWORD\u003e'\n    },\n\n    // Authentication config object, optional\n    // authentication: {...} // explained below\n\n    // APIs config object, optional (same effect could be achieved later by app.defineAPI)\n    apis: [\n        {\n            method: 'GET',\n            route: '/authors',\n            cypherQueryFile: './cypher/authors.cyp'\n        },\n        {\n            method: 'GET',\n            route: '/articles/:skip/:limit',\n            cypherQueryFile: './cypher/articles.cyp'\n        }\n    ],\n\n    // Koa middlewares could be injected and will be loaded before api invoked,e.g:a koa static file serving middleware as following\n    middleware:[convert(staticFile('./public'))]\n});\n\napp.listen(3000, function() {\n    console.log('App listening on port 3000.');\n});\n```\n\n### Defining an API\n\nAn API is defined by at least three keys:\n\n`method`, specifies the request type (GET|POST|PUT|DEL)\n\n`route`, the path to this API (e.g. the first API defined in `apis` above becomes http://localhost:3000/authors)\n\n`cypherQueryFile`, path to the corresponding `.cyp` file\n\nOptionally you can specify roles whom can access this route with `allowedRoles` and\nalso [lifecycle hooks](#lifecycle-hooks).\n\nAs an example:\n\n```javascript\napp.defineAPI({\n    // allowedRoles: ['admin', 'author']    // roles are case insensitive\n    method: 'POST',\n    route: '/create-article',\n    cypherQueryFile: './cypher/create_article.cyp'\n});\n```\n\nAnd then in `./cypher/create_article.cyp`:\n\n```cypher\nCREATE (a:Article {\n    title: $title,\n    author: $author,\n    created_at: timestapm()\n})\nRETURN a\n```\n\nCypher queries accept parameters via [the `$` syntax](http://neo4j.com/docs/developer-manual/current/cypher/syntax/parameters/).\n\nThese parameters are matched by query parameters `/articles?title=Hello\u0026author=World` or route parameters\n(e.g. if route was defined as `/create-article/:author/:title` then `/create-article/World/Hello`)\n\nIn addition, any data accompanied by the request will also be passed to the Cypher query, retaining the variable names,\nso for example:\n\n```bash\ncurl --data \"title=The%20Capital%20T%20Truth\u0026author=David%20Foster%20Wallace\" localhost:3000/create-article\n```\n\nbecomes a POST request, {\"title\": \"The Capital T Truth\", \"author\": \"David Foster Wallace\"} will be\npassed to `./cypher/create_article.cyp` which refers to these parameters as `$title` and `$author`\n\nIn case of encountering same variable names, priority is applied: *`request data` \u003e `route params` \u003e `query params`*\n\n### Authentication\n\nAuthentication is facilitated through [JSON web token](https://github.com/auth0/node-jsonwebtoken), all it takes to\nhave authentication in your app is to supplement `Authentication config object` either with `authentication` key\nwhen initiating the app instance or in `configureAuthentication` method:\n```javascript\napp.configureAuthentication({\n    // route, mandatory.\n    route: '/auth',\n\n    // secret, mandatory. This is the key that JWT uses to encode objects, best practice is to use a\n    // long and random password-like string\n    secret: 'secret',\n\n    // userCypherQueryFile, mandatory. This cypher query is invoked with `$username` and is expected to return\n    // a single object at least containing two keys: `{id: \u003cuser_id\u003e, password: \u003cuser_password_or_hash\u003e}`\n    // the returned `id` would later be passed to get roles of this user\n    userCypherQueryFile: './cypher/user.cyp',\n\n    // rolesCypherQueryFile, optional. Invoked with `$id` returned from userCypherQueryFile, this query is\n    // expected to return a list of strings describing roles of this user, you can do all sorts of traversals\n    // that cypher allows to generate this list. Defaults to labels of the node matching the id:\n    // `MATCH (user) WHERE id(user) = $id RETURN {roles: labels(user)}`\n    // rolesCypherQueryFile: './cypher/roles.cyp'\n});\n```\n\nAnd your desired `$username` to `user` mapping in `userCypherQueryFile`:\n```cypher\n// Takes $username and returns a user object with at least 'id' and 'password'\nMATCH (author:Author)-[:HAS]-\u003e(account:UserAccount)\nWHERE account.user_name = $username\nRETURN {id: id(author), password: account.password_hash}\n```\n\nWhen authentication is configured, you can access it by sending a POST request to the route you specified. Pass a\nJSON object to e.g. `/auth` in the following form:\n\n```json\n{\n  \"username\": \"\u003cuser_name\u003e\",\n  \"password\": \"\u003cuser_password_or_hash\u003e\",\n  \"remember\": true\n}\n```\n\n![Invoking Authentication](https://github.com/yrong/koa-neo4j/raw/master/images/invoking_auth.png \"Invoking Authentication\")\n\nNote that if you don't set `\"remember\": true`, the generated token expires in an hour.\n\nReturned object contains a `token` which should be supplemented as `Authorization` header\nin subsequent calls to routes that have `allowedRoles` protection.\n\nIn addition, a `user` object is returned that matches the object returned by `userCypherQueryFile` except\nfor the `password` key, which is deleted (so that security won't be compromised should\nclients decide to save this object) and `roles` key, which is the object returned by `rolesCypherQueryFile`.\n\nWhen a request to a route guarded by `allowedRoles` is received, the request either does not have an `Authorization`\nheader set, in which case the server responds with a `401: Unauthorized` error, or the `Authorization` header is present.\nIn case of a valid header (not expired or manipulated), the request goes through and the user object would be attached\nto the [Koa context](https://github.com/koajs/koa/blob/v2.x/docs/api/context.md) and made available to\n[lifecycle hooks](#lifecycle-hooks) as `ctx.user`.\n\n### Lifecycle hooks\n\nA lifecycle hook is a single function or a group of functions invoked at a certain phase in request-to-response cycle.\nIt helps with shaping the data according to one's needs. Further, the framework comes with a number of built-in hook\nfunctions, ready to be dropped in their corresponding lifecycle.\n\nA hook function takes the form of a normal JavaScript function, with arguments consistent with the lifecycle in which\nit'd be deployed. If an array of functions is submitted for a lifecycle, each function in the array is executed,\nsequentially, and the returned object from the function would be passed as the first argument of the next function.\n\n```javascript\napp.defineAPI({\n    // ...\n    preProcess: [\n        function(params, ctx) {\n            // do something with params and/or ctx\n            return {modified: 'params'};\n        },  //     ‾‾‾‾‾‾‾‾‾‾|‾‾‾‾‾‾‾‾‾\n        //                   |\n        //         ↓‾‾‾‾‾‾‾‾‾‾\n        function(params, ctx) {\n            params.again = 'modified';\n            // params now is: {modified: 'params', again: 'modified'}\n            return params;\n        },  //     ‾‾|‾‾‾\n        //           |\n        //           ↓\n        function(params, ctx) {\n            params.and = 'again';\n            // params now is: {modified: 'params', again: 'modified', and: 'again'}\n            return params;\n        },\n        //      ... this can continue ...\n    ],\n    // ...\n});\n```\n\n**ProTip:** if returned value in a hook function is a `Promise` or an array containing any `Promise`s, first argument of\nthe next function would be the resolved value or an array with all it's elements resolved, respectively.\n\n#### check lifecycle\n\nHook function signature: **(params[, ctx]) -\u003e :boolean**\n\nThis lifecycle is the first one that a request goes through. It is useful for scenarios where you want to check\nthe parameters or the user before commencing. A `false` return value produces an `error in check lifecycle` error.\n\n```javascript\n// Example:\napp.defineAPI({\n    // ...\n    check: function(params, ctx) {\n        // check user has enough money\n        return params.amount \u003c getBalance(ctx.user.id);\n    },\n    // ...\n});\n```\n\n```javascript\n// Default:\ncheck: function (params) {\n  // Always passes\n  return true;\n}\n```\n\n**check built-in hook functions:** import/require from\n[`koa-neo4j/check`](https://github.com/yrong/koa-neo4j/blob/master/src/check.js)\n([DOCS](https://github.com/yrong/koa-neo4j/blob/master/src/check.md))\n\n#### preProcess lifecycle\n\nHook function signature: **(params[, ctx]) -\u003e params**\n\nUsing this lifecycle, one can adjust parameters before sending them to Cypher. Parsing strings is a usual suspect,\nthe framework comes with many built-in parse functions for this lifecycle.\n\n```javascript\n// Example:\nvar parseFloats = require('koa-neo4j/preprocess').parseFloats;\n\napp.defineAPI({\n    // ...\n    preProcess: [ \n        // parse params.amount as float\n        parseFloats('amount'),\n\n        function(params) {\n            // give 10% discount\n            params.amount = params.amount * 0.9;\n            return params;\n        },\n        // ...\n    ],\n    // ...\n});\n```\n\n```javascript\n// Default:\npreProcess: function (params) {\n  // Returns `params` unchanged\n  return params;\n}\n```\n\n**preProcess built-in hook functions:** import/require from\n[`koa-neo4j/preprocess`](https://github.com/yrong/koa-neo4j/blob/master/src/preprocess.js)\n([DOCS](https://github.com/yrong/koa-neo4j/blob/master/src/preprocess.md))\n\n#### execution lifecycle\n\nExecution happens between `preProcess` and `postProcess`, takes `params` as input and generates `result`. Currently\nthere are 4 types of execution, if all were present in an [API](#defining-an-api) or [Procedure](#procedures) definition,\npriority is applied:\n\n##### *`params.result` \u003e `params.cypher` \u003e `cypherQueryFile`*\n\n**ProTip:** `key` would be *consumed* as a result of any `params.\u003ckey\u003e` execution, meaning that the `key` reference in\n`params` would be deleted in subsequent references to `params`.\n\n##### cypherQueryFile\n\nHappens if a cypherQueryFile is supplied. Executes the Cypher query contained in the file, passing `params` along which\nCypher can access with [the `$` syntax](http://neo4j.com/docs/developer-manual/current/cypher/syntax/parameters/).\n\n##### params.cypher\n\nIf you need string manipulation to create your Cypher query, you can do so in\n[preProcess lifecycle](#preprocess-lifecycle) by assigning `params.cypher` to your query. After all preProcess hook\nfunctions are executed, framework will see whether `params.cypher` is defined, and executes it if present. Except for\nprimitive string, `params.cypher` may also be string array type which contains multiple cypher queries to execute.\n\n##### params.result\n\n`params.result` could be set to a value, a `Promise` or an array containing `Promise`s. `result` would then be the\nvalue, the resolved value of the `Promise` or an array with all it's elements resolved, respectively. This is useful\nwhen one wants the result to come from [procedures](#procedures), since calling a procedure returns a promise:\n\n```javascript\n// Example:\nvar someProcedure = app.createProcedure({\n    // ...\n});\n\napp.defineAPI({\n    // ...\n    preProcess: [\n        // ...\n        function(params) {\n            params.result = [];\n            for (var i = 0; i \u003c params.someArray.length; i++)\n                params.result.push(someProcedure({someParameter: params.someArray[i]}));\n            return params;\n        }\n    ],\n    postProcess: [\n        function(result) {\n            // `result` is now an array containing resolved values of calls to someProcedure\n            return result;\n        }\n    ]\n});\n```\n\n#### postProcess lifecycle\n\nHook function signature: **(result[, params, ctx]) -\u003e result**\n\nThis lifecycle takes the `result` from execution lifecycle and amends further changes to the result before sending it to\nthe client. If `params.cypher` is array type assigned in execution lifecycle, `result` will also be an array contains execution\nresults for each cypher query.\n\n```javascript\n// Example:\nvar fetchOne = require('koa-neo4j/postprocess').fetchOne;\n\napp.defineAPI({\n    // ...\n    postProcess: [\n        fetchOne,\n        function(result, params, ctx) {\n            return {\n                user: ctx.user,\n                balance_after: params.balance - result\n            };\n        },\n        // ...\n    ],\n    // ...\n});\n```\n\n```javascript\n// Default:\npostProcess: function (result) {\n  // serves result of execution lifecycle, unchanged\n  return result;\n}\n```\n\n**postProcess built-in hook functions:** import/require from\n[`koa-neo4j/postprocess`](https://github.com/yrong/koa-neo4j/blob/master/src/postprocess.js)\n([DOCS](https://github.com/yrong/koa-neo4j/blob/master/src/postprocess.md))\n\n#### postServe lifecycle\n\nHook function signature: **(result[, params, ctx]) -\u003e result**\n\nSemantics of `postServe` is identical to `postProcess`, except that `postServe` is invoked **after** the response of the\nrequest is sent (served). This lifecycle suits time consuming tasks that are internal to logic and can be carried out\nafter the request is served.\n\n```javascript\n// Default:\npostServe: function (result) {\n  // Doesn't do anything\n  return result;\n}\n```\n\n#### global transaction\n\nWhen globalTransaction flag is set, neo4j cypher will be committed after finish postProcessing and rollback if exception thrown\n\n```javascript\napp.defineAPI({\n    preProcess:func1,\n    postProcess:func2,\n    globalTransaction:true\n});\n```\n\n\n### Procedures\n\nProcedures share semantics with APIs, they are defined in the same way that an API is defined, except they don't accept\n`method`, `route` and `allowedRoles`. You can create idiomatic and reusable blocks of backend code using procedures and\nbuilt-in lifecycle hook functions:\n\n```javascript\nvar parseIds = require('koa-neo4j/preprocess').parseIds;\nvar parseDates = require('koa-neo4j/preprocess').parseDates;\n\nvar logValues = require('koa-neo4j/debug').logValues;\n\nvar errorOnEmptyResult = require('koa-neo4j/postprocess').errorOnEmptyResult;\nvar fetchOne = require('koa-neo4j/postprocess').fetchOne;\nvar convertToPreProcess = require('koa-neo4j/postprocess').convertToPreProcess;\n\nvar articlesAfterDate = app.createProcedure({\n    // Providing a name facilitates debugging\n    name: 'articlesAfterDate',\n    preProcess: [\n        parseIds('author_id'),\n        parseDates({'timestamp': 'date'}),\n        logValues\n    ],\n    cypherQueryFile: './cypher/articles_after_date.cyp',\n    postProcess: [\n        logValues,\n        errorOnEmptyResult('author not found'), // returns this message with a 404 http code\n        fetchOne,\n        convertToPreProcess('articles') // assigns params.articles to result of procedure\n    ]\n});\n\nvar blogsAfterDate = app.createProcedure({\n    // ...\n});\n\napp.defineAPI({\n    allowedRoles: ['admin'],\n    route: '/author-activity/:author_id/:timestamp',\n    preProcess: [\n        articlesAfterDate,\n        blogsAfterDate,\n        function(params) {\n            params.result = {\n                // params.date is created by parseDates hook function in articlesAfterDate\n                interval: `past ${new Date().getDate() - params.date.getDate()} days`,\n                articles: params.articles,\n                blogs: params.blogs\n            };\n            return params;\n        }\n    ]\n})\n```\n\n**ProTip:** procedures created by `app.createProcedure` are callable and return a promise that resolves to result:\n```javascript\nvar someProcedure = app.createProcedure({\n    // ...\n});\n\nsomeProcedure(params, ctx).then(function(result) {\n    console.log(result);\n});\n```\n\nOr if you can use async/await:\n\n```javascript\napp.defineAPI({\n    preProcess: [\n        async params =\u003e {\n            // ...\n            params.someValue = await someProcedure({some: 'parameter'});\n            return params;\n        },\n        // ...\n    ],\n    // ...\n})\n```\n\n**ProTip:** a `defineAPI` block can reuse a procedure's body via the `procedure` key:\n\n```javascript\napp.defineAPI({\n    method: 'POST',\n    route: '/some-api',\n    procedure: someProcedure\n});\n```\n\n## License\n\n[Anti-996 License](https://github.com/996icu/996.ICU/blob/master/LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyrong%2Fkoa-neo4j","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyrong%2Fkoa-neo4j","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyrong%2Fkoa-neo4j/lists"}