Warning
This is a WIP, but we are currently using it in production at Siteforge to help ensure type safety in our SurrealDB queries. See the Features Supported section for a list of features we currently support.
Warning
We haven't currently setup a build automation system, so you must build via the manual installation instructions below.
You must have the rust toolchain installed, then run:
cargo install --git https://github.com/siteforge-io/surreal-codegen.gitOr, if you have cloned the repo:
cargo install --path surreal-codegensurreal-codegen --helpUsage: surreal-codegen [OPTIONS] --dir <DIR> --schema <SCHEMA>
Options:
-d, --dir <DIR> The directory containing the Surql files
-s, --schema <SCHEMA>
-o, --output <OUTPUT> The name of the output file default of `types.ts` [default: ./types.ts]
--header <HEADER> Header to add to the top of the output file If you specify this, you must import in RecordId type and a Surreal class that has a .query(query: string, variables?: Record<string, unknown>) method [default: "import { type RecordId, Surreal } from 'surrealdb'"]
-h, --help Print help
./schema.surql
DEFINE TABLE user SCHEMAFULL;
DEFINE FIELD id ON user TYPE string;
DEFINE FIELD email ON user TYPE string
VALUE string::lowercase($value)
ASSERT string::is::email($value);
DEFINE FIELD password ON user TYPE string
VALUE crypto::bcrypt::generate($value);
DEFINE FIELD name ON user TYPE string
VALUE string::trim($value);
DEFINE FIELD created_at ON user TYPE datetime
VALUE time::now()
READONLY;./queries/create_user.surql
CREATE user CONTENT $user;This wil generate a types.ts file in the current directory, which includes all your queries, as well as some prototype and type overrides for the SurrealDB database to allow you to use the generated types in your TypeScript code.
surreal-codegen \
--schema ./schema.surql \
--dir ./queries \
--output ./queries.tsimport { TypedSurreal, CreateUserQuery } from "./queries"
const db = new TypedSurreal()
await db.connect(...)
/*
Result is typed as CreateUserResult from the generated types.ts file
*/
const [created_users] = await db.typed(CreateUserQuery, {
user: {
name: "John Doe",
email: "john@doe.com",
password: "123456",
} // can also be an array of users
})We exploit the SurrealDB casting system to infer the types of parameters, for places where they cannot be inferred from the query itself.
All you must do is add a casting annotation with the parameter name, eg:
-- Casting syntax in SurrealDB.
<string> $email;This will allow the codegen to infer the type of $email variable as a string.
./queries/reset_password.surql
<record<user>> $user;
<string> $password;
UPDATE ONLY $user
SET password = $passwordYou can also define global parameters in a global.surql file, which will be available to all queries in the directory, this is useful things like typing the $auth parameters available in SurrealDB across all queries.
./queries/globals.surql
<record<user>> $auth;You can override the default imported classes by specifying the --header option. You must include a RecordID type import, and a Surreal class that contains
a .query(query: string, variables?: Record<string, unknown>) method.
You can also use this to specify a comment to be added to the top of the generated file, such as ESLint ignore comments. Or alternatively, you can ignore the generated file by including the file in your eslint ignore list.
surreal-codegen \
--schema ./schema.surql \
--dir ./queries \
--output ./queries.ts \
--header "import { RecordId, Surreal } from 'my-custom-surreal-class'"- We only currently support
SCHEMAFULLtables so far, but we are working on supporting other table types.
-
never -
unknown -
string -
int -
float -
datetime -
duration -
decimal -
bool -
record<table> -
option<type> -
array<type> -
object -
number -
NULL -
NONE(forOption) -
any -
foo | barUnions (mixed return type unions) - Surreal 2.0 typed literals (eg:
"foo",123,1d,{ foo: 123 },array<1|2>) - GEOJson types (eg:
point,line,polygon) - Typed
idrecord ID values for tables, eg:DEFINE FIELD id ON user TYPE string
-
RETURN { foo: 1, bar: 2 }
-
WHERE foo = $barparameter inference -
fn::foo($bar)function calling parameter inference
-
*all fields -
foo.barfield access -
foo as barfield alias -
foo.{bar, baz}destructuring access. -
FROMtargets -
VALUE -
GROUP BY -
GROUP ALL -
SPLITfields -
FETCHfields
-
FROMtargets -
RETURN BEFORE -
RETURN AFTER -
RETURN DIFF -
RETRUN @statement_paramwith$beforefield access
-
INSERT INTO baz $fooparameter inference -
INSERT INTO baz { foo: $bar }parameter inference -
INSERT INTO baz ... ON DUPLICATE KEY UPDATE foo = $barparameter inference
- TODO
-
DEFINE TABLE foo AS SELECT ... FROM bar -
DEFINE TABLE foo AS SELECT ... FROM bar GROUP BY ... -
DEFINE TABLE foo AS SELECT ... FROM bar GROUP ALL
-
RETURN BEFORE -
RETURN AFTER -
RETURN DIFF -
RETRUN @statement_paramwith$beforeand$afterfield access -
CONTENT $fooparameter inference -
CONTENT { foo: $bar }parameter inference -
SET foo = $barparameter inference -
MERGE $barparameter inference -
MERGE { foo: $bar }parameter inference -
PATCH ...parameter inference
-
CREATE baz SET foo = $barparameter inference -
CREATE baz CONTENT { foo: $bar }parameter inference -
CREATE baz CONTENT $fooparameter inference -
RETURN BEFORE -
RETURN AFTER -
RETURN DIFF -
RETRUN @statement_paramwith$afterfield access
-
RETURN BEFORE -
RETURN AFTER -
RETURN DIFF -
RETRUN @statement_paramwith$afterfield access -
CONTENT $fooparameter inference -
SET foo = $barparameter inference -
MERGE { foo: $bar }parameter inference -
CONTENT { foo: $bar }parameter inference -
MERGE $fooparameter inference -
PATCH ...parameter inference
-
foo.bar -
foo.*for arrays -
foo.*for objects -
foo[0] - edge traversal eg:
foo->bar<-baz
-
true -
false -
null -
"string" -
123 -
123.456 -
[1, 2, 3] -
{"foo": "bar"}
-
foo == "bar" -
foo != "bar" -
foo < "bar" -
foo <= "bar" -
foo > "bar" -
foo >= "bar"
-
SELECTstatements -
DELETEstatements -
INSERTstatements -
UPDATEstatements -
CREATEstatements -
RELATEstatements -
UPSERTstatements
- Custom global
$paramdefinitions in aglobal.surqlfile-
$auth -
$session -
$scope -
$input -
$token
-
- built-in parameters
-
$this -
$parent -
$after -
$before
-
- Automatic parameter inference in some cases
-
IF ELSE -
FOR -
CONTINUE -
BREAK -
RETURN -
BEGIN -
COMMIT -
LET -
ABORT -
THROW
-
LETstatement
-- If we can't infer the type of the `LET` statement
-- you can use a type annotation
LET $id: record<foo> = $foo.id;
UPSERT ONLY $id CONTENT $foo;
We welcome contributions to this project, please see our Contributing Guide for more information.
This project is licensed under the MIT License.