SurrealDB Logo SurrealDB Logo

Welcome to the tutorials repository for SurrealDB, with official and community contributed tutorials.

Official tutorials

To view the official tutorials and code walkthroughs, view the official folder in this repository.

Community tutorials

To view the community-submitted tutorials and code walkthroughs, view the community folder in this repository.

Contributing

If you would like to make a contribution, please follow the instructions in the Contribution Guidelines and then open a Pull-Request. One of the community managers will then evaluate your tutorial, and provide you with feedback.

Due to the way we set up the repo you only need to create ONE file, which is your guide itself (outside of images/assets). Please include the configuration bit in the top of the template file and don't change it's structure, else the github action will fail.

A complete list of requirements can be found in the Pull Request Template.


Official guides


Only SurrealDB staff are allowed to create guides here. If you have a guide which we think will fit in the official category, we will propose this to the surrealdb team for you.



Guides


Community guides


Please follow the contribution guidelines when creating a new guide:

  • A Pull Request will be rejected if it's subject is wider than about only one guide.
  • Please follow the template file and keep the top configuration in the same order. Otherwise the test will fail and it will not build.
  • If you think something can be done better about the repository, please open an issue first to discuss the matter! It will create a mess if everybody opens a PR for small improvements to be rejected.
  • Note: Because of the way we built the repo, your PR only needs to contain one file, which is the guide itself (outside of potential images). All other changes will be rejected.
  • More can be found in the Pull Request Template


Guides


Cloudflare worker with SurrealDB



Tutorial by Micha de Vries.
Edit this tutorial on GitHub.



I am building an open-source social media platform with cloudflare workers. Internally, we we're in debate between projects like Cloudflare D1 and Planetscale but eventually we stumbled upon SurrealDB. It really made a lot of sense for our project, so we went with it!

To be able to achieve this, we built our own database driver, which is also available via NPM.

Below follow a few examples on how to use the database drivers with cloudflare workers. In these examples we use the worker typescript template.

Connection variables from environment

import Surreal from '@theopensource-company/surrealdb-cloudflare';

// Type safety, typescript example :D
type Env = {
    HOST: string;
    USER: string;
    PASS: string;
    NAMESPACE: string;
    DATABASE: string;
}

// We can update the connection variables later on, as we don't have them available here just yet...
const db = new Surreal();

export default {
	async fetch(
		request: Request,
        env: Env
	): Promise<Response> {
        // If database not yet connected, let's update the connection details.
        if (!db.connected()) db.connect({
            host: env.HOST ?? '',
            username: env.USER ?? '',
            password: env.PASS ?? '',
            namespace: env.NAMESPACE ?? '',
            database: env.DATABASE ?? ''
        });

        // Example query: Retrieves all records from table 'table'.
        let res = await db.getRecords('table');
		console.log(res[0].result);

		return new Response("Hello World!");
	},
};

Connection variables defined in code

import Surreal from '@theopensource-company/surrealdb-cloudflare';

// We can update the connection variables later on, as we don't have them available here just yet...
const db = new Surreal({
    host: 'http://surreal.domain.com',
    username: 'root',
    password: 'password',
    namespace: 'awesome',
    database: 'example'
});

export default {
	async fetch(
		request: Request
	): Promise<Response> {
        // Example query: Retrieves all records from table 'table'.
        let res = await db.getRecords('table');
		console.log(res[0].result);

		return new Response("Hello World!");
	},
};

Strong type result

const res = await db.getRecords<{
    id: string;
    username: string;
    status: "verified" | "unverified";
}>('user');

res.forEach(record => {
    // Everything in "result" is now strong typed with the defined type.
    const { result: {
        id,
        username,
        status
    } } = record;
    console.log(`${id} - User ${username} is ${status}`);
});

Host with docker compose



Tutorial by Micha de Vries.
Edit this tutorial on GitHub.



As of writing this tutorial, Surreal Cloud is not yet available. For our project (which is currently heavily in development) we need to have an online instance of surrealdb available, so we have resided to a Hetzner VPS with docker-compose to keep it all clean and tidy.

What follows beyond here is a boiler plate with a very basic docker-compose instance, and the way we have deployed it.


Basic, no SSL

version: '3'

services:
  surrealdb:
    image: surrealdb/surrealdb:latest
    container_name: surrealdb
    restart: always
    command: start --user INSERT_USERNAME_HERE --pass INSERT_PASSWORD_HERE file:/data/database.db
    ports:
      - 8000:8000
    volumes:
      - ./data:/data

Our deployment

We don't need any automatic SSL generation as we use an SSL provided by cloudflare for our origin server, but feel free to add another template to this guide which automatically requests an SSL from Let's encrypt!

version: '3'

services:
  surrealdb:
    image: surrealdb/surrealdb:latest
    container_name: surrealdb
    restart: always
    command: start --user INSERT_USERNAME_HERE --pass INSERT_PASSWORD_HERE --web-crt /data/web.crt --web-key /data/web.key file:/data/database.db
    ports:
      - 443:8000
    volumes:
      - ./data:/data

How to use signin and signup



Tutorial by Koakh.
Edit this tutorial on GitHub.



Start InMemory Server

$ surreal start --log trace --user root --pass root

Option #1: SCHEMAFULL

launch surreal REPL to connect to local SurrealDB server instance

$ surreal sql --conn http://localhost:8000 --user root --pass root --ns test --db test --pretty

now paste bellow surrealQL code block to create the schemafull scheme

---define SCHEMAFULL and PERMISSIONS
DEFINE TABLE user SCHEMAFULL
  PERMISSIONS
    FOR select, update WHERE id = $auth.id, 
    FOR create, delete NONE;
--- define FIELD's
DEFINE FIELD user ON user TYPE string;
DEFINE FIELD pass ON user TYPE string;
DEFINE FIELD settings.* ON user TYPE object;
DEFINE FIELD settings.marketing ON user TYPE string;
DEFINE FIELD tags ON user TYPE array;
--- define INDEX's
DEFINE INDEX idx_user ON user COLUMNS user UNIQUE;

-- define SCOPE
DEFINE SCOPE allusers
  -- the JWT session will be valid for 14 days
  SESSION 14d
  -- The optional SIGNUP clause will be run when calling the signup method for this scope
  -- It is designed to create or add a new record to the database.
  -- If set, it needs to return a record or a record id
  -- The variables can be passed in to the signin method
  SIGNUP ( CREATE user SET settings.marketing = $marketing, user = $user, pass = crypto::argon2::generate($pass), tags = $tags )
  -- The optional SIGNIN clause will be run when calling the signin method for this scope
  -- It is designed to check if a record exists in the database.
  -- If set, it needs to return a record or a record id
  -- The variables can be passed in to the signin method
  SIGNIN ( SELECT * FROM user WHERE user = $user AND crypto::argon2::compare(pass, $pass) )
  -- this optional clause will be run when calling the signup method for this scope

check info db in REPL

> INFO FOR DB;
[
  {
    "result": {
      "dl": {},
      "dt": {},
      "sc": {
        "allusers": "DEFINE SCOPE allusers SESSION 2w SIGNUP (CREATE user SET settings.marketing = $marketing, user = $user, pass = crypto::argon2::generate($pass), tags = $tags) SIGNIN (SELECT * FROM user WHERE user = $user AND crypto::argon2::compare(pass, $pass))"
      },
      "tb": {
        "user": "DEFINE TABLE user SCHEMAFULL PERMISSIONS FOR select WHERE id = $auth.id, FOR create NONE, FOR update WHERE id = $auth.id, FOR delete NONE"
      }
    },
    "status": "OK",
    "time": "153.601µs"
  }
]

Option #2 : SCHEMALESS

another nice option suggested by George Shammas / georgyo

DEFINE SCOPE allusers 
SESSION 14d
SIGNUP (
  CREATE type::thing("user", string::lowercase(string::trim($user)))
  SET pass = crypto::argon2::generate($pass)
)
SIGNIN (
  SELECT * FROM type::thing("user", string::lowercase(string::trim($user)))
  WHERE crypto::argon2::compare(pass, $pass)
)

Because we are reusing the id, the username unique constraint is enforced even in a SCHEMALESS table.

However this also means that username will not be changeable in the future as all relationships will be using the old username as the key.

check info db in REPL

> INFO FOR DB;
[
  {
    "result": {
      "dl": {},
      "dt": {},
      "sc": {
        "allusers": "DEFINE SCOPE allusers SESSION 2w SIGNUP (CREATE type::thing(\"user\", string::lowercase(string::trim($user))) SET pass = crypto::argon2::generate($pass)) SIGNIN (SELECT * FROM type::thing(\"user\", string::lowercase(string::trim($user))) WHERE crypto::argon2::compare(pass, $pass))"
      },
      "tb": {
        "user": "DEFINE TABLE user SCHEMALESS PERMISSIONS NONE"
      }
    },
    "status": "OK",
    "time": "93.978µs"
  }
]

> SELECT * FROM user;
[
  {
    "result": [
      {
        "id": "user:tobie",
        "pass": "$argon2id$v=19$m=4096,t=3,p=1$oFQ8a4HFXjfaadQcOdPlLw$MZrA6mNvtvFLuQq1GGhfrGs4wk9iflhKT2rRsZLxwE4"
      }
    ],
    "status": "OK",
    "time": "125.017µs"
  }
]

Test signin and signup with Javascript surrealdb.js integration

use following query from the client to signup a new user

db.signup({
  NS: 'my_ns',
  DB: 'my_db',
  // We want to signup to the 'allusers' scope defined above
  SC: 'allusers',
  // We can add any variable here to use in the SIGNUP clause
  user: 'tobie',
  // We can add any variable here to use in the SIGNUP clause
  pass: 'some password',
  // We can add any variable here to use in the SIGNUP clause
  marketing: true,
  // We can add any variable here to use in the SIGNUP clause
  tags: ['rust', 'golang', 'javascript'],
});

you should receive a jwt token for ex

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJpYXQiOjE2NjQyMjQxMjEsIm5iZiI6MTY2NDIyNDEyMSwiZXhwIjoxNjY1NDMzNzIxLCJpc3MiOiJTdXJyZWFsREIiLCJucyI6InRlc3QiLCJkYiI6InRlc3QiLCJzYyI6ImFsbHVzZXJzIiwiaWQiOiJ1c2VyOmQ4dzR4aHZtY2hxcHh6enl5ZjUyIn0.TWjIEm8z2TeuE27uJ9MvgfCELvOT0hC6e8vlOPUNJMx-lvxYXIYgXXcibdVxEWNQqnXu1gaUOus4KgjsWaUD-A

and signin an existing user

db.signin({
  NS: 'my_ns',
  DB: 'my_db',
  // We want to signup to the 'allusers' scope defined above
  SC: 'allusers',
  // We can add any variable here to use in the SIGNUP clause
  user: 'tobie',
  // We can add any variable here to use in the SIGNUP clause
  pass: 'some password',
});

you should receive a jwt token for ex

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJpYXQiOjE2NjQyMjQxNjMsIm5iZiI6MTY2NDIyNDE2MywiZXhwIjoxNjY1NDMzNzYzLCJpc3MiOiJTdXJyZWFsREIiLCJucyI6InRlc3QiLCJkYiI6InRlc3QiLCJzYyI6ImFsbHVzZXJzIiwiaWQiOiJ1c2VyOmQ4dzR4aHZtY2hxcHh6enl5ZjUyIn0.kKx_WShYBf9T_sCa_pRldHTG9LyfIx-4q0V4sz2kKzxSG4b_okG-MddTwdqHnsdS7rEJxZ-Gp-iP4cpgo77kqQ

Running a SurrealDB Instance on Fly.io



Tutorial by Mark Lussier.
Edit this tutorial on GitHub.



These are the steps for getting a surrealdb instance running on fly.io and connecting to it. This is heavily based off of steps posted in the surrealdb discord by rvdende. View Original Post.

These steps work just fine for the hobby (pay as you go) plan.

HEADS UP By default this will create an instance on a single shared cpu with 256m memory. This is pretty darn small to do anything but experiment, but its free. You can scale your instance to something more useable by visiting the https://fly.io/apps/<appname>/scale. Obviously scaling to larger instances will incur higher costs, please refer to fly.io pricing

Installing fly.io client

source: fly.io docs

brew install flyctl

Optionally create a free account if needed

source: fly.io docs

flyctl auth signup

Login to your fly.io account

source: fly.io docs

flyctl auth login

Create a Dockerfile to run SurrealDB

Create a directory locally, cd into it and create a new Dockerfile. Note we aren't providing a root user username/password to the start command, we will be providing those as environment variables.

FROM surrealdb/surrealdb:latest
EXPOSE 8080
CMD ["start", "--bind", "0.0.0.0:8080", "file://data/srdb.db"]

Launch your Instance.. sort of :)

fly launch

You will be prompted with various questions. Give your app a memorable name since it will be used in the URL. Pick a region close to you and most importantly, answer NO when prompted if you wish to deploy immediately.

Create a volume

We need to create a volume for surreal to write the database to. Lets create a small 1g volume. Right now you can not scale a volume (you need to create a new one), but fly.io is working on supporting this. You can get up a total of 3g of persistent volume storage (total) for free.

fly volumes create data --region <region> --size 1

Now lets edit the fly.toml file that was created by fly launch. Put this at the end, no indentations or spaces.

[mounts]
source="data"
destination="/data"

Secrets

Now lets add our username and password as secrets. You can see the environment variables surrealdb supports by running surreal start -h

fly secrets set USER=root
fly secrets set PASS=<passwordhere>

Let's Deploy

fly deploy

Connecting

Now that your surrealdb instance is up and running, you need to be able to connect to it. Even though we exposed port 8080 in the Dockerfile, fly will map that appropriatly to port 80/443.

If you're connecting via a websocket client library, surrealdb-go in my case, you would connect with the following. Note the use of wss://

db, err := surrealdb.New("wss://<appname>.fly.dev/rpc")
if err != nil {
	fmt.Printf("error creating socket: %s", err)
	os.Exit(2)
}

Surreal sql Command

If you're connecting via the surreal cli, use a connection string with --conn https://<appname>.fly.dev. Please don't forget to pass --user/--pass with the values you used when creating secrets.

Third Party tools such as rvdende's surrealreact

When creating a connection, specify the host as https://<appname>.fly.dev/rpc