Shovels Of Code

moon indicating dark mode
sun indicating light mode

Let's Create a Data Storage Library Using a Factory

July 30, 2019

Warning

This will be a long post. I know it’s a lot for beginners. If you stick with it you will learn some neat tricks on how to manipulate objects & arrays

C.R.U.D

skip to some examples

My goal today is to better understand a bit about doing CRUD and State Management without the use of any libraries or tools other than raw javascript.

The only way I know to really grow is to build things. Really it’s worth a thousand hand holding tutorials. Nothing wrong with that of course to get a handle on the scope of what your getting into. All I’m saying is building things is far superier to anything else I’ve found out there to make concepts stick. If you want to learn more about that I’d recommend this blog post about learning

There are great ways of storing state now with Redux, MobX, and js-data for ORM stuff. The code to follow is not something I would probably use in a production app. For instance I create my own ids and usually that comes from your backend database. Rather, view it as a gym workout of sorts for the mind.

Additionally, I felt it wise to find a use case for the factory pattern that is concrete and useful in order to solidify understanding. If you are searching for a ready solution to your state management problems I’d say this isn’t the post you are looking for. Use some of the above mentioned libs for your data persistance needs. If instead you want to learn about javascript then this might be useful.

Sometimes it’s nice to pull out what you know in plain javascript and see what you can do to solve a common problem. So if this interests you and you want to follow along as I stumble through creating a data storage solution for simple flat object data then awesome! You are in for a treat!

Lets flesh out the skeleton of the app to get an idea of what we need to fill out. It will take a dataset & rudementry interface that is just an array of strings that describe the properties we want to include in the data.

THE SHAPE OF THINGS

/* shape of the data (interface)
id
first_name
last_name
email
gender
*/
// this will be passed into the dataStore on instantiation
const interfaceProps = ["first_name", "last_name", "email", "gender", "age"]
function dataStore(dataset, interface) {
// PRIVATE STUFF
// PROPERTIES
let sortDirection
let sortProp
// RANDOM ID GENERATOR
function createId() {}
// SETTERS
function setSortProp(prop) {}
function setSortDirection(direction) {}
// SORT DATASET METHOD
function sortDataset() {}
// INTERFACE INFORCEMENT FUNCTIONS
function dataInterface(props, record) {}
function conformToInterface(props, record) {}
// CREATE
function createRecord(record) {}
// READ
function findRecordById(id) {}
function getAllRecords() {}
// UPDATE
function findByIdAndUpdate(id, updatedRecord) {}
// DELETE
function deleteById(id) {}
// UTIL METHODS
function printAllRecords(direction, prop) {}
// RETURN PUBLIC METHODS
return {
createRecord,
findRecordById,
findByIdAndUpdate,
deleteById,
printAllRecords,
getAllRecords,
}
}

That should be the minimum needed to use this factory to instantiate a data storage structure we can use as a model of data to send to a view. Now that we have a structure to work with let’s start building out the implementation. We will tackle one part of C.R.U.D at a time.

CREATE

Let’s start with createRecord. createRecord will take an object that has the following shape {id:12345, first_name: "Fred", last_name: "Flintstone" , email: "bedrock", gender: "male"}

function createRecord(record) {
dataset.push(record)
return dataset[dataset.length - 1]
}

dataset is an array collection of objects of the above shape (contract) After pushing on to the dataset we return the object by getting the length of the dataset - 1 because index starts at 0.

let’s enforce that we only have the contracted property fields in each object we create.

function dataInterface(props, record) {
/* javascript is weird so you have to check that it's an
object and null seperately because null is an object.
console output
typeof null
"object"
*/
if (typeof record === "object" && record !== null) {
let bool = true
// loop over the record object and return false if any property
// is found on it that isn't part of the interface array above.
props.forEach(prop => {
if (!record.hasOwnProperty(prop)) {
bool = false
}
})
if (!bool) {
console.error("data must conform to this interface")
}
return bool
}
}

Now we can add this and createId to createRecord method so that we can create objects that have an enforced contract and a random id for lookup.

Create Id is pretty simple. We will keep it private because there is no reason for anyone consuming the api to use it.

function createId() {
return Math.random() // create random num betwee 0 and 1
.toString(10)
.substr(2) // slice off 0. from the front of the random number
}
function createRecord(record) {
record.id = createId()
if (dataInterface(interface, record)) {
dataset.push(record)
return dataset[dataset.length - 1]
} else {
return { error: "error creating record" }
}
}
// console
const ds = dataStore([], interfaceProps)
ds.createRecord({
first_name: "Bruce",
last_name: "Banner",
email: "dontmakemeangry@gmail.com",
gender: "male",
})
/**********
returns the object in an array with an id attached.
[
{
id: 123254520957832,
first_name: "Bruce",
last_name: "Banner",
email: "dontmakemeangry@gmail.com",
gender: "male",
}
]
********************/

Now you can not add properties that don’t belong in your dataStore class.

READ

If we want to be able to find a particular record and we have a view that lists all our records. The best way to get at that record is to have an id that we can pass.

function findRecordById(id) {
let foundRecord = null
// check that i exists otherwise return error indicating issue
if (id) {
// checks the entire store for the id
// if found it will return it otherwise it
// returns null
dataset.forEach(r => {
if (r.id == id) {
foundRecord = r
}
})
return foundRecord
} else {
return { error: "please provide an id." }
}
}
// simply return the collection of records;
function getAllRecords() {
return dataset
}

That’s all I’m going to add today. Come back later to learn about how we deal with the UPDATE part of C.R.U.D

gist this post is based on