Better Programming

Advice for programmers.

Follow publication

SurrealDB Explained With Express.js, Node.js, and TypeScript

Aidan Tilgner
Better Programming
Published in
8 min readOct 6, 2022

--

image by author, logo by surrealDB

If you keep up with developer news, you might’ve heard of the new kid on the database block, SurrealDB. Although it’s relatively new, ambitious, and still in beta, there is a lot going on for this new database, and I’m here to help clear up some confusion.

What is SurrealDB?

So, first and foremost, what is SurrealDB, and how is it different from any other database? Well, first of all, we’re used to thinking of databases as confined to a certain architectural paradigm, whether that be relational, document, graph, or something else. These paradigms each have their strengths and weaknesses, and oftentimes, are implemented to solve specific problems.

Furthermore, we can also divide databases into two general types: SQL, and NoSQL. SQL stands for Structured Query Language and describes a syntax that database queries can follow for maximum efficiency, performance, and readability. SQL databases are the most common, but both have their advantages and disadvantages, usually fitting into one of the aforementioned paradigms above.

Describing SurrealDB

So what about SurrealDB? What categories does it fit into? Well, that’s the thing, understanding SurrealDB will require a shift in perspective when thinking about databases. You could fit it into any of the categories mentioned above, but not neatly.

SurrealDB is a relational database, a document database, and it also does graphing. You can use all of them at the same time. On top of that, it uses SQL, except it stands for Surreal Query Language. So, it’s technically NoSQL, although I’d hazard a bet that most SQL writers wouldn’t notice the difference at first, as SurrealQL is mostly an expansion of existing SQL syntax.

The idea here is that you can take the best parts of many architectural paradigms and combine them into something that works better on a wider variety of projects. The cash incentive for the creators is to host serverless, managed SurrealDB instances, which would be easy to set up, and act more like REST APIs.

Rust

There’s also a cherry on top, but I wouldn’t eat it, because the cherry is Rust. For anyone who’s been living under a rock, Rust is the most loved programming language of the last couple of years, according to Stack Overflow at least. It is said that once you develop a program in Rust, you ascend to the realm of the developer gods and are bestowed with a holy stone tablet bearing the words “blazingly fast,” and a sacred knowledge that grants you unlimited power over Fireship 100-second videos.

Of course, whether it is written in Rust really matters to you is probably a matter of opinion, but there is something to be said about Rust memory management and compilation. Rust programs tend to be pretty fast, and there’s a reason developers love working with it.

The basic idea

To summarize for those of you busy googling why your Rust “Hello World” program isn’t working, SurrealDB aims to be a paradigm shift in database technologies. Theoretically at least, it combines the best parts of relational, document, and graph architectures into a concise and fast database which you can use SQL-like syntax to interface with. Of course, it is very new and is yet to be battle-tested, but you get the idea.

So, How Do I Use It?

Well, first and foremost, you’ll need to install SurrealDB in order to follow the rest of the tutorial. As of the time of writing, the cloud platform is not yet operational, so you’ll have to set up the database yourself. Once you’ve got it installed, make sure it’s added to your PATH , then run the standard surreal --help command to make sure it’s working. (Pro tip: if you see the surreal executable in your path, but it won’t execute in your terminal, you might need to open a new terminal window).

Using the CLI

Once you’ve got Surreal working on the command line, we’re gonna start up a new database instance. Surreal makes development easier by allowing you to start an in-memory database, which will be created completely from scratch every time you start it, and wiped every time you stop it. In order to start a database in-memory, you’ll run the following command:

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

There is more detail available in the docs here, so I won’t go into much detail about what’s happening here. The important thing to remember is that Surreal is starting in memory, and by default will initialize to port 8000.

By the way, since this was hard for me to find an example of, you can initialize a persistent storage database by providing a --path parameter to the start command instead of memory:

surreal start --log trace --user root --pass root file://"[absolute_path_to_directory_to_store_database]"

You can then interface with your shiny new in-memory database with this command.

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

This will open up a CLI that lets you run SurrealQL on your database. Try the following:

> CREATE users:test SET name = "test_user"

You should see something like this:

[{"time":"1.6982ms","status":"OK","result":[{"id":"users:test","name":"test_user"}]}]

Which is confirmation that you have created a new record in the users table with the id of users:test and the name of test_user . You can select that user by running the following query:

> SELECT * FROM users WHERE name = "test_user"

You get the idea. It’s that simple and very similar to SQL. However, you won’t be building an app with manual queries, and you certainly won’t be doing it from the CLI.

Initializing Express

All code written in this tutorial will be publicly available on my GitHub.

For the purposes of this tutorial, we’re gonna be using Node.js, TypeScript, and Express, which is a fairly basic stack. If you haven’t used TypeScript before, don’t worry, I’ll explain everything in detail as I go along. Make sure you have node installed, and we’ll start by initializing a new npm project in a directory of your choice. I’d recommend just copying my package.json from below instead of bothering to install the dependencies yourself:

Then we’ll initialize our tsconfig.json file in our project root by running the following command:

npm run init

This works because TypeScript is installed as an npm package from the package.json , which also specifies the init script. Once that’s done you should see a tsconfig.json file in the project root. This file contains the options for compiling TypeScript, which are highly customizable. In this case, however, we’re going to be using the defaults, and therefore I won’t bother explaining too much of the details of the file.

Once you have your tsconfig.json , we have one more config file to set up — the .env . If you haven’t used a .env before, it’s a great way to store secrets that should not be easily viewable from the source code. It’s also great for storing variables that may change based on the environment. Therefore, we’ll be using it to store the database host, port, and credentials.

Create a .env file in your project root, and add the following (assuming you used the above commands to start SurrealDB on port 8000 with default credentials):

PORT=3000SURREAL_DB_URL=http://localhost:8000/rpcSURREAL_DB_USER=root
SURREAL_DB_PASSWORD=root

Now that we’ve got the config files in place, let’s get the app going. Create an index.ts file in the project root if you don’t already have one. In this file, add the following:

This is setting up a simple Express app, using the PORT we specified in our .env file. The config() function is initializing a process.env object with the key-value pairs we put in the .env file. Then we can access them in our project. Run the following command and go to localhost:3000 in your browser.

npm run dev

Connecting to our database

Now you should see the text “Hello World” displayed in your browser, which means your Express app is up and running. As for our database connections, we’re going to be handling those in a separate file. Create a new folder in your root directory called utils , and add a file called surrealdb.ts.

Now, we’re going to use the following code to establish a connection with the database. Start by importing some dependencies and initializing a Surreal instance:

As you can see here, we’re importing the Surreal class from the surrealdb.js library. Then, we’re making sure to configure the env variables. Once we have those, we can use the Surreal class to initialize our db instance.

Now we just have to make a function that we can use to get the DB class up and running when we start the app. In the same file, below the code you just wrote, write the following function:

This function is doing a few things. First, it’s connecting to the database instance we have running in our terminal on PORT 8000. Then, it’s using the default root root credentials that we provided in our .env and specified in the surreal start command. Finally, we specify which namespace and database we’d like to use, in this case test test.

That last line is important too. We want to make sure to export the db instance from this file so that it can be accessed project-wide. Once we run our initDB function, and it doesn’t produce any errors, we will be able to use the other methods that db provides us with.

So now, back in our index.ts , we’re going to import this initDB function and run it when the app starts:

Run your app again and you should see the following logging:

Initializing database...
Server started on port 3000
Connected to database
Signed in to database

This means that you’ve been successful, and have a database connection. Now, we can get down to the fun part. I won’t go into too much depth here because it is up to you how to set up your app, but I’ll briefly go over how to use the db.query method. Let’s say you make a new file for your queries:

import { db } from "utils/surrealdb";const newQuery = db.query("CREATE test:test SET name=$variable", {
variable: "test"
});

As you can see, the query method is very similar to what we were doing in the command line, with one notable exception, the $variable syntax. It is vital that you use this syntax and don’t try other ways like string interpolation, to prevent SQL injection attacks. As you can see, when specifying a variable in a query, use a $[variable_name]syntax, and specify the value of the variable with the second argument.

If you would like to learn more about the db.query method, the documentation is available here, where you can also learn about other methods the db instance has.

Conclusion

So, what have we learned here? Well, hopefully, how to boilerplate a SurrealDB, Express, and TypeScript app, but also that there’s a shiny new database to play around with. SurrealDB is very new, in beta, and has not been battle tested. If you are building a production-level app where data security and preservation are key, it might be a good idea to stick with the tried and true databases of the time.

However, if you’re looking to get a taste of what databases are going to be in the near future, I’d highly recommend giving SurrealDB a try. If anyone has questions, concerns, or anything else to say, please feel free to leave a comment. I do read them, and I appreciate the feedback.

Happy coding, everyone!

--

--

Aidan Tilgner
Aidan Tilgner

Written by Aidan Tilgner

Software Developer working my way through the world and sharing what I learn with all of you. More of my writing — aidantilgner.substack.com

Write a response