9. Your First Fullstack App!
Last updated
Last updated
Remember, the model is the layer of the application that directly manipulates the data. It provides methods that the controllers can use to execute those changes whenever the frontend makes the appropriate request.
Thus far, our server application used an in-memory array as a "database". But this data is not persistent — each time we restart the server, all data created during that running session is lost.
Now that we have a database and can connect to it using Knex, we can refactor our model to use that database and have our data persist.
Open the repository and start with the 2-fellow-tracker-final-mvc/
version of the project.
We need to create a database and then replace all logic associated with the in-memory fellows
array used in the server/model/Fellow
file with knex
code to connect with our new database.
Start your Postgres server and create a database called fellows_tracker
using TablePlus or the psql
CLI.
Execute the SQL statement below to create the table structure
Return to the repository and install pg
and knex
into the server directory. Then initialize knexfile.js
:
Modify knexfile.js
to use pg
and the fellow-tracker
database with a valid username and password:
Create a knex.js
file in the model/
directory that exports a knex
object. Remember, this object will be shared by every model in our application to execute SQL statements to the database.
make sure the import statement for knexfile.js
is accurate
Knex is configured to connect to our database! Now we can execute SQL commands to the database using the knex.raw()
method.
Head over to the server/model/Fellow.js
file and remove all logic associated with the in-memory fellows
array, replacing it with the appropriate knex
and SQL logic.
Remember, knex.raw()
returns a promise that resolves to an object with a rows
array property, even if there is only one value returned by the query!
Here's what you need to do:
Import knex
into the Fellow
model
Remove the import of the getId
function, the database will handle automatically setting the id
property
Remove the fellows
array
All methods need to be async
to use await knex.raw()
Every function needs to return something (use RETURNING *
on all non-SELECT
queries)
Fellow.create
should return the new fellow
Fellow.list
should return an array of all fellows
Fellow.find
should return a single fellow object (the first object in the rows
array)
Fellow.editName
should return the updated fellow object
Fellow.delete
should return the deleted fellow object
Refactor fellowControllers.js
to await
everything and double check that all values returned from the model are being handled properly.
Here's where things get fun! We can add a posts
table to our database that has a one-to-many relationship with our fellows
table. Each post must have a fellow_id
reference pointing to the fellow that created it.
Create a posts table that references the fellows table
Create a Post
model with methods:
create(content, fellowId)
findPostsByFellowId(fellowId)
delete(id)
deleteAllPostsForFellow(fellowId)
list()
— only used for testing
findById(id)
— only used for testing
Make the postControllers
Define endpoints in index.js
. Make sure to follow REST principles! Since every post is owned by a fellow, the endpoint should communicate that:
POST /api/fellows/:fellowId/posts
→ Post.create
GET /api/fellows/:fellowId/posts
→ Post.findPostsByFellowId
DELETE /api/fellows/:fellowId/posts/:postId
→ Post.delete
DELETE /api/fellows/:fellowId/posts/
→ Post.deleteAllPostsForFellow
Test with postman
Add frontend components and fetching on the FellowDetails
page
You'll need the following components:
A form for adding a new post
A list of posts with delete buttons for each post
Event handlers / fetching:
useEffect
now also fetches fellow posts -> GET /api/fellows/:fellowId/posts
new post form on submit -> POST /api/fellows/:fellowId/posts/
list item delete button on click -> DELETE /api/posts/:id
When deleting a fellow, we need to delete posts first. How can we ensure that this happens in the right order?