Saving User Data - ldco2016/microurb_web_framework GitHub Wiki
When we start to think about saving data tied to a user, there is actually two scenarios we have to worry about.
There is a strong responsibility of that ID property. If a user has an id, it means that a representation of that user already exists on the backend server. So if we are trying to save a user that already has an id, we are trying to update a record.
If we are trying to update a record, we want to make a PUT request to /users/:id
and then attach all the update information with it.
However, if the user does not have an id, that means this must be a brand new user, they are not persisted on the server and so when we make this network request, we want to make an entirely new record. And if we are following RESTful conventions, that means we want to make a POST request along with all of our information about this particular user.
So inside of the save()
method we want to check to see if the user has an id, if so, we are going to make a PUT request, if they do not have an id, we make a POST request.
With the above in mind, this is how we put our save()
method together:
save(): void {
if (this.get('id')) {
// put
} else {
// post
}
}
PUT Request First
save(): void {
if (this.get('id')) {
// put
axios.put(`http://localhost:3000/users/${this.get('id')}`);
} else {
// post
}
}
Above you see that we have a duplication of this.get('id')
which goes against the DRY principle or Don't Repeat Yourself. So we can extract that above like so:
save(): void {
const id = this.get('id');
if (id) {
// put
axios.put(`http://localhost:3000/users/${id}`);
} else {
// post
}
}
So this way we no longer have the duplicated function call and can refactor this code snippet as you see above. Now as a second argument we pass in all the information about the user. All the information on the user is inside the data
property, so as a second argument we can pass in this.data
like so:
save(): void {
const id = this.get('id');
if (id) {
// put
axios.put(`http://localhost:3000/users/${id}`, this.data);
} else {
// post
}
}
POST Request Second
save(): void {
const id = this.get('id');
if (id) {
// put
axios.put(`http://localhost:3000/users/${id}`, this.data);
} else {
// post
axios.post('http://localhost:3000/users', this.data);
}
}
Take note that because we are not passing an id to POST there is no need to use backticks in axios.post()
.
We are currently hardcoding this localhost string inside of the user class, its almost definitely likely that the url will not always be localhost:3000, so in the future we will handle that in a better way.
Testing It Out
- We need to try to create a user that already has a backend representation, update information on that user and save it.
- We create a brand new user without an id, give it some information and attempt to save it as well.
To test the first case we go inside index.ts
:
import { User } from './User';
const user = new User({ id: 1 });
user.set({ name: 'NEW_NAME', age: 9999 });
user.save();
We should see an update to the db.json
file when the above snippet is saved and we should also see a PUT
request that indicates we are updating a record, so we see the response we get back from the server:
And in the db.json
file, we should see the new name
and new age
:
{
"users": [
{
"id": 1,
"name": "NEW_NAME",
"age": 9999
}
]
}
Next, we need to ensure we have the ability to create a new user without the id and if we save it, it creates a new user inside the db.json
file:
import { User } from './User';
const user = new User({ name: 'new record', age: 0 });
user.save();
So we save this and check out our network request in our Chrome console:
And if we look inside our db.json
file:
{
"users": [
{
"id": 1,
"name": "NEW_NAME",
"age": 9999
}
},
{
"name": "new record",
"age": 0,
"id": 2
}
]
}
Awesome! We now have the entire spectrum of abilities tied to our user. We have the ability to represent properties of a user, we can get
them and we can set
them. We have some eventing functionality to set up callbacks and we have the ability to fetch data tied to a user and save information tied to a user as well.
There was not much TypeScript written above. We are just creating a mega user class of sorts. We have a user class that does a lot of different things.
We will refactor our User
to instead make use of composition by generating separate different objects of each of the responsibility our User
has.
So at a high level view, we achieved the Single Responsibility Principle through the use of independent classes to handle attributes, events and data sync. We wired these classes together into the User
class by using the Facade design pattern. The Facade design pattern kind of violates the Single Responsibility Principle by definition.