---
title: "1: Schemaless vs. schemafull | SurrealDB University"
description: "Schemaless defaults, STRICT, schemafull tables, type flexibility, and security notes."
url: https://surrealdb.com/learn/schemas/page-01
---

![Course content preview](https://surrealdb.com/assets/static/course-schemas.D4CFbBhP.avif)

[Back to Courses](https://surrealdb.com/learn)

Course chapters

[Schema internals and migrations](https://surrealdb.com/learn/schemas) Internals [1: Schemaless vs. schemafull](https://surrealdb.com/learn/schemas/page-01) [2: Schema internals](https://surrealdb.com/learn/schemas/page-02) [3: Migrations](https://surrealdb.com/learn/schemas/page-03) [4: Data types](https://surrealdb.com/learn/schemas/page-04) [5: Automation](https://surrealdb.com/learn/schemas/page-05) Migrations [6: SurrealKit and the first table](https://surrealdb.com/learn/schemas/page-06) [7: Activities and seed data](https://surrealdb.com/learn/schemas/page-07) [8: Computed and asserted fields](https://surrealdb.com/learn/schemas/page-08) [9: Graph dependencies](https://surrealdb.com/learn/schemas/page-09) [10: Sync vs rollouts](https://surrealdb.com/learn/schemas/page-10) [11: Milestones](https://surrealdb.com/learn/schemas/page-11) [12: People and indexes](https://surrealdb.com/learn/schemas/page-12) [13: Events and CI](https://surrealdb.com/learn/schemas/page-13) [14: Capstone](https://surrealdb.com/learn/schemas/page-14)

# 1: Schemaless vs. schemafull

The first thing to know about schemas in SurrealDB is that, strictly speaking, you don't need one. SurrealDB at its most basic state is a schemaless document database, and that still covers most of what people build apps with. Here is a small sample of what you can do without ever touching a schema:

- **CRUD** - `CREATE`, `SELECT`, `UPDATE`, `DELETE`, plus `UPSERT` and `INSERT` when you need them
- **Relations** - `RELATE` for graph edges, and ordinary record IDs stored in fields when a simple link is enough
- **Joins and graph queries** - traverse with `->` / `<-`, pull related records with dot syntax, or nest subqueries
- **Filtering and aggregation** - `WHERE`, `GROUP BY`, `ORDER BY`, `LIMIT`, and math helpers such as `math::sum` and `math::mean`
- **Database functions** - string, array, time, crypto, HTTP, geo, and the rest of the built-in library
- **Query logic** - `LET` parameters, `IF` / `ELSE`, `FOR` loops, and closures inside queries
- **Manual transactions** - multi-statement work that commits or rolls back together
- **Live queries** - `LIVE SELECT` so clients get pushed updates when matching records change

So if that's all you need, then so long! 👋 You're already "done" the course.

But since you've come all the way to a course devoted to SurrealDB schemas, you're probably interested in learning how their internals work. After all, even a few `DEFINE` statements can unlock advantages such as greater type safety, automated events, table views, and indexes.

Let's start with the first point to know about schema.

## Schemaless means defined for you, not undefined

Not defining a schema doesn't mean that SurrealDB doesn't make use of `DEFINE` statements under the hood. All it means is that SurrealDB just populates them automatically in the most flexible way the first time you write to a table.

Let's demonstrate this by creating a couple of `person` records and relating them with a `->knows->` edge. These are followed up by a final `INFO FOR DB` statement that shows why the first statements worked in the first place: because SurrealDB has quietly run a `DEFINE` statement for each table.

Both tables are `TYPE ANY`, and `SCHEMALESS`. Here's what that means:

- `TYPE ANY`: The table can be used both as a regular table or a graph edge.
- `SCHEMALESS`: You don't need to define any fields to set them.

That's why running `INFO FOR TABLE person` and `INFO FOR TABLE knows` won't show any `DEFINE FIELD` definitions, because they were never needed.

```
-- surreal start --user root --pass secret-- surreal sql   --user root --pass secretCREATE person:rand;CREATE person:asmodean CONTENT {    name: "Asmodean",    age: 3050};RELATE person:asmodean->knows->person:rand;INFO FOR DB.tables;INFO FOR TABLE person; -- No contentINFO FOR TABLE knows;  -- No content
```

![Surrealist Icon](https://surrealdb.com/assets/static/fdfe2c20f5941d5e.D0guSOhZ.webp) Run Query

Output

```surql
{
	knows: 'DEFINE TABLE knows TYPE ANY SCHEMALESS PERMISSIONS NONE',
	person: 'DEFINE TABLE person TYPE ANY SCHEMALESS PERMISSIONS NONE'
}
```

SurrealDB's philosophy is to provide a maximum of flexibility by default, with the option to add as much strictness and type safety as you like. (The main exceptions are those [related to security](https://surrealdb.com/learn/schemas/page-01#general-security-notes) that must be strict by default unless chosen otherwise.)

You can use the `STRICT` clause when defining a database if you don't want these `DEFINE` statements to be created for you when first writing to a table. Let's give that a try: start from the default namespace `main` and database `main`, then switch to a namespace called `new_ns` and define a new database called `new_db` with `STRICT`.

```surql
-- surreal start --user root --pass secret
-- surreal sql   --user root --pass secret

DEFINE NS new_ns;
USE NS new_ns;
DEFINE DB new_db STRICT;
USE DB new_db;

CREATE person:rand;

CREATE person:asmodean CONTENT {
    name: "Asmodean",
    age: 3050
};

RELATE person:asmodean->knows->person:rand;

INFO FOR DB.tables;
```

This time the `CREATE` and `RELATE` statements refuse to run, because a `STRICT` database won't let you use anything you haven't explicitly defined yourself.

Output

```surql
-------- Query 1 --------

NONE

-------- Query 2 --------

{
	database: 'main',
	namespace: 'new_ns'
}

-------- Query 3 --------

NONE

-------- Query 4 --------

{
	database: 'new_db',
	namespace: 'new_ns'
}

-------- Query 5 --------

"The table 'person' does not exist"

-------- Query 6 --------

"The table 'person' does not exist"

-------- Query 7 --------

"The table 'knows' does not exist"

-------- Query 8 --------

{  }
```

## Types of `DEFINE` statements

The opening chapters of this course are about how to work with `DEFINE` statements, and their counterparts `ALTER` and `REMOVE`.

There are quite a few `DEFINE` statements to choose from. To keep from being overwhelmed by them all, you can think of them as being divided into three categories:

- **Defining tenancy & shape**: NAMESPACE, DATABASE, TABLE, FIELD, SEQUENCE, INDEX (especially UNIQUE)
- **Defining add-ons**: FUNCTION, PARAM, API, MODULE, BUCKET, ANALYZER, USER, ACCESS, CONFIG
- **Defining reactive behaviour**:
  - On record CREATE/UPDATE/DELETE: `EVENT`
  - At the boundary (HTTP/auth calls): `ACCESS`, `API`, `CONFIG API`

Think of this as a rough grouping rather than a hard rule. A `DEFINE INDEX` statement, for example, can exist purely for performance, in which case it plays no role in the shape of a table's data.

## After choosing schemaless or schemafull

The main distinction between tables is whether they're schemaless or schemafull, after which you can adjust the behaviour if you like.

A schemaless table starts out completely flexible, after which you can add type safety piece by piece. A schemafull table starts out completely strict, to which you can add flexibility piece by piece. Here is a quick summary and a visual aid to understand how this works, and where to go after choosing -full vs. -less.

- SCHEMALESS: any non-defined field can be set. You can think of these as free slots in a machine. However, type safety can be bolted on with additional DEFINE FIELD statements, after which the table becomes schemafull for those fields alone.
- SCHEMAFULL: no field can be set unless it is defined, so DEFINE FIELD statements are required unless you're happy with a table that only has an `id` field. To make a schemafull table more flexible, you can use the FLEXIBLE clause for fields containing objects, or define fields that can take a variety of types.

```text
SCHEMALESS record                    SCHEMAFULL record
┌────┬────┬─────┬────┬ ─ ─ ─        ┌────┬──────────────────┐
│name│age │email│ ?? │ ??  │        │name│     payload      │
│ ▓▓ │ ▓▓ │ ▓▓  │ ·· │ ··  │        │ ▓▓ │  ┌───┬───┬───┐   │
└────┴────┴─────┴────┴ ─ ─ ─        └────┴──│ ? │ ? │ ? │ ──┘
  ▓ = defined + validated              only 2 top-level slots;
  ·· = free slots, no validation       inside payload: FLEXIBLE
  ─ ─ = open frontier
```

In short, `SCHEMALESS` gives you unlimited flexibility with optional field-level safety, and `SCHEMAFULL` gives you the opposite by default.

## Type flexibility

Type definitions themselves also vary in flexibility. Here are three examples of how this can vary:

- Most strict: a single type such as `string`. No other type can be set.
- More flexible: a literal/multiple type such as `int | float` or a multi-table record type. A variety of types can be set.
- Most flexible: `TYPE any`. Any type can be set.

On top of this, other clauses can be added to field definitions such as `ASSERT`.

## Some examples

To begin acquiring some schema muscle memory, let's take a look at some concrete but quick examples of the variety of flexibility introduced above.

### SCHEMALESS with no defined fields

Any fields can be set for this table.

```surql
DEFINE TABLE person SCHEMALESS;

CREATE person SET 
  name = "Asmodean", 
  age = 3050, 
  birth_name = "Joar Addam Nessosin";
```

### SCHEMALESS with defined fields

Here the `name` and `age` fields must be present and of a certain type. But `birth_name` (here a string) could have been anything.

```surql
DEFINE TABLE person SCHEMALESS;
DEFINE FIELD name ON person TYPE string;
DEFINE FIELD age ON person TYPE int;

CREATE person SET 
  name = "Asmodean", 
  age = 3050, 
  birth_name = "Joar Addam Nessosin";
```

### SCHEMAFULL with defined fields

Here we see two failed attempts to create a `person` record, followed by one that succeeds.

```surql
DEFINE TABLE person SCHEMAFULL;
DEFINE FIELD name ON person TYPE string;
DEFINE FIELD age ON person TYPE int;
DEFINE FIELD birth_name ON person TYPE string;

-- Fails: `birth_name` must be set
CREATE person SET 
  name = "Asmodean",
  age = 3050;

-- Fails: no top-level field `nickname` has been defined
CREATE person SET 
  name = "Asmodean", 
  age = 3050, 
  nickname = "Dean";

-- Succeeds: all defined fields and no others are present
CREATE person SET 
  name = "Asmodean", 
  age = 3050, 
  birth_name = "Joar Addam Nessosin";
```

### SCHEMAFULL with a flexible field

Here we have a schemaless microcosm inside a schemafull table thanks to the `FLEXIBLE` keyword:

```surql
DEFINE TABLE person SCHEMAFULL;
DEFINE FIELD name ON person TYPE string;
DEFINE FIELD metadata ON person TYPE object FLEXIBLE;

CREATE person SET
    name = "Asmodean",
    metadata = { 
      age: 3050, 
      birth_name: "Joar Addam Nessosin",
      tags: ["musician"] 
    };
```

### A field with a single type

```surql
DEFINE FIELD age ON person TYPE int;
```

### A field with multiple possible types

```surql
DEFINE FIELD score ON person TYPE int | float;
```

### A field that can take any type

```surql
DEFINE FIELD payload ON person TYPE any;
```

### Adding an `ASSERT` on a field

```surql
DEFINE FIELD email ON person TYPE string ASSERT $value.is_email();
```

## General security notes

The main exception to “flexible by default” is, naturally, when security comes into play. That's why you need to pass flags when starting a server via the [`surreal start`](https://surrealdb.com/docs/reference/cli/surrealdb-cli/commands/start) command to override the secure defaults, such as:

- The `unauthenticated` flag to turn off authentication and thereby give anonymous users root access
- The `--allow-net` flag to allow the server to use HTTP functions
- The `--allow-scripting` flag to allow JavaScript functions to be used inside SurrealQL

And many more. For more on this, see our [Security best practices page](https://surrealdb.com/docs/learn/security/best-practices/security-best-practices) where you can learn how to balance functionality and security such as denying by default followed by allowing access to only certain functions and URLs.

```bash
surreal start --deny-all --allow-funcs "array, string, crypto::argon2, http::get" --allow-net api.example.com:443
```

That covers the main dials: schemaless vs schemafull tables, and how strict a field's type can be. [The next chapter](https://surrealdb.com/learn/schemas/page-02) looks at what the database actually stores for those definitions, how to read it with `INFO`, and how SurrealDB Studio shows the same catalog visually.

Previous

Schema internals and migrations

[Previous](https://surrealdb.com/learn/schemas)

Next lesson

2: Schema internals

[Next lesson](https://surrealdb.com/learn/schemas/page-02)

```json
{"@context":"https://schema.org","@type":"Course","name":"Schema internals and migrations","description":"Learn how SurrealDB stores schema metadata, how DEFINE statements shape your database, and how to migrate production data safely.","url":"https://surrealdb.com/learn/schemas","inLanguage":"en","isAccessibleForFree":true,"provider":{"@type":"Organization","name":"SurrealDB","url":"https://surrealdb.com"},"hasPart":[{"@type":"LearningResource","name":"Schema internals and migrations","url":"https://surrealdb.com/learn/schemas"},{"@type":"LearningResource","name":"1: Schemaless vs. schemafull","url":"https://surrealdb.com/learn/schemas/page-01"},{"@type":"LearningResource","name":"2: Schema internals","url":"https://surrealdb.com/learn/schemas/page-02"},{"@type":"LearningResource","name":"3: Migrations","url":"https://surrealdb.com/learn/schemas/page-03"},{"@type":"LearningResource","name":"4: Data types","url":"https://surrealdb.com/learn/schemas/page-04"},{"@type":"LearningResource","name":"5: Automation","url":"https://surrealdb.com/learn/schemas/page-05"},{"@type":"LearningResource","name":"6: SurrealKit and the first table","url":"https://surrealdb.com/learn/schemas/page-06"},{"@type":"LearningResource","name":"7: Activities and seed data","url":"https://surrealdb.com/learn/schemas/page-07"},{"@type":"LearningResource","name":"8: Computed and asserted fields","url":"https://surrealdb.com/learn/schemas/page-08"},{"@type":"LearningResource","name":"9: Graph dependencies","url":"https://surrealdb.com/learn/schemas/page-09"},{"@type":"LearningResource","name":"10: Sync vs rollouts","url":"https://surrealdb.com/learn/schemas/page-10"},{"@type":"LearningResource","name":"11: Milestones","url":"https://surrealdb.com/learn/schemas/page-11"},{"@type":"LearningResource","name":"12: People and indexes","url":"https://surrealdb.com/learn/schemas/page-12"},{"@type":"LearningResource","name":"13: Events and CI","url":"https://surrealdb.com/learn/schemas/page-13"},{"@type":"LearningResource","name":"14: Capstone","url":"https://surrealdb.com/learn/schemas/page-14"}]}
```

```json
{"@context":"https://schema.org","@type":"LearningResource","name":"1: Schemaless vs. schemafull","description":"Schemaless defaults, STRICT, schemafull tables, type flexibility, and security notes.","url":"https://surrealdb.com/learn/schemas/page-01","learningResourceType":"lesson","isPartOf":{"@type":"Course","name":"Schema internals and migrations","url":"https://surrealdb.com/learn/schemas"},"position":2}
```

```json
{"@context":"https://schema.org","@type":"Organization","name":"SurrealDB","url":"https://surrealdb.com","logo":"https://surrealdb.com/assets/static/logo.BG7_TG2b.svg","description":"SurrealDB is the unified data layer for AI. 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":"Page 01","item":"https://surrealdb.com/learn/schemas/page-01"}]}
```
