{"id":15650174,"url":"https://github.com/atulmy/wispy","last_synced_at":"2025-10-14T03:31:21.076Z","repository":{"id":54304379,"uuid":"147529170","full_name":"atulmy/wispy","owner":"atulmy","description":"🌱  An experimental lightweight (remote procedure call) API pattern.","archived":true,"fork":false,"pushed_at":"2021-02-25T10:51:03.000Z","size":2166,"stargazers_count":38,"open_issues_count":0,"forks_count":3,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-10-03T12:37:49.465Z","etag":null,"topics":["api","api-graphql","api-rest","api-soap","idea","json","learn-by-examples","mongodb","mongoose","nodejs","opinionated","poc","remote-procedure-calls","rpc"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":false,"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/atulmy.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":"2018-09-05T14:14:53.000Z","updated_at":"2024-08-04T21:53:11.000Z","dependencies_parsed_at":"2022-08-13T11:30:53.286Z","dependency_job_id":null,"html_url":"https://github.com/atulmy/wispy","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/atulmy%2Fwispy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/atulmy%2Fwispy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/atulmy%2Fwispy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/atulmy%2Fwispy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/atulmy","download_url":"https://codeload.github.com/atulmy/wispy/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":236441988,"owners_count":19149327,"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":["api","api-graphql","api-rest","api-soap","idea","json","learn-by-examples","mongodb","mongoose","nodejs","opinionated","poc","remote-procedure-calls","rpc"],"created_at":"2024-10-03T12:33:41.935Z","updated_at":"2025-10-14T03:31:15.738Z","avatar_url":"https://github.com/atulmy.png","language":"JavaScript","readme":"# WISPY 🌱\nAn experimental lightweight (remote procedure call) API pattern.\n\n## Idea\nExecute functions or piece of code living in your API end without any hassles of creating multiple endpoints (REST) or managing complex schemas (GraphQL). Server essentially exposes functions or methods which can be called by client with standard JSON payload along with passing any parameters to it. \n\n## Comparisons\n- Uses JSON unlike string based GQL in GraphQL or URL in REST\n- Only one endpoint or route on server unlike in REST (similar to GraphQL)\n- No strict schema unlike GraphQL\n- Simple customizable payload in JSON format unlike in SOAP which uses XML and strict structure\n- Option to select the fields you want the API to return (similar to GraphQL)\n- Option to subscribe to live updates (Subscriptions via websockets) like in GraphQL\n\n\u003ctable\u003e\n    \u003cthead\u003e\n        \u003ctr\u003e\n            \u003cth\u003eTechnology\u003c/th\u003e\n            \u003cth\u003eMessage format\u003c/th\u003e\n            \u003cth\u003eEndpoints\u003c/th\u003e\n            \u003cth\u003eField selection\u003c/th\u003e\n            \u003cth\u003eStrict Schema\u003c/th\u003e\n            \u003cth\u003eSubscriptions\u003c/th\u003e\n        \u003c/tr\u003e\n    \u003c/thead\u003e\n    \u003ctbody\u003e\n        \u003ctr\u003e\n            \u003ctd\u003eWispy\u003c/td\u003e\n            \u003ctd\u003eJSON\u003c/td\u003e\n            \u003ctd\u003eOne\u003c/td\u003e\n            \u003ctd\u003eYes\u003c/td\u003e\n            \u003ctd\u003eNo\u003c/td\u003e\n            \u003ctd\u003eYes\u003c/td\u003e\n        \u003c/tr\u003e\n        \u003ctr\u003e\n            \u003ctd\u003eGraphQL\u003c/td\u003e\n            \u003ctd\u003eGQL\u003c/td\u003e\n            \u003ctd\u003eOne\u003c/td\u003e\n            \u003ctd\u003eYes\u003c/td\u003e\n            \u003ctd\u003eYes\u003c/td\u003e\n            \u003ctd\u003eYes\u003c/td\u003e\n        \u003c/tr\u003e\n        \u003ctr\u003e\n            \u003ctd\u003eREST\u003c/td\u003e\n            \u003ctd\u003eJSON\u003c/td\u003e\n            \u003ctd\u003eMultiple\u003c/td\u003e\n            \u003ctd\u003eNo\u003c/td\u003e\n            \u003ctd\u003eNo\u003c/td\u003e\n            \u003ctd\u003eNo\u003c/td\u003e\n        \u003c/tr\u003e\n        \u003ctr\u003e\n            \u003ctd\u003eSOAP\u003c/td\u003e\n            \u003ctd\u003eXML\u003c/td\u003e\n            \u003ctd\u003eOne\u003c/td\u003e\n            \u003ctd\u003eYes\u003c/td\u003e\n            \u003ctd\u003eYes\u003c/td\u003e\n            \u003ctd\u003eNo\u003c/td\u003e\n        \u003c/tr\u003e\n    \u003c/tbody\u003e\n\u003c/table\u003e\n\n## Examples\n\n#### 1. Create\n\ncURL\n```bash\ncurl http://localhost:8000 \\\n  -H 'Content-type: application/json' \\\n  -d '{\"operation\": \"productCreate\", \"params\": {\"name\": \"Product 1\", \"description\": \"Good product.\"}}'\n```\n\nfetch\n```javascript\nasync function productCreate() {\n  try {\n    const config = {\n      method: 'POST',\n      headers: {\n        'Accept': 'application/json',\n        'Content-Type': 'application/json',\n      },\n      body: JSON.stringify({\n        \"operation\": \"productCreate\", \n        \"params\": {\n          \"name\": \"Product 1\", \n          \"description\": \"Good product.\"\n        }\n      })\n    }\n    const response = await fetch('http://localhost:8000', config)\n    const data = await response.json()\n    console.log(data)\n  } catch (error) {\n    console.error(error)\n  }\n}\n```\n\nResult\n```json\n{\n  \"success\": true,\n  \"message\": \"Product created successfully.\",\n  \"data\": {\n    \"_id\": \"5b914211aaabdc51bf1839a9\",\n    \"name\": \"Product 1\",\n    \"description\": \"Good product.\",\n    \"createdAt\": \"2018-09-06T15:04:49.111Z\",\n    \"updatedAt\": \"2018-09-06T15:04:49.111Z\",\n    \"__v\": 0\n  }\n}\n```\n\n#### 2.1 Read\ncURL\n```bash\ncurl http://localhost:8000 \\\n  -H 'Content-type: application/json' \\\n  -d '{\"operation\": \"productList\"}'\n```\n\nfetch\n```javascript\nasync function productList() {\n  try {\n    const config = {\n      method: 'POST',\n      headers: {\n        'Accept': 'application/json',\n        'Content-Type': 'application/json',\n      },\n      body: JSON.stringify({ \"operation\": \"productList\" })\n    }\n    const response = await fetch('http://localhost:8000', config)\n    const data = await response.json()\n    console.log(data)\n  } catch (error) {\n    console.error(error)\n  }\n}\n```\n\nResult\n```json\n{\n  \"success\": true,\n  \"message\": \"\",\n  \"data\": [\n    {\n      \"_id\": \"5b91146bcc58ba33ee349e28\",\n      \"name\": \"Hercle, habena dexter!, clabulare!\",\n      \"description\": \"Tremble oddly like a unrelated pathway.\",\n      \"createdAt\": \"2018-09-06T11:50:03.910Z\",\n      \"updatedAt\": \"2018-09-06T11:50:03.910Z\",\n      \"__v\": 0\n    },\n    {\n      \"_id\": \"5b9113b3cc58ba33ee349e26\",\n      \"name\": \"Passion is a small captain.\",\n      \"description\": \"This turbulence has only been evacuated by a reliable proton.\",\n      \"createdAt\": \"2018-09-06T11:46:59.167Z\",\n      \"updatedAt\": \"2018-09-06T11:49:52.799Z\",\n      \"__v\": 0\n    }\n  ]\n}\n```\n\n#### 2.2 Read with fields selection\ncURL\n```bash\ncurl http://localhost:8000 \\\n  -H 'Content-type: application/json' \\\n  -d '{\"operation\": \"productList\", \"fields\": [\"_id\", \"name\"]}'\n```\n\nfetch\n```javascript\nasync function productList() {\n  try {\n    const config = {\n      method: 'POST',\n      headers: {\n        'Accept': 'application/json',\n        'Content-Type': 'application/json',\n      },\n      body: JSON.stringify({\n        \"operation\": \"productList\", \n        \"fields\": [\"_id\", \"name\"]\n      })\n    }\n    const response = await fetch('http://localhost:8000', config)\n    const data = await response.json()\n    console.log(data)\n  } catch (error) {\n    console.error(error)\n  }\n}\n```\n\nResult\n```json\n{\n  \"success\": true,\n  \"message\": \"\",\n  \"data\": [\n    {\n      \"_id\": \"5b91146bcc58ba33ee349e28\",\n      \"name\": \"Hercle, habena dexter!, clabulare!\"\n    },\n    {\n      \"_id\": \"5b9113b3cc58ba33ee349e26\",\n      \"name\": \"Passion is a small captain.\"\n    }\n  ]\n}\n```\n\n#### 3. Update\n\ncURL\n```bash\ncurl http://localhost:8000 \\\n  -H 'Content-type: application/json' \\\n  -d '{\"operation\": \"productUpdate\", \"params\": {\"_id\": \"5b914211aaabdc51bf1839a9\", \"name\": \"Product 1\", \"description\": \"Good product it is.\"}}'\n```\n\nfetch\n```javascript\nasync function productUpdate() {\n  try {\n    const config = {\n      method: 'POST',\n      headers: {\n        'Accept': 'application/json',\n        'Content-Type': 'application/json',\n      },\n      body: JSON.stringify({\n        \"operation\": \"productUpdate\", \n        \"params\": {\n          \"_id\": \"5b914211aaabdc51bf1839a9\", \n          \"name\": \"Product 1\", \n          \"description\": \"Good product it is.\"\n        }\n      })\n    }\n    const response = await fetch('http://localhost:8000', config)\n    const data = await response.json()\n    console.log(data)\n  } catch (error) {\n    console.error(error)\n  }\n}\n```\n\nResult\n```json\n{\n  \"success\": true,\n  \"message\": \"Product updated successfully.\",\n  \"data\": {\n    \"n\": 1,\n    \"nModified\": 1,\n    \"ok\": 1\n  }\n}\n```\n\n#### 4. Delete\n\ncURL\n```bash\ncurl http://localhost:8000 \\\n  -H 'Content-type: application/json' \\\n  -d '{\"operation\": \"productRemove\", \"params\": {\"productId\": \"5b914211aaabdc51bf1839a9\"}}'\n```\n\nfetch\n```javascript\nasync function productRemove() {\n  try {\n    const config = {\n      method: 'POST',\n      headers: {\n        'Accept': 'application/json',\n        'Content-Type': 'application/json',\n      },\n      body: JSON.stringify({\n        \"operation\": \"productRemove\", \n        \"params\": { \"productId\": \"5b914211aaabdc51bf1839a9\" }\n      })\n    }\n    const response = await fetch('http://localhost:8000', config)\n    const data = await response.json()\n    console.log(data)\n  } catch (error) {\n    console.error(error)\n  }\n}\n```\n\nResult\n```json\n{\n  \"success\": true,\n  \"message\": \"Product removed successfully.\",\n  \"data\": {\n    \"n\": 1,\n    \"ok\": 1\n  }\n}\n```\n\n\n## Setup and Running\n- Prerequisites\n  - Node\n  - MongoDB\n\n- Clone repo `git clone git@github.com:atulmy/wispy.git wispy`\n\n- Configurations\n  - Create `.env` for API `cd api` and `cp .env.example .env`\n  - Modify `/api/.env` for PORT (optional)\n  - Modify `/web/.env` for PORT / API URL (optional)\n\n- Setup\n  - API: Install packages and database setup `cd api` and `npm run setup`\n  - Web: Install packages `cd web` and `npm install`\n\n- Running\n  - Run API `cd api` and `npm start`, API running at http://localhost:8000/\n  - Run Web `cd web` and `npm start`, browse web app at http://localhost:3000/\n\n- Change API to behave as RPC or REST or both\n  - Available modes: `{ \"rpc\", \"rest\", \"composite\" }`\n  - Set `endpoint.mode` in `api/src/setup/config/params.json` to one of available modes, eg: `composite`\n\n\n## Todo\n- [x] Execute operations\n- [x] Accept params and fields selection\n- [x] Inject authentication info to operations via middleware\n- [x] Option to expose operations as REST endpoints\n- [x] Query (read)\n- [x] Mutations (create/update/delete)\n- [x] Subscriptions (websocket)\n- [ ] Auto generate documentations\n\n\n## Authors\n- Atul Yadav - [GitHub](https://github.com/atulmy) · [Twitter](https://twitter.com/atulmy)\n\n\n## Support\nIf you found this project useful, kindly donate to support it ❤️\n\n[![Donate via PayPal](https://raw.githubusercontent.com/atulmy/atulmy.github.io/master/images/mix/paypal-me-smaller.png)](http://paypal.me/atulmy)\n\n\n## Hire me\nLooking for a developer to build your next idea or need a developer to work remotely? Get in touch: [atul.12788@gmail.com](mailto:atul.12788@gmail.com)\n\n\n## License\nCopyright (c) 2018 Atul Yadav http://github.com/atulmy\n\nThe MIT License (http://www.opensource.org/licenses/mit-license.php)\n","funding_links":["http://paypal.me/atulmy"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fatulmy%2Fwispy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fatulmy%2Fwispy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fatulmy%2Fwispy/lists"}