SurrealDB Explained With Express.js, Node.js, and TypeScript
An in-depth introduction to the new database on the block

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!