{"id":31979334,"url":"https://github.com/sorenholsthansen/surrealx","last_synced_at":"2026-03-02T04:03:22.519Z","repository":{"id":65746482,"uuid":"595295748","full_name":"SorenHolstHansen/SurrealX","owner":"SorenHolstHansen","description":"Typesafe SurrealDB client","archived":false,"fork":false,"pushed_at":"2023-03-02T21:54:08.000Z","size":106,"stargazers_count":45,"open_issues_count":5,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-24T12:27:08.485Z","etag":null,"topics":["surrealdb","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"isc","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/SorenHolstHansen.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,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-01-30T19:48:09.000Z","updated_at":"2025-08-17T16:10:00.000Z","dependencies_parsed_at":null,"dependency_job_id":"3ee6b96d-17f8-48a2-a2a0-6c2a09070eb1","html_url":"https://github.com/SorenHolstHansen/SurrealX","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/SorenHolstHansen/SurrealX","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SorenHolstHansen%2FSurrealX","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SorenHolstHansen%2FSurrealX/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SorenHolstHansen%2FSurrealX/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SorenHolstHansen%2FSurrealX/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/SorenHolstHansen","download_url":"https://codeload.github.com/SorenHolstHansen/SurrealX/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SorenHolstHansen%2FSurrealX/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29992286,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-02T01:47:34.672Z","status":"online","status_checked_at":"2026-03-02T02:00:07.342Z","response_time":60,"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":["surrealdb","typescript"],"created_at":"2025-10-14T22:50:11.707Z","updated_at":"2026-03-02T04:03:22.511Z","avatar_url":"https://github.com/SorenHolstHansen.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# SurrealX\n\nA strongly typed SurrealDB client.\n\nSurrealX is a CLI and library that generates a strongly typed client for\nSurrealDB queries from your running Surreal database. SurrealX extends the basic\nSurreal instance from the surrealdb package with `X` variants (e.g. `select`\nbecomes `selectX`) that is aware of your active tables in your database.\n\nFurthermore it provides a very basic migration setup. The SurrealDB team is\nworking on a built-in migration tool, so our migration tool is only prelimenary,\nand should probably not be used in production.\n\n## Example\n\nSay you have made the following queries to your Surreal database (possible\ncreated with our migration tool)\n\n```sql\n-- Schemaless table\nCREATE post SET title = \"My first post\";\n\n-- Schemafull table\nDEFINE TABLE user SCHEMAFULL;\nDEFINE FIELD age ON TABLE user TYPE int ASSERT $value != NONE;\nDEFINE FIELD name ON TABLE user TYPE object;\nDEFINE FIELD name.first ON TABLE user TYPE string ASSERT $value != NONE;\nDEFINE FIELD name.last ON TABLE user TYPE string;\nDEFINE FIELD comments ON TABLE user TYPE array;\nDEFINE FIELD comments.* ON TABLE user TYPE object ASSERT $value != NONE;\nDEFINE FIELD comments.*.id ON TABLE user TYPE string ASSERT $value = /^comment:.*/;\nDEFINE FIELD comments.*.title ON TABLE user TYPE string;\n```\n\nAnd then generate the client lib with `surrealx generate --output gen.ts`. Then\nyou will have a fully typechecked client lib that can do the following, where\nthe tablenames table records, update statements and so on are type checked.\n\n```typescript\n// gen.ts\nimport { Post, SurrealX, User } from \"./gen.ts\";\n\n/**\n * type Post = Record\u003cstring, unknown\u003e;\n *\n * type User = {\n *  age: number;\n *  comments?: {\n *      id?: string;\n *      title?: string;\n *  }[];\n *  name?: {\n *      first: string;\n *      last?: string;\n *  };\n * };\n */\n\n// SETUP\nconst db = new SurrealX(\"http://127.0.0.1:8000/rpc\");\nawait db.signin({ user: \"root\", pass: \"root\" });\nawait db.use(\"test\", \"test\");\n\n// selectX and selectAllX\nawait db.selectAllX(\"user\"); // type: User[]\nawait db.selectAllX(\"user:123\"); // typeError\nawait db.selectX(\"user:123\"); // type: User | undefined\nawait db.selectX(\"user\"); // typeError\nawait db.selectX(\"post:123\"); // type: Record\u003cstring, unknown\u003e\n\n// createX, with type checked data insert\nawait db.createX(\"user\", { age: 20, name: { first: \"Ben\" } }); // type: User\n\n// updateX and updateAllX, with type checked data insert\nawait db.updateX(\"user:123\", { age: 20, name: { first: \"Ben\" } }); // type: User\nawait db.updateAllX(\"user\", { age: 20, name: { first: \"Ben\" } }); // type: User[]\n\n// changeX and changeAllX, with type checked data insert (there are deep partial)\nawait db.changeX(\"user:123\", { name: { first: \"Ben\" } }); // type: User\nawait db.changeAllX(\"user\", { name: { first: \"Ben\" } }); // type: User[]\n\n// deleteX, with type checked table name, like the others\nawait db.deleteX(\"user:123\"); // type; void\n\n// modifyX, modifyAllX\nawait db.modifyX(\"user:123\", [{ op: \"replace\", path: \"/age\", value: 20 }]);\n\n// You can always remove the `X` from the end of the method, which will use the built in Surreal method\n```\n\n## Docs\n\nYou can either use surrealX as a CLI or a library (the bin is located in\n`./bin/mod.ts` and the library is exported from `./mod.ts`). The usage is very\nsimilar for both, so these docs show the CLI usage:\n\nYou can see how to use the CLI by running\n\n```\ndeno run https://deno.land/x/surrealx/bin/mod.ts --help\n```\n\nWhich will yield the following\n\n```\nsurrealx \u003ccmd\u003e [options]\n\nCommands:\n  surrealx migrate \u003csubcommand\u003e  Group of commands for creating and running migr\n                                 ations\n  surrealx generate              Generate a SurrealDB client from the database\n  surrealx database              Group of commands for interacting with the data\n                                 base\n\nOptions:\n      --version           Show version number                          [boolean]\n      --url               The url with which to connect with SurrealDB\n                                 [string] [default: \"http://127.0.0.1:8000/rpc\"]\n      --token             The token with which to connect with SurrealDB[string]\n  -u, --user                                          [string] [default: \"root\"]\n  -p, --pass, --password                              [string] [default: \"root\"]\n      --ns, --namespace                               [string] [default: \"test\"]\n      --db, --database                                [string] [default: \"test\"]\n      --help              Show help                                    [boolean]\n```\n\nFrom there you can either add migrations with `migrate add \u003cdescription\u003e`, run\npending migrations with `migrate run` or generate the surrealX client with\n`generate --output \u003coutput.file\u003e`. You can also reset your database with\n`database reset`.\n\n### Migrations\n\nTo add a new migration file run\n\n```\nsurrealx migrate add \u003cdescription\u003e\n```\n\nwhich will create a migration file with the name `\u003ctimestamp\u003e_\u003cdescription\u003e.sql`\n(e.g. `20230206192324_initial_migration.sql`). You can then write whatever\nSurrealDB statements you want. However because of the current implementation you\nHAVE TO END ALL YOUR STATEMENTS WITH SEMICOLONS;\n\nWe are working on making the migrations implementation better.\n\nAfter you have written all your statements, you can run any pending migrations\nwith\n\n```\nsurrealx migrate run\n```\n\n## Notes and considerations\n\n#### `[REPLACED]` bits in comments.\n\nTo every field in a tables type we add a comment specifying how the field was\ndefined. However, sometimes you might have defined a field like this\n\n```sql\nDEFINE FIELD comment ON TABLE post TYPE string ASSERT $value = /^comment:.*/;\n```\n\nor similar. The issue is that this includes the string `*/` which is the same as\nthe closing tag of the ts doc comment. There are currently\n[no workaround for this](https://github.com/microsoft/tsdoc/issues/166), so\nhence the `[REPLACED]`.\n\n#### `null` and `undefined`\n\nSurrealDB distinguishes between their `Null` and `None` type, which are similar\nto JS's `null` and `undefined`. For instance, if you have the following table\n\n```sql\nDEFINE TABLE user SCHEMAFULL;\nDEFINE FIELD age ON TABLE user TYPE int ASSERT $value != NONE;\nDEFINE FIELD name ON TABLE user TYPE string;\n```\n\nand then query it, you get the following results depending on what fields you\nquery for\n\n```typescript\n/**\n * Can return things like\n * [\n *   { age: 1, name: \"a\" },\n *   { age: 2 }\n * ]\n */\nawait db.query(\"SELECT * FROM user\");\n\n/**\n * Can return things like\n * [\n *   { age: 1, name: \"a\" },\n *   { age: 2, name: null }\n * ]\n */\nawait db.query(\"SELECT age, name FROM user\");\n\n/**\n * And even querying for a non-existing field can return things like\n * [\n *   { age: 1, nonexistent: null },\n *   { age: 2, nonexistent: null }\n * ]\n */\nawait db.query(\"SELECT age, nonexistent FROM user\");\n```\n\nSo depending on what fields you want to query you either get `null` or\n`undefined`. We could potentially support this in surrealX, but have not gotten\naround to it yet.\n\nIn the mean time we strongly suggest using `user.name == null` for checking if\nthings are `null` or `undefined` rather than `===` (potentially enforced using\nthe [eqeqeq linting rule](https://eslint.org/docs/latest/rules/eqeqeq)).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsorenholsthansen%2Fsurrealx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsorenholsthansen%2Fsurrealx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsorenholsthansen%2Fsurrealx/lists"}