M09 - Full Stack Example - Todo App
Introduction
This material describes the principles of creating a Todo Application. The application implements the functionality of a simple fullstack application. The user can add and delete todos. The todos are stored in the MongoDB database and are visible on the web page.
The image below shows an empty to-do list, when the page has been accessed for the first time.
The image below shows a few added tasks.
Application architecture
The image below shows the architecture of the application.
- Application UI uses HTML/CSS and JavaScript.
- Information is passed between the user interface and the server in JSON format.
- Node.js and Express technologies are used on the server side.
- Todos are stored in the MongoDB database in the cloud service.
Database
This example application uses MonboDB database located in the Mongo Atlas cloud service. When creating this example application, you must have a username and password to the service. Also find out the connection address of your service database. You will need it later when following this material.
Look more information from MongoDB materials.
Server side programming
On the server side, Node.js programming and the necessary npm packages are used for programming.
Project
Create a new Node.js project
1 2 3 |
|
npm packages
Install following npm packages to your project.
1 2 3 4 |
|
Note
Read more about Express cors middleware.
Express application
The implementation starts by making a simple Express application using Node.js to the index.js file defined in the project. The goal is to get the server to respond to a single GET request.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
Now the application should respond to the following request in the web browser: http://localhost:3000/todos, returning only the Todos
string to the visible body of the browser.
Connection to MongoDB database
Add a needed connection to MongoDB. The project uses the Mongoose library.
Add the programming below to // Mongo here...
comment in the programming above. Remember to use your own MongoDB database address. MongoDB is used with a Mongoose. A possible successful or unsuccessful connections is visible in the console.
1 2 3 4 5 6 7 8 9 10 11 |
|
Mongoose Scheema and Model
The Todo database will be used through Todo
model. Mongoose's Schema will be used to create a Mongoose Model.
Add the programming below to // Mongoose Schema and Model here...
comment in the programming above. In programming, a new Schema and the model that uses it are created. Todo documents are saved in the todos
collection of the opened MongoDB database.
1 2 3 4 5 6 7 8 9 |
|
Routes, Todos API
The last trick is to create/add the routes provided by the server, i.e. to form Todo's API, which is provided for user interface programming. Use VS Code's RestClient or Postman and test the functionality of all defined routes before starting client-side programming.
Add the routes below to the // Routes here...
comment section of the previous programming.
POST
POST route adds a new Todo document to the database. The todo text is obtained from the client application via the request.body
object. Next, a new Todo object is created using the Mongoose model. Finally, the created todo
object is stored in the MongoDB database and the stored object is returned back to the client application.
1 2 3 4 5 6 7 8 |
|
Functional testing of the route via VS Code is visible below. Note that the server also returns the _id
of the todo in the MongoDB database in the todo
object. This _id
value will be used later to remove the todo.
Create your own post_todo.rest
file (below) and send request to server side from VS Code.
Note
Remember check that the saving a new todo has successfully done in the MongoDB database.
GET
GET route returns all the Todo documents from the database. Next, add a get route that reads the MongoDB database using the Todo
Mongoose model. An empty object {}
is used as the search condition, thus all objects are returned from the base to the calling program.
1 2 3 4 |
|
Functional testing via VS Code is visible in below image.
GET
GET route (parametric): returns the Todo document according to the id from the database. Next, add a get route that uses the id value of the todo object when specifying the route. This unique id value is searched for in the MongoDB database using the findById
function. Either a found todo object or a 404 status value (not found) is returned to the calling program.
1 2 3 4 5 |
|
Below you can see the functional testing of the route via VSCode.
Note
Remember also try an id
value that is not in use in your MongoDB todo
collection. Note the returned 404 Not Found value and the Content-Length value of zero.
DELETE
DELETE route deletes the Todo document according to the id from the database. The id value is passed as a route parameter. The corresponding todo object is retrieved from the MongoDB database using the findByIdAndRemove
function of the Todo
model. The route returns a deleted todo object or a 404 status code (not found) to the calling program.
Use below code if mongoose 8 or above version is used. Check versio number from package.json
file.
1 2 3 4 5 6 7 8 |
|
Use below code if mongoose version below 8 is used.
1 2 3 4 5 |
|
Below you can see the functional testing of the route via VSCode.
UI with HTML/CSS and JavaScript
The user interface must be built using HTML, CSS and JavaScript technologies.
HTML
Create a new folder and index.html
file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
Note a folliwing from above html file
- own made
styles.css
will be used (created later) - own made
code.js
will be used (created later) - will call
init()
function when a HTML page is loaded - new todo text can be found via
newTodo
id - pressing the
Add
button calls theaddTodo
function - todo items are displayed in
todosList
- possible messages can be displayed in the
infoText
CSS
You can use the style definitions below, or create your own even nicer personal styles for your own todo application.
Create a new styles.css
file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
|
JavaScript
Next, the programming of the user interface must be carried out. Let's start the task by implementing the init
function, which is called when the entire HTML code has been rendered into the body of the web browser, i.e. the onload
event of the body has taken place. See previous HTML code and line 9.
In the programming below, the infoText
id is retrieved from the HTML and Loading todos, please wait...
text will be visible.
1 2 3 4 5 |
|
Now you will need to run the client-side programming (HTML, CSS and JavaScript) through the right server, so that the data can be downloaded successfully to the Node.js application. One good option is to install the LiveServer add-on for Visual Studio Code. Of course, you can always transfer files after changes to e.g. the student server, but it's tedious in the middle of programming the application.
Install the LiveServer add-on and start your application by pressing the "Go Live" button from the Visual Studio Code view (when writing the material, the button is visible at the bottom right of the bottom bar). Or right-click when the HTML file is open and select "Open with Live Server".
Now you should be able to see the UI of the application, with the text related to the data downloading visible.
Next start downloading data from the server. In addition to the JavaScript programming, add the loadTodo
function below and uncomment the function call from the previous programming, so that the function is called from the init
function.
1 2 3 4 5 6 7 |
|
Save and refresh the browser view.
The image below shows todos objects loaded to the web browser console. Please note that you may see a different number of todos (depending on how many of them you have added to the database).
In the application, new LI
elements are made to represent one todo item. In the implementation, it is necessary to make these LI elements visible after the application is started (i.e. all todo objects are retrieved from the base) and whenever a new todo is added to the application. Thus, this same "routine" is needed in at least two parts of the application. Therefore, you should make your own function and call it whenever necessary.
Add the following createTodoListItem
function to your JavaScript. The function takes a new todo object as a parameter, i.e. an object that always contains the text and id of the todo.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
|
Note
Look above code comments very carefully!
That's it, now we have a function that can be used to create a new LI element in the todosList
in the user interface whenever necessary with the data of the todo object. Function will be used in the next showTodos
function.
In the earlier loadTodos
function, uncomment the showTodos
function call so that the function below is called.
Add below showTodos
function to your JavaScript file. At first, the programming retrieves the todosList and infoText items from the HTML file and updates their values according to the situation.
If todos objects have been downloaded from the server, they are created/showed using the forEach
loop. In each round of iteration, a new LI element is created using the above createTodoListItem
function. The created LI elements are connected (as children) to the UL element todosList
in the user interface.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
Save codes and check how the situation looks in the application in the browser. All todo objects should be visible in the user interface.
Pressing the Add button in the UI calls the addTodo()
function (check your HTML). Next, add the addTodo()
function below to your JavaScript programming.
The programming looks for the todo text in the user interface and uses fetch to communicate todos route on the server using the POST method. This can be achieved by using the object with the fetch command. Note that we need to convert the JSON data into a string using the JSON.stringify
function and specifying the data type to be sent as JSON
. The server reads data from request.body
and we attach the data to be sent to it (check the server-side programming made earlier for the POST route).
User interface programming is waiting for feedback from the server. After receiving it, a new LI element is created and added to the user interface.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
Test and add a few Todos.
The last trick is to add a delete task to the app. The x character inside the LI element is defined to call the removeTodo
function. See the previous implementation of the createTodoListItem
function.
Todo API interface on the server is called with the DELETE method to the todos
route with the parameter (todo id). First delete the data from the server/database and after that the LI element also deleted from the user interface (it has a unique id because it is also unique in the MongoDB database). The user interface displays the text No Todos
, if all tasks have been deleted.
Add below code to your app.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
Try delete a few of your Todos.
Summary
The todo application presented in this material implements the principles of a simple fullstack application. It includes the user interface, connection to the server, server-side programming and data storage in the database. The application is very simple and does not contain e.g. managing error situations or editing tasks.
Another good addition would be to implement users for the todos as well. For example, you could register for the application and each user would have the opportunity to add/change/delete only their own todos.
You can think about these things in your own practice work.