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.git
Or, if you have cloned the repo:
cargo install --path surreal-codegen
surreal-codegen --help
Usage: 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.ts
import { 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: "[email protected]",
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 = $password
You 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
SCHEMAFULL
tables 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 | bar
Unions (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
id
record ID values for tables, eg:DEFINE FIELD id ON user TYPE string
-
RETURN { foo: 1, bar: 2 }
-
WHERE foo = $bar
parameter inference -
fn::foo($bar)
function calling parameter inference
-
*
all fields -
foo.bar
field access -
foo as bar
field alias -
foo.{bar, baz}
destructuring access. -
FROM
targets -
VALUE
-
GROUP BY
-
GROUP ALL
-
SPLIT
fields -
FETCH
fields
-
FROM
targets -
RETURN BEFORE
-
RETURN AFTER
-
RETURN DIFF
-
RETRUN @statement_param
with$before
field access
-
INSERT INTO baz $foo
parameter inference -
INSERT INTO baz { foo: $bar }
parameter inference -
INSERT INTO baz ... ON DUPLICATE KEY UPDATE foo = $bar
parameter 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_param
with$before
and$after
field access -
CONTENT $foo
parameter inference -
CONTENT { foo: $bar }
parameter inference -
SET foo = $bar
parameter inference -
MERGE $bar
parameter inference -
MERGE { foo: $bar }
parameter inference -
PATCH ...
parameter inference
-
CREATE baz SET foo = $bar
parameter inference -
CREATE baz CONTENT { foo: $bar }
parameter inference -
CREATE baz CONTENT $foo
parameter inference -
RETURN BEFORE
-
RETURN AFTER
-
RETURN DIFF
-
RETRUN @statement_param
with$after
field access
-
RETURN BEFORE
-
RETURN AFTER
-
RETURN DIFF
-
RETRUN @statement_param
with$after
field access -
CONTENT $foo
parameter inference -
SET foo = $bar
parameter inference -
MERGE { foo: $bar }
parameter inference -
CONTENT { foo: $bar }
parameter inference -
MERGE $foo
parameter 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"
-
SELECT
statements -
DELETE
statements -
INSERT
statements -
UPDATE
statements -
CREATE
statements -
RELATE
statements -
UPSERT
statements
- Custom global
$param
definitions in aglobal.surql
file-
$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
-
LET
statement
-- 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.