Check out a free preview of the full Introduction to Node.js, v3 course
The "Using a File as a DB" Lesson is part of the full, Introduction to Node.js, v3 course featured in this preview video. Here's what you'd learn in this lesson:
Scott demonstrates how to use the FS module and a JSON file as a database to store created notes. The foundation for utility functions to read the db.json file, save a JavaScript object, and add a new note are also covered in this segment.
Transcript from the "Using a File as a DB" Lesson
[00:00:00]
>> The next thing is using a file system as a database. So let's do that. This is a place where you do wanna follow along. This is gonna be for our note taking app. So ignore this test JS thing that I did in this demo stuff. But now what we wanna do is on the root of our file of our project, not inside the source folder, but on the root I'll just add it here.
[00:00:20]
We're gonna add a db.json file or json. And like I said, put it on the routes. I'm gonna drag mine down. It is okay. So, db.json file. Let's put some json in here that says notes and then an empty array and this is going to be our database.
[00:00:39]
That's an array with notes, it's super simple. The next thing you wanna do is we're gonna create our db.js file. And inside of here we're gonna do a few things. We're gonna create some utility functions to help us interact with our database file. So we don't have to write this from scratch every time we want to interact with a database we can choose these abstracted methods.
[00:00:59]
You could think of it as like an ORM, I guess, for our database. If you don't know what an ORM is, it's basically an SDK for your database. That's the best way I can describe it. It's like you want to talk to your database, but maybe you don't want to write raw SQL.
[00:01:12]
Cool, use this SDK. That's an ORM. So this is like the SDK for our database. Our database, which is a file. So we're gonna do that. So let's create a db.js in our source folder and we're gonna use the fs module from node fs promises. If you don't use promises, 'cause you just like callbacks, you go for it, I won't stop you.
[00:01:36]
The next thing is we just wanna just hard code the path to our database, Which is going to look exactly like I had before, which is just basically using, okay, new URL and then the path to it, which will be up one database.json,import.metadata url.pathname. So we get the DB path.
[00:02:02]
And then we just want to create an export three functions basically right? So one function is gonna be able to get the entire database. Another function is gonna be able to take in a completely new database and write it so overwrite the database. And the last one will just be able to insert one new note, basically, so I wanna append it to the notes column in the database.
[00:02:26]
So let's do that. So export const getDB=async function here. And, Yeah, my AI is already trying to write the code for me. So I'm trying to tell it to go away because I don't want to do it that way. So basically what we wanna do here is rerun these fs to read the JSON file, we wanna parse it and we wanna give it back.
[00:02:57]
So let's do that. So we'll say const db = await fs.readFile, we wanna pass in a db path like that. And then this utf-8 thing this is just the encoding. So strings and programming have different types of encoding. So think of an encoding as in like the best way I can describe an encoding, it's like us humans know different languages.
[00:03:22]
Computers know different languages for strings as well. For instance, an emoji is in a different encoding than an alphabet. That's a different encoding, right? So this is, utf-8 is the encoding that we know as humans, basically. It's human words basically. So we want to utf-8, encode that, get the file, so now we have the database there.
[00:03:46]
And then we just want to return the database after we parse it into JavaScript. If you don't know what parse is, it just takes a JSON string and turns it to a JSON object. Or a JavaScript object, I should say. Cool, so that's how we can get DB.
[00:04:12]
The next one is going to be saving the DB. So this is the opposite. I want to take in a DB object, turn it into JSON. And then save into the database onto that that file right so we'll say export const save DB And this is gonna take a database and instead it will say, const all.
[00:04:41]
We could just say await fs.writeFile, DB_PATH stringify. So I have these extra arguments here in stringify. This is formatting, this is basically saying, take the stringify and space everything out by two spaces. So it looks good. If I don't do that, it'll just save it all on one line, which is fine.
[00:05:05]
But when I go look at the file, it bugs me. I hate that it looks like that way. So this is making it save like spaced out Json by spaces tabbed out, I guess. So that's what that's doing. You can put four if you prefer four. I like two, so we're gonna save that.
[00:05:22]
And then I guess we might as well just return the DB that you gave us. So we can just say, return that DB. And then the last thing is basically like appending to it. So there is an fs.append file. We probably don't want to do that. Because append file doesn't know anything about structure.
[00:05:42]
It doesn't know that something in there is JSON. It's just gonna add to the bottom of the file, which is what append means. And we don't want to add to the bottom of this file that would just put something here so we can't use append file.
[00:05:56]
So that means in order to add something to this array we have to either be really good with regex, which I'm not about to do, or turn this JSON into JavaScript. And then just push something in the array and then turn it back into JSON and save it.
[00:06:13]
Probably makes the most sense. So let's just do that. So we'll say export const insertDB and this is also async and it takes a note and the first thing is we need to get the db all right. So we can use our functions that we created db = awaits get db okay, look I don't want you doing all this for me go away.
[00:06:41]
So it's like, take it, do it. Okay, so we got the DB. The next thing we want to do is db.notes.push, and this note like that, so we can push that in. Basically, all we need to do now is just save it, and then we can just return the note itself.
[00:07:08]
So I can say, await, save the db, the entire database since we modified it, and then just return the note that you sent us anyway.
>> Does read file get all the contents and then close the file handle? Correct, who is asking that? How do they know about opening and closing?
[00:07:26]
That person used another language before? So yes, it does, it's not open anymore. It's not like a stream. Yeah, that's why it's non blocking because opening and closing a file handle is very expensive. So, does it make sense like why we made these functions because in a minute we're going to be like.
[00:07:49]
If you look at our commands these commands do things like creating a new note means we need to put something in that file getting all notes means we need to get everything from the file. Finding something means we need to get everything from the file, filter it you know and then put it back remove is like same thing it's filtering but remove it when you find it.
[00:08:13]
This web one will just get all of them so that's just an all and then clean means remove everything. So we were gonna be using a bunch of repetitive logic over and over again. And we didn't want to write it that many times. So I put it in another file, and now it's a module.
[00:08:27]
So now because these are exported, we can import them wherever we need to. So we could just import these directly into our commands and put them here. But I'm going to write one more level of abstraction. That uses these DB commands and they will use those methods inside the command.
[00:08:46]
I still think the DB functions as great as they are as an abstraction from the database file is still a little too generic for what we need for notes. Notes still need a little more Polish on them and I don't want that to be specific to the database files because for instance let's say we we went to the database.
[00:09:05]
And we had something else in here like users right I don't want to put note logic inside of these functions because I want these functions to work for users too. I have the word note here. I guess this is a little bit hard-coded, but we could abstract that out too, but everything else is pretty generic.
[00:09:24]
So in order to keep it that way, I'm goNNA create another level of abstraction that's specific just to notes, and it will use these generic functions instead.
Learn Straight from the Experts Who Shape the Modern Web
- In-depth Courses
- Industry Leading Experts
- Learning Paths
- Live Interactive Workshops