{"id":13801973,"url":"https://github.com/giapnguyen74/nextql","last_synced_at":"2025-05-13T12:32:13.678Z","repository":{"id":143971055,"uuid":"98615929","full_name":"giapnguyen74/nextql","owner":"giapnguyen74","description":"Yet Another Data Query Language. Equivalent GraphQL but much more simple.","archived":false,"fork":false,"pushed_at":"2023-03-20T14:52:28.000Z","size":917,"stargazers_count":113,"open_issues_count":4,"forks_count":7,"subscribers_count":10,"default_branch":"master","last_synced_at":"2024-03-15T01:00:49.415Z","etag":null,"topics":["graphql","json","nextql","queries","restful"],"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/giapnguyen74.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,"governance":null,"roadmap":null,"authors":null}},"created_at":"2017-07-28T06:23:41.000Z","updated_at":"2023-03-16T14:22:07.000Z","dependencies_parsed_at":"2024-01-13T10:42:01.300Z","dependency_job_id":"c7a5d2bb-44ba-46ba-badb-4a7126415de3","html_url":"https://github.com/giapnguyen74/nextql","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/giapnguyen74%2Fnextql","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/giapnguyen74%2Fnextql/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/giapnguyen74%2Fnextql/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/giapnguyen74%2Fnextql/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/giapnguyen74","download_url":"https://codeload.github.com/giapnguyen74/nextql/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225217912,"owners_count":17439712,"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":["graphql","json","nextql","queries","restful"],"created_at":"2024-08-04T00:01:31.980Z","updated_at":"2024-11-18T17:30:37.811Z","avatar_url":"https://github.com/giapnguyen74.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"\n\u003cimg src=\"images/logo_next-01.png\" width=\"100\" height=\"100\"\u003e\n\n# NextQL \n\n[**What is NextQL?**](https://medium.com/@giapnguyen74/what-is-nextql-5ca4193795ea) : My blog about NextQL's visions, thoughts and brief informations.\n\nNextQL is JSON query language for APIs and a robust and extensible runtime for resolve those queries. [Equivalent](#equivalent-with-graphql) with [Facebook's GraphQL](http://graphql.org/), but much more simple.\n\n\n\n[![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Dependency Status][daviddm-image]][daviddm-url]\n\n1. Ask what you need, get exactly that. \n2. Get many resources by a single request.\n3. No limitation how to define type systems.\n4. No limitation how to resolve requests.\n\n## TOC\n\u003c!-- TOC --\u003e\n\n- [NextQL](#nextql)\n\t- [TOC](#toc)\n\t- [Change Logs](#change-logs)\n\t\t- [0.0.6 (08-14-2017)](#006-08-14-2017)\n\t\t- [0.0.5 (08-10-2017)](#005-08-10-2017)\n\t\t- [0.0.3 (08-08-2017)](#003-08-08-2017)\n\t- [Install](#install)\n\t- [Plugins](#plugins)\n\t- [Introduction to NextQL](#introduction-to-nextql)\n\t- [Type System](#type-system)\n\t\t- [getAttr hook](#getattr-hook)\n\t\t- [Complex type define](#complex-type-define)\n\t- [How NextQL decide field/method type?](#how-nextql-decide-fieldmethod-type)\n\t- [Query](#query)\n\t\t- [Conditional Query](#conditional-query)\n\t\t- [Self Conditional Query](#self-conditional-query)\n\t\t- [Arguments](#arguments)\n\t\t- [Alias](#alias)\n\t\t- [Traverse related object](#traverse-related-object)\n\t- [NextQL :heart: Plugins](#nextql-heart-plugins)\n\t- [Samples](#samples)\n\t- [APIs](#apis)\n\t\t- [execute](#execute)\n\t\t- [model](#model)\n\t\t- [use](#use)\n\t\t- [beforeCreate](#beforecreate)\n\t\t- [afterResolveType](#afterresolvetype)\n\t- [Equivalent with GraphQL](#equivalent-with-graphql)\n\t- [Testing](#testing)\n\t- [Benchmarks](#benchmarks)\n\t- [Licensing](#licensing)\n\n\u003c!-- /TOC --\u003e\n\n## Change Logs\n### 0.0.6 (08-14-2017)\n* Support conditional queries.\n* Support getAttr hook\n\n### 0.0.5 (08-10-2017)\n* Fix bugs\n* Replace lodash.set by simpler implementation; now nextql have no-dependencies and some increase in raw performance.\n\n### 0.0.3 (08-08-2017)\n* Support explicit type defines for methods and computed fields.\n* Remove Object typed behavior.\n* Introduce new type define: Scalar\n* New beforeExecute hook\n* Better error messages.\n* Back compatible with 0.0.2 except Object typed behavior.\n\n\n## Install\nInstall NextQL from npm\n\n```sh\nnpm install --save nextql\n```\n## Plugins\n* [nextql-configuration](https://github.com/giapnguyen74/nextql-configuration): Pattern to organize and distribute complex NextQL's model systems.\n* [nextql-validate](https://github.com/giapnguyen74/nextql-validate) : Validate nextql methods with fastest-validator.\n* [nextql-feathers](https://github.com/giapnguyen74/nextql-feathers) : Extend NextQL with awesome Feathersjs service. NextQL could do real-time/multiple backend/authentication.\n* [nextql-limit](https://github.com/giapnguyen74/nextql-limit) : Simple solution to protect against excessive or abusive calls (DoS)\n* [nextql-neo4j](https://github.com/giapnguyen74/nextql-neo4j) : Use nextql to provide OGM interface for neo4j database.\n* [nextql-serverless](https://github.com/giapnguyen74/nextql-serverless) : NextQL serve as serverless aggregator function which invoke and concentrates many functions into single result.\n\n## Introduction to NextQL\nNextQL is simply a data query engine inspired by [Facebook GraphQL](http://graphql.org/) but much more simple. NextQL consists a type system based on pure JS objects and a JSON query language.\n\n## Type System\nFor example a User model:\n```js\n{\n    fields: {\n\t\tfirstName: 1, /* let NextQL decide field type */\n\t\tlastName: 1,\n        address: \"Address\", // explicit field type\n        phone: { /* explicit inline type */\n            work: 1,\n            home: 1\n        },\n\t\tfullName: \"*\" // explicit scalar type for computed field [fullName]\n\t},\n\n\tcomputed: {\n\t\t// virtual field computed from source value.\n\t\tfullName(source, params, context){\n\t\t\treturn source.firstName + source.lastName;\n\t\t}\n\t},\n\n\tmethods: {\n\t\t// exposed method for this model.\n\t\tget(params, context){\n\t\t\treturn context.db.get(params.id);\n\t\t}\n\t},\n\treturns: {\n\t\t// Define resolve function to return type for method [get]\n\t\tget(){\n\t\t\treturn \"User\"\n\t\t}\n\t}\n}\n```\n\nEvery model configuration may have 4 keys:\n* **fields** : define model's field name and how to resolve its type.\n* **computed** : define model's virtual field and the function to compute value from source object. NextQL will automatically resolve the value type, unless explicit defined in **fields**\n* **methods**: define exposed APIs for the model. \n* **returns**: By default NextQL will try to resolve type for method's result. The options allow explicit defined type for return value.\n\n**fields** and **computed** equivalent with GraphQL's fields; **methods** and **returns** equivalent with GraphQL's queries and mutations.\n\nDifferent with GraphQL, NextQL not enforced strong-typed for field and method values rather use \"ducking type system\". You have many options for define how to resolve value type:\n* **1** : let NextQL decide value type. [How NextQL decide field/method type?](#how-nextql-decide-fieldmethod-type)\n* **\"other model name\"** : explicit assign value type.\n* **\"*\"** : explicit assign value as scalar value.\n* **[Object]** : explicit define value as inline nested type\n* **[Function]** : Given a function, NextQL should call to resolve value type.\n\n### getAttr hook\nBy default, nextql resolve directly field from source:\n```js\n\tfieldValue = source[fieldName]\n```\nOverride getAttr hook, you could implement your own field resolver. For example, Neo4j Entry object actually store  value inside \"properties\" field; so you either model Entry use nested fields or use getAttr hook\n\nwithout getAttr\n```js\n{\n\tfields: { // without getAttr hook you must define Entry with nested properties field\n\t\tproperties: { \n\t\t\tname: 1\n\t\t}\n\t}\n}\n```\n\nwith getAttr\n```js\n{\n\tfields: { // with getAttr hook, you could define Entry as normal\n\t\tname: 1\n\t},\n\tgetAttr: (source, fieldName){\n\t\treturn source.properties[fieldName];\n\t}\n}\n```\n\n\n### Complex type define\nCombine all those options, you can define very complex model. For example:\n```js\ntest(\"execute#super_complex_inline_type\", async function() {\n\t// auto resolve for hello field\n\tnextql.afterResolveType(source =\u003e (source.a == \"x\" ? \"Test\" : undefined));\n\n\tnextql.model(\"Test\", {\n\t\tfields: { a: 1, b: { c: 1, d: { x: 1, y: \"Test\" } } }, // nested of nested with recusive type\n\t\tcomputed: {\n\t\t\thello() {\n\t\t\t\treturn [{ a: \"x\" }];\n\t\t\t}\n\t\t},\n\t\treturns: {\n\t\t\t// function resolve for test method\n\t\t\ttest: () =\u003e \"Test\"\n\t\t},\n\t\tmethods: {\n\t\t\ttest(params) {\n\t\t\t\treturn [\n\t\t\t\t\t{\n\t\t\t\t\t\ta: \"a\",\n\t\t\t\t\t\tb: {\n\t\t\t\t\t\t\tc: params.x,\n\t\t\t\t\t\t\td: { x: \"22\", y: { a: \"super nest\" } }\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t];\n\t\t\t}\n\t\t}\n\t});\n\tconst result = await nextql.execute({\n\t\tTest: {\n\t\t\ttest: {\n\t\t\t\t$params: { x: 1 },\n\t\t\t\ta: 1,\n\n\t\t\t\tb: {\n\t\t\t\t\tc: 1,\n\t\t\t\t\td: {\n\t\t\t\t\t\tx: 1,\n\t\t\t\t\t\ty: {\n\t\t\t\t\t\t\ta: 1\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\thello: {\n\t\t\t\t\ta: 1\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t});\n\n\texpect(result).toMatchObject({\n\t\tTest: {\n\t\t\ttest: [\n\t\t\t\t{\n\t\t\t\t\ta: \"a\",\n\t\t\t\t\tb: {\n\t\t\t\t\t\tc: 1,\n\t\t\t\t\t\td: {\n\t\t\t\t\t\t\tx: \"22\",\n\t\t\t\t\t\t\ty: {\n\t\t\t\t\t\t\t\ta: \"super nest\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\thello: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ta: \"x\"\n\t\t\t\t\t\t}\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t]\n\t\t}\n\t});\n});\n```\n\n\n## How NextQL decide field/method type?\nNextQL use a global **resolveType function** to resolve model name from \nobject which by default use value constructor name for model name. \n```js\nconst defaultResolveType = value =\u003e value.constructor \u0026\u0026 value.constructor.name;\n```\n\nYou can config your own **resolveType** or better use **afterResolveTypeHooks**. You free to choose whatever to resolve type from object. It could be mongoose model name, __type field ... \n \n\n## Query\nNextQL query is a JSON object define what API methods called and what data to return. NextQL will start to resolve query follow order: model -\u003e method -\u003e fields -\u003e ... recursive fields -\u003e final result.\n\nFor example the query\n```js\n{\n    \"user\": { // model\n        \"me\": { // method\n            \"fullName\": 1 //field or computed field\n        }\n    }\n}\n```\n\nCould produce the JSON result:\n```json\n{\n    \"user\":{\n        \"me\": {\n            \"fullName\": \"Giap Nguyen Huu\"\n        }\n    }\n}\n```\n\nEquivalent call **me** method of class **user** then pick **fullName** field from result. It look like combine REST API call with GraphQL query. \n```\n/user/me =\u003e { fullName }\n```\n\n### Conditional Query\nNextQL conditional query is close with GraphQL fragment but more powerful. Conditional query is a computed function start with \"?\".\n```js\n{\n\t\"user\": { \n        \"me\": { \n            \"fullName\": 1, \n\t\t\t\"?manager\": {\n\t\t\t\t\"subordinates\": {\n\t\t\t\t\t\"fullName\": 1\n\t\t\t\t}\n\t\t\t}\n        }\n    }\n}\n```\n\nThe results could be\n```js\n{\n    \"user\":{\n        \"me\": {\n            \"fullName\": \"Giap Nguyen Huu\",\n\t\t\t\"subordinates\": [ // This fields only resolve if the computed \"?manager\" passed.\n\t\t\t\t{ \"fullName\": \"Tuyen Phuong\"}\n\t\t\t]\n        }\n    }\n}\n```\n\nThe conditonal function is a resolver start with \"?\". It should return a model name or \"true\" if the conditionals passed. \n\nIf a model name, the query inside conditional field will resolved with return model. The behavior is same with GraphQL fragment.\n\nIf true, the query inside conditonal field will resolved as current model or self conditonal query. \n\n```js\n    nextql.model(\"a\", {\n\t\tfields: {\n\t\t\ta: 1\n\t\t},\n\t\tcomputed: {\n\t\t\t\"?a\": function(source, params) { // self conditional resolver\n\t\t\t\treturn source.a ? true : undefined;\n\t\t\t},\n\t\t\t\"?b\": function(source, params) { // normal conditional resolver; cast source as b model\n\t\t\t\treturn source.b ? \"b\" : undefined;\n\t\t\t}\n\t\t},\n\t\tmethods: {\n\t\t\ttest() {\n\t\t\t\treturn [{ a: \"a\", b: \"b\" }, { a: \"a\" }, { b: \"b\" }];\n\t\t\t}\n\t\t},\n\t\treturns: {\n\t\t\ttest: \"a\"\n\t\t}\n\t});\n\n\tnextql.model(\"b\", {\n\t\tfields: {\n\t\t\tb: 1\n\t\t}\n\t});\n\n\tconst result = await nextql.execute({\n\t\ta: {\n\t\t\ttest: {\n\t\t\t\t\"?a\": {\n\t\t\t\t\ta: 1\n\t\t\t\t},\n\t\t\t\t\"?b\": {\n\t\t\t\t\tb: 1\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t});\n```\n\nThe result should be\n```js\n\t{\n\t\ta: {\n\t\t\ttest: [\n\t\t\t\t{\n\t\t\t\t\ta: \"a\",\n\t\t\t\t\tb: \"b\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\ta: \"a\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tb: \"b\"\n\t\t\t\t}\n\t\t\t]\n\t\t}\n\t}\n```\n\n### Self Conditional Query\n Self conditional is when the resolver return true. It seems make no sense because the parent query already resolve as current model. But it could useful in some usecases. Assume you have a User model. If a user is admin, it need additional fields. So you either define a User model with additional fields or 3 models: User interface, Admin model and User model. But if you feels use 1 model not clear the relationship and 3 models is overkill and self conditonal could be used.\n\n```js\n nextql.model(\"User\", {\n\t\tfields: {\n\t\t\tname: 1\n\t\t\tadminWebsites: 1,\n\t\t\tadminStuffs: 1\n\t\t},\n\t\tcomputed: {\n\t\t\t\"?admin\": function(source, params) { // self conditional resolver\n\t\t\t\treturn source.isAdmin ? true : undefined;\n\t\t\t}\n\t\t}\n\t});\n\nconst result = await nextql.execute({\n\t\tUser: {\n\t\t\tfindAll: {\n\t\t\t\tname: 1\n\t\t\t\t\"?admin\": {\n\t\t\t\t\tadminWebsites: 1,\n\t\t\t\t\tadminStuffs: 1\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t});\n```\n\nThe result should be\n```js\n\t{\n\t\tUser: {\n\t\t\tfillAll: [\n\t\t\t\t{ name : \"Thanh\" }, // not admin\n\t\t\t\t{ name : \"Liem\" },// not admin\n\t\t\t\t{ name: \"Giap\",  adminWebsites: null, adminStuffs: [1,2,3]}, //admin\n\t\t\t]\n\t\t}\n\t}\n```\n\nNext time you decide that Admin is enough complex for another model, just remove admin fields and update ?admin resolver. Client side use the same query without aware your changes.\n```js\n\t\"?admin\": function(source, params) { \n\t\t\treturn source.isAdmin ? \"Admin\" : undefined;\n\t\t}\n```\n\n\n### Arguments\nNextQL allow pass arguments to methods and computed fields and conditional fields via reserved **$params** field.\n\n```json\n{   \n    \"human\": {\n        \"get\": {\n            \"$params\": { \"id\": \"1000\" },\n            \"fullName\": 1,\n            \"height\": {\n                \"$params\": { \"unit\": \"m\"}\n            }\n        }\n    }\n}\n```\n\nCould produce the JSON result:\n```json\n{\n    \"human\":{\n        \"get\": {\n            \"fullName\": \"Nguyen Huu Giap\",\n            \"height\" : 1.69\n        }\n    }\n}\n```\n\nYou could use params in conditional query.\n```js\n{\n\tcomputed: {\n\t\t\"?cast\": function(source, params){ // I going to cast source in any model\n\t\t\treturn params.name;\n\t\t}\n\t}\n}\n```\nThen query:\n```js\n{\n\tPerson: {\n\t\tget: {\n\t\t\tpersonStuffs: 1,\n\t\t\t\"?cast\": {\n\t\t\t\t\"$params\": { \"name\": \"Drone\" }, // Please treat me as a drone\n\t\t\t\tdroneStuffs: 1\n\t\t\t}\n\t\t}\n\t}\n}\n```\n### Alias\nBecause result field match with query field. If you need call multiple methods, fields you need alias. NextQL alias is a suffix separator which resolver ignore.\n```json\n{\n    \"human\":{\n        \"get/1000\": {\n            \"$params\": { \"id\": \"1000\" },\n            \"name\": 1\n        },\n        \"get/1001\": {\n            \"$params\": { \"id\": \"1001\" },\n            \"name\": 1\n        }\n    }\n}\n```\n\nCould produce the JSON result:\n```json\n{\n    \"human\":{\n        \"get/1000\": {\n            \"name\": \"Nguyen Huu Giap\"\n        },\n        \"get/1001\": {\n            \"name\": \"Dinh Thi Kim Nguyen\"\n        }\n    }\n}\n```\n\nBy default **\"/\"** is alias separator, anything after it doesn't counted. You could config any suffix separator.\n\n### Traverse related object\nYou can ask more data from relate objects. \n\n```json\n{\n\t\"person\": {\n\t\t\"get/giapnh\": {\n\t\t\t\"$params\": { \"id\": \"giapnh\" },\n\t\t\t\"name\": 1,\n\t\t\t\"children\": {\n\t\t\t\t\"name\": 1\n\t\t\t}\n\t\t},\n\t\t\"get/nguyen\": {\n\t\t\t\"$params\": { \"id\": \"nguyen\" },\n\t\t\t\"name\": 1,\n\t\t\t\"children\": {\n\t\t\t\t\"name\": 1\n\t\t\t}\n\t\t}\n\t}\n}\n```\n\nThe JSON result should be\n```json\n{\n\t\"person\": {\n\t\t\"get/giapnh\": {\n\t\t\t\"name\": \"Nguyen Huu Giap\",\n\t\t\t\"children\": [{\n\t\t\t\t\"name\": \"Nguyen Huu Vinh\"\n\t\t\t}]\n\t\t},\n\t\t\"get/nguyen\": {\n\t\t\t\"name\": \"Dinh Thi Kim Nguyen\",\n\t\t\t\"children\": [{\n\t\t\t\t\"name\": \"Nguyen Huu Vinh\"\n\t\t\t}]\n\t\t}\n\t}\n}\n```\n\n## NextQL :heart: Plugins\nNextQL very simple and flexible. Everything could extensible/customize. NextQL follow Vue plugin pattern.\n\n```js\nMyPlugin.install = function (nextql, options) {\n  nextql.beforeCreate(schema =\u003e schema);\n  nextql.afterResolveType(source =\u003e source.__type);\n}\n\nnextql.use(MyPlugin);\n```\n\n* **nextql.beforeCreate** : the hook call before NextQL build Model object from schema. It is powerful hook to customize schema.\n* **nextql.afterResolveType** : the hook call after NextQL resolve type from source object. It give you a chance to map source to NextQL model type.\n\nSample Mongoose plugin - it catch any schema have mongoose option:\n* Create mongoose model from schema fields.\n* Inject CRUD methods into schema methods.\n\nFinally it help resolve mongoose document into NextQL model.\n\n```js\nconst mongoose = require(\"mongoose\");\n\n/** Simply convert mongoose schema to nextql fields */\nfunction normalizeFields(fields) {\n\tconst _fields = {};\n\tObject.keys(fields).forEach(k =\u003e {\n\t\tif (fields[k].constructor == Object \u0026\u0026 !fields[k].type) {\n\t\t\t_fields[k] = normalizeFields(fields[k]);\n\t\t} else {\n\t\t\t_fields[k] = 1;\n\t\t}\n\t});\n\treturn _fields;\n}\nfunction hookBeforeCreate(options) {\n\tif (options.mongoose) {\n\t\tconst model = mongoose.model(options.name, options.fields);\n\t\toptions.fields = normalizeFields(options.fields);\n\n\t\toptions.methods = Object.assign(\n\t\t\t{\n\t\t\t\tget({ id }) {\n\t\t\t\t\treturn model.findById(id);\n\t\t\t\t},\n\t\t\t\tfind() {\n\t\t\t\t\treturn model.find();\n\t\t\t\t},\n\t\t\t\tcreate({ data }) {\n\t\t\t\t\tvar ins = new model(data);\n\t\t\t\t\treturn ins.save();\n\t\t\t\t},\n\t\t\t\tupdate({ id, data }) {\n\t\t\t\t\treturn model.findById(id).then(ins =\u003e {\n\t\t\t\t\t\tObject.keys(data).forEach(path =\u003e\n\t\t\t\t\t\t\tins.set(path, data[path])\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn ins.save();\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t\tremove({ id }) {\n\t\t\t\t\treturn model.findByIdAndRemove(id);\n\t\t\t\t}\n\t\t\t},\n\t\t\toptions.methods\n\t\t);\n\t}\n}\n\nfunction hookAfterResolveType(source) {\n\treturn source.constructor \u0026\u0026 source.constructor.modelName;\n}\n\nmodule.exports = {\n\tinstall(nextql) {\n\t\tnextql.beforeCreate(hookBeforeCreate);\n\t\tnextql.afterResolveType(hookAfterResolveType);\n\t}\n};\n```\n\n**Mongoose plugin in action**\n```js\nconst mongoose = require(\"mongoose\");\nmongoose.Promise = global.Promise;\n\nmongoose.connect('mongodb://localhost/nextql', { useMongoClient: true }).catch(error =\u003e {\n\tconsole.log(error);\n\tprocess.exit(1);\n});\n\nconst nextql = require('../nextql');\nconst nextqlMongoose = require('./index');\nnextql.use(nextqlMongoose);\n\nnextql.model('test', {\n\tmongoose: true,\n\tfields: {\n\t\t_id: String,\n\t\tname: String\n\t}\n});\n\nasync function run() {\n\tconst users = await nextql.execute({\n\t\ttest: {\n\t\t\tfind: {\n\t\t\t\tname: 1\n\t\t\t}\n\t\t}\n\t});\n\n\treturn users.test\n}\n```\n\nCombine beforeCreate hook and afterResolveType hook, you able to create any kind of NextQL schema and behaviors.\n\n\n## Samples\n* [StarWar](https://github.com/giapnguyen74/nextql/tree/master/samples/starwar)\n\n## APIs\n\n### execute\nExecute query.\n```js\nnextql.execute(query, context).then(\n\tresult =\u003e result,\n\terror =\u003e error\n);\n```\n\n### model\nRegister new model\n```js\nnextql.model('name', { \n\tfields: {},\n\tcomputed: {},\n\tmethods: {}\n})\n```\n\nLookup model throw execption if not found\n```js\nnextql.model('name')\n```\n\n### use\nRegister plugin\n```js\nnextql.use(pluginObj, pluginOpts);\n```\n\n### beforeCreate\nRegister a hook called when new model added. Allow you manipulate model options.\n```js\nnextql.beforeCreate(options =\u003e options);\n```\n\n### afterResolveType\nRegister a hook called when NextQL try resolve type from source. \n```js\nnextql.afterResolveType(source =\u003e modelName);\n```\n\n## Equivalent with GraphQL\nCompare two version of [getDie example](http://graphql.org/graphql-js/object-types/), NextQL very close with GraphQL. Very easy to convert GraphQL code into NextQL. NextQL's [StarWar](https://github.com/giapnguyen74/nextql/tree/master/samples/starwar) reuse most of GraphQL sample code except model's definition.\n\n![getdie](images/getdie.png)\n\n\n## Testing\n\n```\n PASS  test/resolvers.test.js\n PASS  samples/starwar/starwar.test.js\n PASS  test/nextql.test.js\n PASS  test/getDie.test.js\n\nTest Suites: 4 passed, 4 total\nTests:       55 passed, 55 total\nSnapshots:   0 total\nTime:        0.539s, estimated 1s\nRan all test suites.\n------------------|----------|----------|----------|----------|----------------|\nFile              |  % Stmts | % Branch |  % Funcs |  % Lines |Uncovered Lines |\n------------------|----------|----------|----------|----------|----------------|\nAll files         |    95.26 |    92.63 |    90.91 |    96.15 |                |\n samples/starwar  |    90.24 |       75 |    85.71 |    89.47 |                |\n  starWarsData.js |    92.31 |       75 |    85.71 |     91.3 |        104,121 |\n  starwar.js      |    86.67 |      100 |      100 |    86.67 |          30,60 |\n src              |    96.64 |    94.25 |    91.89 |    97.92 |                |\n  index.js        |    91.43 |    82.35 |    83.33 |    93.75 |          39,81 |\n  model.js        |    93.75 |    92.86 |    85.71 |    96.77 |             18 |\n  resolvers.js    |      100 |    98.15 |      100 |      100 |             53 |\n  util.js         |      100 |      100 |      100 |      100 |                |\n------------------|----------|----------|----------|----------|----------------|\n```\n\n## Benchmarks\n```\nnode benchmark/index.js\nnextql#getDie x 42,284 ops/sec ±0.44% (84 runs sampled)\ngraphql#getDie x 9,713 ops/sec ±4.18% (78 runs sampled)\nFastest is nextql#getDie\n```\nWithout type checked and parse query string, NextQL significantly faster than GraphQL.\n\n\n## Licensing\n\n\"The code in this project is licensed under MIT license.\"\n\n\n[npm-image]: https://badge.fury.io/js/nextql.svg\n[npm-url]: https://npmjs.org/package/nextql\n[travis-image]: https://travis-ci.org/giapnguyen74/nextql.svg?branch=master\n[travis-url]: https://travis-ci.org/giapnguyen74/nextql\n[daviddm-image]: https://david-dm.org/giapnguyen74/nextql.svg?theme=shields.io\n[daviddm-url]: https://david-dm.org/giapnguyen74/nextql\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgiapnguyen74%2Fnextql","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgiapnguyen74%2Fnextql","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgiapnguyen74%2Fnextql/lists"}