---
title: "7: Managing the schema with SurrealKit | SurrealDB University"
description: "Keeping the schema in files with SurrealKit. A step in the SurrealDB movie database tutorial, with queries you can run."
url: https://surrealdb.com/learn/movies/surrealkit
---

![Course content preview](https://surrealdb.com/assets/static/header.C1-dPXT9.avif)

Course chapters

[Back to courses](https://surrealdb.com/learn) [Movie database tutorial](https://surrealdb.com/learn/movies) [1: Getting started](https://surrealdb.com/learn/movies/page-01) [2: Improving the existing data](https://surrealdb.com/learn/movies/page-02) [3: SurrealQL data types](https://surrealdb.com/learn/movies/page-03) [4: Adding a schema](https://surrealdb.com/learn/movies/page-04) [5: Adding and querying relations](https://surrealdb.com/learn/movies/page-05) [6: Defining users and permissions](https://surrealdb.com/learn/movies/page-06) [7: Managing the schema with SurrealKit](https://surrealdb.com/learn/movies/surrealkit) [All the queries from the tutorial](https://surrealdb.com/learn/movies/page-07)

# 7: Managing the schema with SurrealKit

Over the last six pages, our schema grew one statement at a time. Some tables were defined twice, first without permissions and later with `OVERWRITE` and a `PERMISSIONS` clause. If you wanted to build the same database again somewhere else, you would have to find every one of those statements and run them in the right order.

[SurrealKit](https://surrealdb.com/docs/manage/schema-migration) is a small command-line tool that solves this problem, and is one you will want to be familiar with when developing any project of a certain complexity.

With SurrealKit you write each `DEFINE` statement once, in its final form, in a `.surql` file. SurrealKit then makes a database match those files with a single command, loads your sample data, and runs tests to check that the schema still behaves the way you expect.

On this page we will move the movie database into a SurrealKit project. There are only four commands to learn: `init`, `sync`, `seed` and `test`.

## Installing SurrealKit

The quickest way to install SurrealKit is with [`cargo binstall`](https://github.com/cargo-bins/cargo-binstall), which downloads a prebuilt binary:

```bash
cargo binstall surrealkit
```

You can also download a binary for your platform from the [releases page](https://github.com/surrealdb/surrealkit/releases).

## Creating a project

In an empty folder, run:

```bash
surrealkit init --minimal
```

This creates a `database` folder with a few subfolders. We only need three of them on this page:

| Folder | What goes in it |
| --- | --- |
| `database/schema/` | The `DEFINE` statements, split into as many `.surql` files as you like |
| `database/seed/` | Statements that add data, such as `INSERT` and `CREATE` |
| `database/tests/suites/` | Tests, written as `.toml` files |

The `init` command also adds a placeholder file called `database/seed/seed.surql`. We can delete it, because we are about to add our own seed files.

## Moving the schema into files

We could use a single file, but for readability (and to show that multiple files can be used with SurrealKit) we will use one file per part of the database. Each `DEFINE` statement appears exactly once, in the form it had at the end of page 6. You do not need `OVERWRITE` anywhere, because SurrealKit adds it itself whenever it applies a file.

```text
database/schema/
├── functions.surql
├── movie.surql
├── params.surql
├── person.surql
├── relations.surql
└── users.surql
```

Here is `movie.surql`. Note that the permissions are part of the `DEFINE TABLE` statement from the start, so the table is only defined once.

```surql
DEFINE TABLE movie SCHEMAFULL TYPE NORMAL
    PERMISSIONS
        FOR select WHERE $auth.id IS NOT NONE
        FOR create, update, delete WHERE created_by = $auth.id;

DEFINE FIELD actors            ON TABLE movie TYPE array<string>;
DEFINE FIELD awards            ON TABLE movie TYPE option<string>;
DEFINE FIELD box_office        ON TABLE movie TYPE option<int>;
DEFINE FIELD directors         ON TABLE movie TYPE array<string>;
DEFINE FIELD dvd_released      ON TABLE movie TYPE option<datetime>;
DEFINE FIELD genres            ON TABLE movie TYPE array<string>;
DEFINE FIELD imdb_rating       ON TABLE movie TYPE option<int>;
DEFINE FIELD languages         ON TABLE movie TYPE array<string>;
DEFINE FIELD metacritic_rating ON TABLE movie TYPE option<int>;
DEFINE FIELD oscars_won        ON TABLE movie TYPE option<int>;
DEFINE FIELD plot              ON TABLE movie TYPE string;
DEFINE FIELD released          ON TABLE movie TYPE datetime;
DEFINE FIELD rt_rating         ON TABLE movie TYPE option<int>;
DEFINE FIELD runtime           ON TABLE movie TYPE duration;
DEFINE FIELD title             ON TABLE movie TYPE string;
DEFINE FIELD writers           ON TABLE movie TYPE array<string>;
DEFINE FIELD average_rating    ON TABLE movie COMPUTED math::mean([imdb_rating, metacritic_rating, rt_rating][WHERE $this IS NOT NONE]);
DEFINE FIELD poster            ON TABLE movie TYPE option<string> ASSERT $value.is_url();
DEFINE FIELD rated             ON TABLE movie TYPE option<string> ASSERT $value IN $RATINGS;
DEFINE FIELD created_by        ON TABLE movie TYPE option<record<user>> READONLY VALUE $auth.id;

DEFINE ANALYZER movie_fts TOKENIZERS class FILTERS ascii, lowercase, edgengram(2,10);
DEFINE INDEX plot_index  ON TABLE movie FIELDS plot  FULLTEXT ANALYZER movie_fts BM25 HIGHLIGHTS;
DEFINE INDEX title_index ON TABLE movie FIELDS title FULLTEXT ANALYZER movie_fts BM25;

DEFINE EVENT movie_activity ON TABLE movie WHEN $auth IS NOT NONE THEN {
    UPDATE $auth SET actions += { event: $event, input: $value, at: time::now() }
};
```

The other five files follow the same idea, with each statement in the form it had at the end of page 6. Open each one below and copy it into a file of the same name in `database/schema/`.

Alternatively, you could copy them all into a single file if you prefer.

**`functions.surql`** 

functions.surql

```surql
DEFINE FUNCTION fn::month_to_num($input: string) -> string {
    IF      $input = 'Jan' { '01' }
    ELSE IF $input = 'Feb' { '02' }
    ELSE IF $input = 'Mar' { '03' }
    ELSE IF $input = 'Apr' { '04' }
    ELSE IF $input = 'May' { '05' }
    ELSE IF $input = 'Jun' { '06' }
    ELSE IF $input = 'Jul' { '07' }
    ELSE IF $input = 'Aug' { '08' }
    ELSE IF $input = 'Sep' { '09' }
    ELSE IF $input = 'Oct' { '10' }
    ELSE IF $input = 'Nov' { '11' }
    ELSE IF $input = 'Dec' { '12' }
    ELSE {
        THROW "Invalid input: `" + $input + "`. Please use a three-letter abbreviation such as 'Oct'."
    }
};

DEFINE FUNCTION fn::date_to_datetime($input: string) -> datetime {
    LET $split = $input.split(' ');
    <datetime>($split[2] + '-' + fn::month_to_num($split[1]) + '-' + $split[0]);
};

DEFINE FUNCTION fn::get_imdb($obj: array<object>) -> option<int> {
    LET $score = $obj[WHERE Source = 'Internet Movie Database'][0].Score;
    IF $score IS NONE { NONE } ELSE { <int>(<number>$score.replace('/10', '') * 10) }
};

DEFINE FUNCTION fn::get_rt($obj: array<object>) -> option<int> {
    LET $score = $obj[WHERE Source = 'Rotten Tomatoes'][0].Score;
    IF $score IS NONE { NONE } ELSE { <int>$score.replace('%', '') }
};

DEFINE FUNCTION fn::get_metacritic($obj: array<object>) -> option<int> {
    LET $score = $obj[WHERE Source = 'Metacritic'][0].Score;
    IF $score IS NONE { NONE } ELSE { <int>$score.replace('/100', '') }
};
```

**`params.surql`** 

params.surql

```surql
DEFINE PARAM $GENRES VALUE (SELECT VALUE Genre FROM naive_movie)
    .map(|$m| $m.split(', '))
    .group();

DEFINE PARAM $RATINGS VALUE (SELECT VALUE Rated FROM naive_movie)
    .flatten()
    .group();
```

**`person.surql`** 

person.surql

```surql
DEFINE TABLE person SCHEMAFULL TYPE NORMAL
    PERMISSIONS
        FOR select WHERE $auth.id IS NOT NONE
        FOR create, update, delete WHERE created_by = $auth.id;

DEFINE FIELD name       ON TABLE person TYPE string;
DEFINE FIELD roles      ON TABLE person TYPE array<string> VALUE $value.distinct();
DEFINE FIELD created_by ON TABLE person TYPE option<record<user>> READONLY VALUE $auth.id;

DEFINE EVENT person_activity ON TABLE person WHEN $auth IS NOT NONE THEN {
    UPDATE $auth SET actions += { event: $event, input: $value, at: time::now() }
};
```

**`relations.surql`** 

relations.surql

```surql
DEFINE TABLE starred_in TYPE RELATION FROM person TO movie
    PERMISSIONS
        FOR select WHERE $auth.id IS NOT NONE
        FOR create, update, delete WHERE created_by = $auth.id;
DEFINE TABLE wrote TYPE RELATION FROM person TO movie
    PERMISSIONS
        FOR select WHERE $auth.id IS NOT NONE
        FOR create, update, delete WHERE created_by = $auth.id;
DEFINE TABLE directed TYPE RELATION FROM person TO movie
    PERMISSIONS
        FOR select WHERE $auth.id IS NOT NONE
        FOR create, update, delete WHERE created_by = $auth.id;

DEFINE FIELD created_by ON TABLE starred_in TYPE option<record<user>> READONLY VALUE $auth.id;
DEFINE FIELD created_by ON TABLE wrote      TYPE option<record<user>> READONLY VALUE $auth.id;
DEFINE FIELD created_by ON TABLE directed   TYPE option<record<user>> READONLY VALUE $auth.id;
```

**`users.surql`** 

users.surql

```surql
DEFINE USER owner  ON DATABASE PASSWORD "owner"  ROLES OWNER;
DEFINE USER editor ON DATABASE PASSWORD "editor" ROLES EDITOR;
DEFINE USER viewer ON DATABASE PASSWORD "viewer" ROLES VIEWER;

DEFINE TABLE user SCHEMAFULL
    PERMISSIONS FOR select WHERE $auth.id = id;

DEFINE FIELD name    ON TABLE user TYPE string;
DEFINE FIELD pass    ON TABLE user TYPE string;
DEFINE FIELD actions ON TABLE user TYPE option<array<object>> FLEXIBLE;

DEFINE INDEX unique_name ON TABLE user FIELDS name UNIQUE;

DEFINE ACCESS account ON DATABASE TYPE RECORD
    SIGNUP ( CREATE user SET name = $name, pass = crypto::argon2::generate($pass) )
    SIGNIN ( SELECT * FROM user WHERE name = $name AND crypto::argon2::compare(pass, $pass) )
    DURATION FOR TOKEN 15m, FOR SESSION 12h;
```

## Adding the data

Seed files hold the statements that add data. SurrealKit runs them in alphabetical order, so a number at the front of each name sets the order:

```text
database/seed/
├── 01_naive_movies.surql
├── 02_movies.surql
└── 03_people.surql
```

The first file is the [file of movie data](https://datasets.surrealdb.com/learn/book/book-naive-movies.surql) from page 1. Download it and save it as `database/seed/01_naive_movies.surql`. The other two are the `FOR` loops from pages 3 and 5, which turn each `naive_movie` into a `movie` and then create the `person` records and their relations:

**`02_movies.surql`** 

02_movies.surql

```surql
FOR $data in SELECT * FROM naive_movie {
    CREATE movie CONTENT {
        actors: $data.Actors.split(', '),
        awards: $data.Awards,
        box_office: IF $data.BoxOffice = 'N/A' { NONE } ELSE { <int>$data.BoxOffice.replace('$', '').replace(',', '') },
        directors: $data.Director.split(', '),
        dvd_released: IF $data.DVD = 'N/A' { NONE } ELSE { fn::date_to_datetime($data.DVD) },
        genres: $data.Genre.split(', '),
        imdb_rating: fn::get_imdb($data.Ratings),
        languages: $data.Language.split(', '),
        metacritic_rating: fn::get_metacritic($data.Ratings),
        plot: $data.Plot,
        poster: $data.Poster,
        rated: $data.Rated,
        released: fn::date_to_datetime($data.Released),
        rt_rating: fn::get_rt($data.Ratings),
        runtime: <duration>$data.Runtime.replace(' min', 'm'),
        title: $data.Title,
        writers: $data.Writer.split(', ')
    };
};
```

**`03_people.surql`** 

03_people.surql

```surql
FOR $movie IN SELECT * FROM movie {
  FOR $actor_name IN $movie.actors {
      LET $actor = (SELECT * FROM ONLY person WHERE name = $actor_name LIMIT 1);
      LET $actor = IF $actor IS NONE {
        CREATE person CONTENT { name: $actor_name, roles: ["actor"]}
      } ELSE {
          UPDATE $actor.id SET roles += "actor";
        $actor
      };
      RELATE $actor->starred_in->$movie;
  };
      FOR $writer_name IN $movie.writers {
      LET $writer = (SELECT * FROM ONLY person WHERE name = $writer_name LIMIT 1);
      LET $writer = IF $writer IS NONE {
        CREATE person CONTENT { name: $writer_name, roles: ["writer"]}
      } ELSE {
          UPDATE $writer.id SET roles += "writer";
        $writer
      };
      RELATE $writer->wrote->$movie;
  };
      FOR $director_name IN $movie.directors {
      LET $director = (SELECT * FROM ONLY person WHERE name = $director_name LIMIT 1);
      LET $director = IF $director IS NONE {
        CREATE person CONTENT { name: $director_name, roles: ["director"]}
      } ELSE {
          UPDATE $director.id SET roles += "director";
        $director
      };
      RELATE $director->directed->$movie;
  };
};
```

## Applying the schema with sync

We can now start a database in one terminal, as on page 6:

```bash
surreal start --user root --password secret
```

Now let's try applying the schema to a new database called `movies_kit` from the folder that holds `database/`.

```bash
surrealkit sync --user root --pass secret --ns main --db movies_kit
```

The first attempt fails partway through:

```text
applied schema/functions.surql
applied schema/movie.surql
error applying schema/params.surql: The table 'naive_movie' does not exist
error: default → default: The table 'naive_movie' does not exist
```

SurrealKit has found a problem that was easy to miss while we typed statements one by one. On page 3, `$GENRES` and `$RATINGS` were calculated from the `naive_movie` records with a `SELECT`, and a param stores the value it had at the moment it was defined. That only worked because the data was already there. A schema file has to work on an empty database, because the schema is applied before any data is added.

The fix is to write the two lists out in full. They come from the same query, so the values are the same as before:

```surql
DEFINE PARAM $GENRES VALUE ['Action', 'Adventure', 'Animation', 'Biography', 'Comedy', 'Crime', 'Drama', 'Family', 'Fantasy', 'Film-Noir', 'History', 'Horror', 'Music', 'Musical', 'Mystery', 'Romance', 'Sci-Fi', 'Thriller', 'War', 'Western'];
DEFINE PARAM $RATINGS VALUE ['Approved', 'G', 'Not Rated', 'PG', 'PG-13', 'Passed', 'R', 'TV-PG', 'Unrated', 'X'];
```

Let's now save `params.surql` and run the same `sync` command again. SurrealKit skips the two files it has already applied, because their contents have not changed, and applies the rest. The output is a bit more interesting than expected:

```text
applied schema/params.surql
applied schema/person.surql
applied schema/relations.surql
schema/users.surql: line 14: record access `account` has no JWT key, so SurrealDB generates a new random one each time it is applied with OVERWRITE, and every session token it issued stops working. Give it a stable key, e.g. `WITH JWT ALGORITHM HS512 KEY ${JWT_SECRET}`
applied schema/users.surql
```

The warning is another thing that SurrealKit checks for you. A record access method with no key gets a new random one every time it is redefined, which signs every record user out. That doesn't matter for us when practising, but on a real database you would add a key of your own.

## Adding the data with seed

We can now load the data:

```bash
surrealkit seed --user root --pass secret --ns main --db movies_kit
```

```text
Seeding from ./database/seed (3 files found)
  executing seed/01_naive_movies.surql
  executing seed/02_movies.surql
  executing seed/03_people.surql
Seeded 3 file(s); 0 unchanged
```

The database now has 146 movies and 628 people, the same as the one that we built by hand. SurrealKit remembers which seed files it has run, so running `seed` a second time does nothing unless you have changed a file.

## Changing the schema

From now on, a change to the schema is done through a change to a file. For example, the `movie` table has an `oscars_won` field, but nothing in the tutorial ever fills it in, so we can remove it. Let's delete its line from `movie.surql`, and then add `--dry-run` to see what `sync` would do without changing anything:

```bash
surrealkit sync --dry-run --user root --pass secret --ns main --db movies_kit
```

```text
DRY RUN: would apply schema/movie.surql
DRY RUN: would prune 1 stale managed entities
  REMOVE FIELD IF EXISTS oscars_won ON movie;
```

Because the field is no longer in any file, SurrealKit will remove it from the database. Run the command again without `--dry-run` to make the change.

`sync` makes the database match the files exactly, including removing what you deleted from them. That is what you want on a database of your own like this one. For a database that other people or apps depend on, SurrealKit has a slower, reviewed path, which the [schema course](https://surrealdb.com/learn/schemas/page-10) covers.

## Testing the database

The last command we'll take a look at is `test`. A test file describes some queries, which user should run them, and what they should return. Here is `database/tests/suites/movies.toml`, which checks a few of the permissions from page 6:

```toml
name = "movies"

[actors.viewer]
kind = "database"
username = "viewer"
password = "viewer"

[actors.user]
kind = "record"
access = "account"
signup_params = { name = "user", pass = "password" }
signin_params = { name = "user", pass = "password" }

[[cases]]
name = "a viewer can read movies"
kind = "sql_expect"
actor = "viewer"
sql = "SELECT count() FROM movie GROUP ALL;"
[[cases.assertions]]
path = "0.count"
equals = 146

[[cases]]
name = "a record user can add a movie, and is recorded as its creator"
kind = "sql_expect"
actor = "user"
sql = """
CREATE ONLY movie SET
    actors = ["Mirlan Abdykalykov"],
    directors = ["Aktan Abdykalykov"],
    genres = ["Drama", "Family"],
    imdb_rating = 69,
    languages = ["Kyrgyz"],
    plot = "A happy-go-lucky Kyrgyz lad learns he is adopted as he begins his transition to adulthood.",
    rt_rating = 100,
    released = <datetime>"1998-06-01",
    runtime = 81m,
    title = "Beshkempir",
    writers = ["Aktan Abdykalykov", "Avtandil Adikulov", "Marat Sarulu"];
"""
[[cases.assertions]]
path = "average_rating"
equals = 84.5
[[cases.assertions]]
path = "created_by"
equals_auth = "$auth.id"

[[cases]]
name = "a record user cannot change a movie it did not create"
kind = "sql_expect"
actor = "user"
sql = "UPDATE movie SET title = 'Changed' WHERE title = 'Up';"
[[cases.assertions]]
path = ""
equals = []
```

Each `[actors.…]` section is a user to run queries as. The `user` actor is a record user who signs up through the `account` access method, just as we did with `curl` on page 6. Each `[[cases]]` section is one query, and each `[[cases.assertions]]` section checks one part of its result. `equals_auth` compares a value with the id of the user who ran the query.

Let's try running the tests now:

```bash
surrealkit test --user root --pass secret
```

```text
Test run: PASS (642ms)

Summary
- suites: 2 total, 0 failed
- cases : 4 total, 4 passed, 0 failed
```

The second suite is a small one that `init` created. Every time you run `test`, SurrealKit creates a new, empty database, applies the schema files, runs the seed files, runs the tests and deletes the database again. The database we created above is never touched.

Because each test run builds the database from your files, a change that breaks something shows up straight away. Suppose that someone adds a required field to `movie.surql`:

```surql
DEFINE FIELD countries ON TABLE movie TYPE array<string>;
```

The next `surrealkit test` stops before any test runs, because the movies in `02_movies.surql` do not have the new field:

```text
Error: executing seed/02_movies.surql

Caused by:
    Couldn't coerce value for field `countries` of `movie:m05nxdzeovvhx2wlo2in`: Expected `array` but found `NONE`
```

## Where to go from here

The movie database now lives in a handful of files that you can keep in Git, review and share. Anyone with the files can build the same database with `sync` and `seed`, and check it with `test`.

- The [SurrealKit documentation](https://surrealdb.com/docs/manage/schema-migration) describes every command and option.
- The [schema course](https://surrealdb.com/learn/schemas/page-01) builds a project with SurrealKit from the start which goes into more detail. One functionality in particular you will want to learn is rollouts, which are SurrealKit's way to change a database that other people or apps already use.

The next page collects every query from this tutorial in one place.

At a glance

**You are on**

Chapter 7 of 8

**Chapters**

8

**Format**

Text, with queries you can run

**Runs in**

SurrealDB Studio, in the browser

**Cost**

Free

**Then take**

[Schema internals and migrations](https://surrealdb.com/learn/schemas)

[Next: All the queries from the tutorial](https://surrealdb.com/learn/movies/page-07)

## Continue

### [6: Defining users and permissions](https://surrealdb.com/learn/movies/page-06)

Previous

### [All the queries from the tutorial](https://surrealdb.com/learn/movies/page-07)

Next lesson

THE PLATFORM

## Everything an application and its agents know. Five surfaces, one engine.

Database

Document, graph, vector, time-series and relational in one engine.

![Five data models as dotted tiles: documents, graph, vector, time-series and relational](https://surrealdb.com/assets/static/platform-database.DUdumYDz.avif)

Read more

[Database](https://surrealdb.com/surrealdb)

Agent Memory

What an agent learns, with its source and its time, in the same engine.

![A timeline of remembered facts, each with its source](https://surrealdb.com/assets/static/platform-agent-memory.Cvv8D5Wl.avif)

Read more

[Agent Memory](https://surrealdb.com/agent-memory)

Cloud

Managed clusters in the regions you choose, scaled on demand.

![Clusters in three regions on a world map, each running or scaling](https://surrealdb.com/assets/static/platform-cloud.--QZnaVi.avif)

Read more

[Cloud](https://surrealdb.com/cloud)

Studio

Query, explore and design the schema from the browser.

![A SurrealQL query in Studio and the schema graph under it](https://surrealdb.com/assets/static/platform-studio.7ykBFNLq.avif)

Read more

[Studio](https://surrealdb.com/studio)

MCP

Every model that speaks MCP reaches the database and the memory directly.

![Three models connected through MCP to the database and Agent Memory](https://surrealdb.com/assets/static/platform-mcp.D_oH0_wm.avif)

Read more

[MCP](https://surrealdb.com/mcp)

IN PRODUCTION

## Trusted at scale. Samsung, Nvidia, Verizon, Tencent, and Walmart run on SurrealDB.

14,000+

Developers building on SurrealDB Cloud

4M+

Developers building on SurrealDB worldwide

FROM THE TEAMS

> SurrealDB gives us a foundation where we can unify semantic search, knowledge graphs, and AI-driven decision making without stitching together multiple systems. Collapsing responsibility into SurrealDB has become our default engineering posture.

*Justin Foley*

VP of Engineering, Later

```json
{"@context":"https://schema.org","@type":"Course","name":"Movie database tutorial","description":"Movie database tutorial","url":"https://surrealdb.com/learn/movies","inLanguage":"en","isAccessibleForFree":true,"provider":{"@type":"Organization","name":"SurrealDB","url":"https://surrealdb.com"},"hasPart":[{"@type":"LearningResource","name":"Movie database tutorial","url":"https://surrealdb.com/learn/movies"},{"@type":"LearningResource","name":"1: Getting started","url":"https://surrealdb.com/learn/movies/page-01"},{"@type":"LearningResource","name":"2: Improving the existing data","url":"https://surrealdb.com/learn/movies/page-02"},{"@type":"LearningResource","name":"3: SurrealQL data types","url":"https://surrealdb.com/learn/movies/page-03"},{"@type":"LearningResource","name":"4: Adding a schema","url":"https://surrealdb.com/learn/movies/page-04"},{"@type":"LearningResource","name":"5: Adding and querying relations","url":"https://surrealdb.com/learn/movies/page-05"},{"@type":"LearningResource","name":"6: Defining users and permissions","url":"https://surrealdb.com/learn/movies/page-06"},{"@type":"LearningResource","name":"7: Managing the schema with SurrealKit","url":"https://surrealdb.com/learn/movies/surrealkit"},{"@type":"LearningResource","name":"All the queries from the tutorial","url":"https://surrealdb.com/learn/movies/page-07"}]}
```

```json
{"@context":"https://schema.org","@type":"LearningResource","name":"7: Managing the schema with SurrealKit","description":"Keeping the schema in files with SurrealKit","url":"https://surrealdb.com/learn/movies/surrealkit","learningResourceType":"lesson","isPartOf":{"@type":"Course","name":"Movie database tutorial","url":"https://surrealdb.com/learn/movies"},"position":8}
```

```json
{"@context":"https://schema.org","@type":"Organization","@id":"https://surrealdb.com/#organization","name":"SurrealDB","url":"https://surrealdb.com","logo":"https://surrealdb.com/assets/static/logo.BG7_TG2b.svg","description":"SurrealDB is the context and memory layer for AI agents. A multi-model database for documents, graphs, vectors, and time-series.","foundingDate":"2022","legalName":"SurrealDB Ltd","identifier":{"@type":"PropertyValue","propertyID":"GB-COH","value":"13615201"},"address":{"@type":"PostalAddress","streetAddress":"3rd Floor, 1 Ashley Road","addressLocality":"Altrincham","addressRegion":"Cheshire","postalCode":"WA14 2DT","addressCountry":"GB"},"contactPoint":[{"@type":"ContactPoint","contactType":"customer support","email":"support@surrealdb.com","url":"https://surrealdb.com/contact","availableLanguage":"English"},{"@type":"ContactPoint","contactType":"sales","email":"info@surrealdb.com","url":"https://surrealdb.com/contact","availableLanguage":"English"},{"@type":"ContactPoint","contactType":"security","email":"security@surrealdb.com","url":"https://surrealdb.com/.well-known/security.txt","availableLanguage":"English"},{"@type":"ContactPoint","contactType":"legal","email":"legal@surrealdb.com","url":"https://surrealdb.com/legal","availableLanguage":"English"}],"hasCertification":[{"@type":"Certification","name":"SOC 2 Type 2"},{"@type":"Certification","name":"GDPR"},{"@type":"Certification","name":"Cyber Essentials Plus"},{"@type":"Certification","name":"ISO 27001"}],"owns":[{"@type":"SoftwareApplication","name":"SurrealDB","url":"https://surrealdb.com/surrealdb"},{"@type":"SoftwareApplication","name":"Agent Memory","url":"https://surrealdb.com/agent-memory"}],"knowsAbout":["multi-model databases","document databases","graph databases","vector search","time-series databases","SurrealQL","Agent Memory","real-time databases","embedded databases","context layer","graph ontology","distributed database","knowledge graphs","distributed transaction protocols","highly-scalable databases"],"sameAs":["https://www.wikidata.org/wiki/Q124316308","https://github.com/surrealdb/surrealdb","https://twitter.com/surrealdb","https://www.youtube.com/@surrealdb","https://www.linkedin.com/company/surrealdb","https://discord.gg/surrealdb","https://www.reddit.com/r/surrealdb","https://www.instagram.com/surrealdb","https://medium.com/surrealdb","https://dev.to/surrealdb"]}
```

```json
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://surrealdb.com"},{"@type":"ListItem","position":2,"name":"Learn","item":"https://surrealdb.com/learn"},{"@type":"ListItem","position":3,"name":"Surrealkit","item":"https://surrealdb.com/learn/movies/surrealkit"}]}
```
