{"id":23507307,"url":"https://github.com/pj/vrpc","last_synced_at":"2025-06-27T14:08:07.147Z","repository":{"id":34885731,"uuid":"161001922","full_name":"pj/vrpc","owner":"pj","description":"Versioned RPC: Version control for types and service stubs.","archived":false,"fork":false,"pushed_at":"2023-01-04T21:45:52.000Z","size":5478,"stargazers_count":1,"open_issues_count":13,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-05-12T17:23:21.665Z","etag":null,"topics":["merkle-tree","schema","versioning","versioning-api"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pj.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-12-09T03:51:36.000Z","updated_at":"2020-06-23T23:12:59.000Z","dependencies_parsed_at":"2023-01-15T10:00:41.868Z","dependency_job_id":null,"html_url":"https://github.com/pj/vrpc","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/pj/vrpc","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pj%2Fvrpc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pj%2Fvrpc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pj%2Fvrpc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pj%2Fvrpc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pj","download_url":"https://codeload.github.com/pj/vrpc/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pj%2Fvrpc/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":262270427,"owners_count":23285165,"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":["merkle-tree","schema","versioning","versioning-api"],"created_at":"2024-12-25T10:17:25.868Z","updated_at":"2025-06-27T14:08:07.126Z","avatar_url":"https://github.com/pj.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# VRPC\n\nVRPC (versioned rpc) is a tool to generate types and service definitions that are versioned e.g. Order_V0, Order_V2 or Post_V1. This is intended to help manage forwards and backwards compatibility and make it easier to evolve software as external interfaces change.\n\nConceptually you can think of it as working like a version control system, but for the types in your software rather than just files. Changes to your types are stored as a series of change actions e.g. \"add a field\", \"add a service version\" rather than as a single static definition file. Each action potentially produces a new type or service version to be generated. Underneath it uses something like a merkle tree, where every change to a type is hashed to ensure that past definitions can't be changed.\n\nUltimately the goal would be to version all the external interfaces of a service, including things like files and the database.\n\n## Status\n\nThis is still extremely experimental and there's still a lot of work to do to make this truly useful. For now I've moved on to other things and it might be awhile before I continue work on this.\n\n## Example of the concept.\n\n**NB: Not all of this is working yet.**\n\nThe current definitions are stored in a backend file that is used to hold the current state of the definition and the log of changes generated from previous versions of it.\n\nTo define a service, first you need to define some types to use for the service:\n\n````json\n[\n    {\n        \"name\": \"OrderQuery\",\n        \"description\": \"Query orders from the database\",\n        \"fields\": [\n            {\n                \"name\": \"username\",\n                \"description\": \"username of the person ordering\",\n                \"_type\": \"string\",\n                \"changeLog\": \"Front end needs to be able to look up a persons orders\"\n            },\n            {\n                \"name\": \"total\",\n                \"description\": \"get orders with a specific value\",\n                \"_type\": \"float\",\n                \"optional\": true,\n                \"changeLog\": \"Need this for looking up specific orders by value on the admin page\"\n            }\n        ]\n    },\n    {\n        \"name\": \"OrderQueryResult\",\n        \"description\": \"Result of an OrderQuery\",\n        \"fields\": [\n            {\n                \"name\": \"id\",\n                \"description\": \"Id of the order\",\n                \"_type\": \"integer\",\n                \"changeLog\": \"Useful for display\"\n            },\n            {\n                \"name\": \"total\",\n                \"description\": \"Total value of the order\",\n                \"_type\": \"float\",\n                \"changeLog\": \"User needs to see value of the order.\"\n            }\n        ]\n    }\n]\n````\n\nRun the commit command to add the definition changes to the backend file:\n\n````\nvrpc update backend.json vrpc_definitions.json\n````\n\nFor now this has to be done as a step prior to adding the service definitions, to make sure that the types have been added to the backend.\n\nAdd the service definition to the bottom of vrpc_definitions.json using the types defined above:\n\n````json\n[\n    ****Snip type definitions****\n    {\n        \"name\": \"OrderQueryService\",\n        \"description\": \"Service for querying Orders\",\n        \"versions\": [\n            {\n                \"_from\": {\n                    \"name\": \"OrderQuery\",\n                    \"version\": 0\n                },\n                \"to\": {\n                    \"name\": \"OrderQueryResult\",\n                    \"version\": 0\n                },\n                \"changeLog\": \"Necessary to support the order querying feature\"\n            }\n        ]\n    }\n]\n````\n\nRun the commit command again to add the service changes to the backend:\n\n````\nvrpc update backend.json vrpc_definitions.json\n````\n\nGenerate the stubs for the types and the service:\n\n````\nvrpc generate backend.json src/generated\n````\n\nDefine your service by passing in an express app to the service definition function along with the definition of the service:\n\n````typescript\nimport {OrderQuery, OrderQueryResult, OrderQuery_V0, OrderQueryResult_V0} from './generated/types';\nimport {OrderQueryServiceExpress} from './generated/services';\n\nimport express from 'express';\n\nconst app = express();\napp.use(express.json());\n\nOrderQueryServiceExpress(\n    app, \n    {\n        V0: {\n            \"OrderQueryResult_V0\": async (input: OrderQuery_V0) Promise\u003cOrderQueryResult_V0\u003e =\u003e { \n            let result;\n            if (input.total){\n                result = query(\"Select * from orders WHERE username = ? AND total = ?\", input.username, input.total);\n            } else {\n                result = query(\"Select * from orders WHERE username = ?\", input.username);\n            }\n\n            return new OrderQueryResult_V0(result.id, result.total);\n        }\n        }\n    }\n);\n````\n\nTo query the service you can use the generated client:\n\n````typescript\nimport * as client from './generated/client';\n\nasync function queryOrders(username: string) {\n    return await client.OrderQueryService.V0.OrderQuery_V0(username);\n}\n````\n\n## TODO\n\n- [] Test CLI.\n- [] Package and put on NPM.\n- [] Options to control what services and types are generated, rather than just generating them all.\n- [] Options to control generating a union type for a group of service inputs.\n- [] Handle forward compatibility in client.\n- [] Simplify definition of services by spreading input, rather than passing message object.\n- [] Service and type deprecation and corresponding changes to generation logic.\n- [] Force changelog when updating types and services.\n- [] New types:\n    - [] arrays\n    - [] enums\n    - [] intersections\n    - [] unions\n- [] Add HTTP methods for services, possibly by adding fields to types and services for controlling the generation process.\n- [] Fix HTTP codes in generated services, e.g. 400 for misformatted requests, or 410 for deleted versions.\n- [] Fix types in generated typescript services.\n- [] Remove duplication in code related to handling services and types.\n- [] Define services at the same time as the types they depend on.\n- [] Generate other languages e.g. protobufs, json schema, python etc.\n- [] Metalog format to define generatables and structures as part of log.\n- [] Database access framework e.g. for Postgres, Mongo etc.\n\n## Authors\n\n- **Paul Johnson** - [pj](https://github.com/pj)\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpj%2Fvrpc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpj%2Fvrpc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpj%2Fvrpc/lists"}