/surreal-codegen

SurrealDB code generation library inspired by GraphQL codegen tooling

Primary LanguageRustMIT LicenseMIT

surreal-codegen

Warning

This is a work in progress, but we are currently using it in production at Siteforge to help ensure type safety in our SurrealDB queries.

Installation

Note

Currently I haven't published this as a easily installable dependency, so you will need to git clone this repo and build it yourself.

  1. Clone this repo
git clone https://github.com/siteforge-io/surreal-codegen.git
  1. Build the binary
cargo install --path ./surreal-codegen
  1. Run the binary
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]
  -h, --help             Print help

Usage

Schema Example

./schema.surql

DEFINE TABLE user SCHEMAFULL;
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;

Query Example

./queries/create_user.surql

CREATE user CONTENT $user;

Codegen

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

TypeScript usage

import { TypedSurreal, CreateUserQuery } from "./queries"

const db = new TypedSurreal({
  ...
});

/*
  Result is typed as CreateUserResult from the generated types.ts file
*/
const result = await db.typed(CreateUserQuery, {
  user: {
    name: "John Doe",
    email: "john@doe.com",
    password: "123456",
  } // can also be an array of users
});

Typing parameters

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.

Example:

./queries/reset_password.surql

<record<user>> $user;
<string> $password;

UPDATE ONLY $user
  SET password = $password

Global parameters

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>> $user;

Notes

  • We only currently support SCHEMAFULL tables so far, but we are working on supporting other table types.

Features Supported So Far

General Type Support and Handling

  • Never
  • Unknown
  • String
  • Int
  • Float
  • Datetime
  • Duration
  • Decimal
  • Bool
  • Record
  • Option
  • Array
  • Object
  • Number
  • Null (for Option)
  • Any
  • None
  • Either (mixed return types)

Objects

  • RETURN { foo: 1, bar: 2 }

Automatic Parameter Inference

  • WHERE foo = $bar parameter inference
  • fn::foo($bar) parameter inference
  • CREATE baz SET foo = $bar parameter inference
  • CREATE baz CONTENT { foo: $bar } parameter inference
  • CREATE baz CONTENT $foo parameter inference
  • UPDATE baz SET foo = $bar parameter inference
  • UPDATE baz CONTENT $foo parameter inference
  • UPDATE baz MERGE $foo parameter inference
  • UPDATE baz MERGE { foo: $bar } parameter inference
  • UPSERT baz SET foo = $bar parameter inference

SELECT statements

  • All fields
  • Fields
  • Fields with aliases
  • FROM targets
  • VALUE
  • GROUP BY
  • GROUP ALL
  • SPLIT fields
  • FETCH fields

DELETE statements

  • FROM targets
  • RETURN BEFORE
  • RETURN AFTER
  • RETURN DIFF
  • RETRUN @statement_param with $before field access

INSERT statements

  • TODO

RELATE statements

  • TODO

DEFINE TABLE .. AS precomputed tables

  • 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

UPDATE statements

  • RETURN BEFORE
  • RETURN AFTER
  • RETURN DIFF
  • RETRUN @statement_param with $before and $after field access
  • CONTENT { foo: $bar } parameter inference
  • CONTENT $foo parameter inference
  • SET foo = $bar parameter inference
  • MERGE $bar parameter inference
  • MERGE { foo: $bar } parameter inference

CREATE statements

  • RETURN BEFORE
  • RETURN AFTER
  • RETURN DIFF
  • RETRUN @statement_param with $after field access
  • CONTENT { foo: $bar } parameter inference
  • CONTENT $foo parameter inference
  • SET foo = $bar parameter inference

UPSERT statements

  • TODO

Value expressions

Idiom/path expressions

  • foo.bar
  • foo.* for arrays
  • foo.* for objects
  • foo[0]
  • edge traversal eg: foo->bar<-baz

Literal/constant expressions

  • true
  • false
  • null
  • "string"
  • 123
  • 123.456
  • [1, 2, 3]
  • {"foo": "bar"}

Comparison expressions

  • foo == "bar"
  • foo != "bar"
  • foo < "bar"
  • foo <= "bar"
  • foo > "bar"
  • foo >= "bar"

Subquery expressions

  • SELECT statements
  • DELETE statements
  • INSERT statements
  • UPDATE statements
  • CREATE statements
  • RELATE statements

Parameter expressions

  • Custom global $param definitions in a global.surql file
    • $auth
    • $session
    • $scope
    • $input
    • $token
  • built-in parameters
    • $this
    • $parent
    • $after
    • $before
  • Automatic parameter inference in some cases

Other Statements

  • IF ELSE
  • FOR
  • CONTINUE
  • BREAK
  • RETURN
  • BEGIN
  • COMMIT
  • LET
  • ABORT
  • THROW