DEV Community

Cover image for How to Design a SurrealDB schema and create a basic client for TypeScript
Sebastian Wessel
Sebastian Wessel

Posted on

How to Design a SurrealDB schema and create a basic client for TypeScript

In the midst of a dynamic landscape of exciting new projects, one name shines bright — SurrealDB.

It's not just another database - it's touted as 'The ultimate multi-model database.'.
Just last week, during the 'SurrealDB World' event, they celebrated the launch of their first production-ready version.

What makes SurrealDB unique is its exceptional flexibility in data storage.
Unlike traditional databases that force you to choose between fixed schemas or total chaos, SurrealDB lets you have the best of both worlds.
You can define a table as schema-less while still specifying schema information for known fields.
In this article, we'll explore SurrealDB's features and learn how to create a data schema and a basic client to make the most of this versatile database.

Defining Your Schema

When it comes to creating a schema definition, you have two primary methods at your disposal.

Using SurrealQL for Schema Definition

If you're already familiar with SQL, you'll find SurrealQL to be quite approachable since it shares similarities with standard SQL.

As described in the SurrealDB documentation, you can define your schema like this:

-- Let's start by creating a schema-full user table.
DEFINE TABLE user SCHEMAFULL;

-- Now, let's define some fields.
DEFINE FIELD firstName ON TABLE user TYPE string;
DEFINE FIELD lastName ON TABLE user TYPE string;
DEFINE FIELD email ON TABLE user TYPE string
  ASSERT string::is::email($value);
Enter fullscreen mode Exit fullscreen mode

With SurrealQL, you can structure your schema just the way you want it, making it a powerful tool for designing your data model.

As mentioned earlier, the same principles apply when defining a table as SCHEMALESS instead of SCHEMAFULL.

Here's an example in SurrealQL:

-- Let's create a schemaless user table.
DEFINE TABLE user SCHEMALESS;

-- Now, we'll proceed to define some fields.
DEFINE FIELD firstName ON TABLE user TYPE string;
DEFINE FIELD lastName ON TABLE user TYPE string;
DEFINE FIELD email ON TABLE user TYPE string
  ASSERT string::is::email($value);
Enter fullscreen mode Exit fullscreen mode

When it comes to field definition, SurrealQL offers a rich array of features. Some of the key ones include:

  • Specifying the data type
  • Setting up validation rules
  • Defining default values
  • Applying data transformations
  • Establishing a list of possible values, similar to enums

For more detailed information, be sure to explore the official documentation.

Leveraging Surrealist.app

When it comes to working with SurrealDB, there's one tool that I wholeheartedly recommend: Surrealist.
This software is a game-changer for simplifying your SurrealDB experience.

Surrealist offers a user-friendly interface that streamlines your interactions with SurrealDB. Not only does it come in a convenient browser version, but it also offers the powerful Surrealist Desktop application.

Surrealist Desktop takes your SurrealDB workflow to the next level by introducing a range of additional features, including the highly versatile Designer.

Surrealist Designer

The Surrealist Designer empowers you to effortlessly craft your table schema and manage entity relations. It's not just for beginners; the intuitive UI makes it accessible to newcomers, while experts can appreciate its ability to visualize even the most intricate data structures.

Whether you're just starting or diving into complex projects, Surrealist is your trusted companion for making the most of SurrealDB. Give it a try and see how it enhances your database design journey.

Generating Zod Schemas and TypeScript Clients from SurrealDB

Being a TypeScript developer, my journey with a database doesn't end at just interacting with it.
Naturally, I want a TypeScript program that seamlessly connects to SurrealDB.

I soon found myself at a point where I wanted to automate the creation of a simple client, leveraging the information already present in the database.

So, I embarked on a weekend coding session and crafted a small CLI tool. This tool works its magic by extracting schema information directly from SurrealDB, and it doesn't stop there. It also generates corresponding Zod schemas and a TypeScript client for basic CRUD operations.
The client is designed to work seamlessly with the official SDK, making the integration process smooth and efficient.

With this tool, I aimed to simplify the process of working with SurrealDB, allowing TypeScript developers to dive into their projects with confidence, knowing they have a solid foundation to build upon.

The intention is, to provide a solid starting point for development, and to speed up the processed.

You can find the project on GitHub:
https://github.com/sebastianwessel/surrealdb-client-generator

You have multiple configuration options at your disposal, which can be set either as command-line interface (CLI) options or within a configuration file.
What's more, you can even use both methods simultaneously.
In such cases, the CLI options take precedence.

Getting started with the tool is a breeze:

npx surql-gen
Enter fullscreen mode Exit fullscreen mode
Options:
  -V, --version                          output the version number
  -f, --file [schemaFile]                a SurrealQL file containing the definitions (default: "myschema.surql")
  -c, --config [configFile]              SurrealDB connection url (default: "surql-gen.json")
  -s, --surreal [surreal]                SurrealDB connection url (default: "ws://127.0.0.1:8000")
  -u, --username [username]              auth username (default: "root")
  -p, --password [password]              auth password (default: "root")
  -n, --ns [ns]                          the namspace (default: "test")
  -d, --db [db]                          the database (default: "test")
  -o, --outputFolder [outputFolder]      output folder (default: "client_generated")
  -g, --generateClient [generateClient]  generate client (default: true)
Enter fullscreen mode Exit fullscreen mode

For further information, just check out the readme file in the repository.

Understanding Generated Files

In your designated output folder, it's essential to grasp how the generated files are organized for efficient management:

_generated Subfolder

Anything residing beneath the _generated subfolder is subject to potential overwriting during subsequent runs of the generator. This serves as the workspace where dynamic updates can occur.

schema Subfolder

The schema subfolder plays a pivotal role. It contains the schema and type definitions, offering customization possibilities. Unlike the _generated folder, everything within schema is generated only once and remains untouched or deleted in subsequent runs of the generator.

client Subfolder

When it comes to accessing the generated client code, you'll find it neatly tucked away within the client subfolder.

This organized structure ensures that your generated files are readily accessible while maintaining the integrity of your project's foundation.

Using the Client

The generated client is designed with simplicity in mind, adhering to the efficient repository pattern. In this pattern, each table is treated as a distinct entity, complete with its dedicated repository. These repositories, in turn, consolidate actions specific to the respective entity.

Using the client is straightforward and user-friendly. You'll always encounter methods in the format of get[Entity Name]Repository(db: Surreal), requiring only the database instance as an argument.

For a quick illustration, let's take a peek at how we can access all the generated methods for a project entity:

import { Surreal } from 'surrealdb.js'

import { getProjectRepository } from './client_generated/client/project/getProjectRepository.js'

const main = async () => {
  const db = new Surreal()
  await db.connect('ws://0.0.0.0:8000', {
    auth: { user: 'root', pass: 'root' },
  })
  await db.use({
    ns: 'voyage',
    db: 'voyage',
  })

  const rep = getProjectRepository(db)

  const project = await rep.createProject({
    name: 'Test project',
    logo: '/mylogo',
  })
  console.log('project', project)

  const projects = await rep.getAllProjects()
  console.log('projects', projects)

  const updated = await rep.updateProject(project.id, { name: 'Renamed !' })
  console.log('updated', updated)

  const selected = await rep.getProjectById(project.id)
  console.log('selected', selected)

  await rep.deleteProject(project.id)
  console.log('deleted')

  const projectsDel = await rep.getAllProjects()
  console.log('projects', projectsDel)

  await db.close()
}

main()

Enter fullscreen mode Exit fullscreen mode

All return values are automatically typed based on the Zod schema, simplifying your code and ensuring consistency.
Even if you customize a schema, TypeScript types adapt seamlessly, eliminating the need for manual updates.
This streamlined process keeps your codebase error-free and agile, saving you time and effort.

Alternative

There is the possibility, to disable the generation of the TypeScript client.
The intention is, to only generate Zod schema, which than can simply used in Cirql - SurrealDB ORM & Query Builder or in your own choice.

Final words

In Closing

SurrealDB is undeniably a remarkable addition to the world of databases, and it's a platform that deserves your attention. With Surrealist, the entry barrier for beginners is lowered to a minimum, offering a professional UI enriched with fantastic features.

I trust that my small tool will serve as a valuable resource for TypeScript developers looking to dive into the world of SurrealDB, making it more accessible and enjoyable.

If you find this tool helpful, I kindly ask you to show your appreciation by giving it a star on GitHub.

And, of course, if you encounter any issues or have suggestions for improvements, please don't hesitate to open an issue on GitHub. Your feedback is invaluable.

For further inquiries about SurrealDB, don't hesitate to explore their comprehensive documentation or engage with the community on their Discord channel.

Happy coding - Cheers and bye bye! 👋


Looking to dive deeper into SurrealDB?
Don't miss out on the comprehensive series I've recently penned

Top comments (0)