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
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)idfirst_namelast_namegender*/// this will be passed into the dataStore on instantiationconst interfaceProps = ["first_name", "last_name", "email", "gender", "age"]function dataStore(dataset, interface) {// PRIVATE STUFF// PROPERTIESlet sortDirectionlet sortProp// RANDOM ID GENERATORfunction createId() {}// SETTERSfunction setSortProp(prop) {}function setSortDirection(direction) {}// SORT DATASET METHODfunction sortDataset() {}// INTERFACE INFORCEMENT FUNCTIONSfunction dataInterface(props, record) {}function conformToInterface(props, record) {}// CREATEfunction createRecord(record) {}// READfunction findRecordById(id) {}function getAllRecords() {}// UPDATEfunction findByIdAndUpdate(id, updatedRecord) {}// DELETEfunction deleteById(id) {}// UTIL METHODSfunction printAllRecords(direction, prop) {}// RETURN PUBLIC METHODSreturn {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 anobject and null seperately because null is an object.console outputtypeof 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" }}}// consoleconst 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 issueif (id) {// checks the entire store for the id// if found it will return it otherwise it// returns nulldataset.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