Building a To-Do List Application

Build a Basic, React, or Angular to-do list web application using Catalyst Advanced I/O Function and Catalyst Data Store that enables you to note down tasks and delete them after they are done.

Configure the Advanced I/O Function

Next, we will begin coding the to-do list application by configuring the function component.

If you have initialized the Advanced I/O function in Java, the functions directory, in this case functions/ToDoList contains:

  • The ToDoList.java main function file
  • The catalyst-config.json configuration file
  • Java library files in the lib directory
  • .classpath and .project dependency files

If you have initialized the Advanced I/O function in Node.js, the function's directory, in this case, functions/to_do_list_function contains:

You will be adding code in the ToDoList.java or index.js file.

The three APIs in the Advanced I/O function that handle the routing between the server and the Data Store are:

  • GET /todo: To obtain the to-do list items from the TodoItems table in the Data Store
  • POST /todo: To create and save a new list item in the Data Store
  • DELETE /todo: To delete a list item from the Data Store

 

Install Express Framework for Node.js

If you are coding in the Node.js platform, you will need to use the Express framework to perform routing operations. To import the Express package in your function's code, you must install the Express dependency in your system.

To install Express.js iin your local machine, navigate to the function's home directory (functions/to_do_list_function) in your terminal and execute the following command:

$ npm install express --save

This will install the Express module and save the dependencies.

catalyst_to_node_express_install

This information will also be updated in the package.json file.

catalyst_todo_package_json

Next, let's add the code in the function file. Copy the Java code and paste it in ToDoList.java in the functions/TodoList directory, or the Node.js code and paste it in index.js in the functions/to_do_list_function directory of your project, and save the file. You can use any IDE of your choice to work with the application's files.

Note: Please go through the code given in this section to ensure you fully understand it. We will discuss the architecture of the function and the client in the next section.
  • View code for ToDoList.java and index.js

    Copied 
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.logging.Level;
    import java.util.logging.Logger;
    import java.io.InputStreamReader;
    
    import javax.servlet.ServletInputStream;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.json.simple.JSONObject;
    import org.json.simple.parser.JSONParser;
    
    import com.catalyst.advanced.CatalystAdvancedIOHandler;
    import com.zc.component.object.ZCObject;
    import com.zc.component.object.ZCRowObject;
    import com.zc.component.object.ZCTable;
    import com.zc.component.zcql.ZCQL;
    
    public class ToDoList implements CatalystAdvancedIOHandler {
     private static final String GET = "GET";
     private static final String POST = "POST";
     private static final String DELETE = "DELETE";
     private JSONObject responseData = new JSONObject();
     private static final Logger LOGGER = Logger.getLogger(ToDoList.class.getName());
    
     @Override
     public void runner(HttpServletRequest request, HttpServletResponse response) throws Exception {
     	try {
     		String uri = request.getRequestURI();
     		String method = request.getMethod();
    //GET method gets data from the TodoItems table in the Data Store.
     		if (method.equals(GET) && uri.equals("/all")) {
     			Integer page = Integer.parseInt(request.getParameter("page"));
     			Integer perPage = Integer.parseInt(request.getParameter("perPage"))	;
     			
     			Integer totalTodos = Integer
     					.parseInt(ZCQL.getInstance().executeQuery("SELECT COUNT(ROWID) FROM TodoItems").get(0)
     							.get("TodoItems", "ROWID").toString());
     			Boolean hasMore = totalTodos > page * perPage;
    
     			ArrayList> todoItems = new ArrayList>();
    
     			ZCQL.getInstance().executeQuery(String.format("SELECT ROWID,Notes FROM TodoItems LIMIT %d,%d",
     					(page - 1) * perPage + 1, perPage)).forEach(row -> {
     						todoItems.add(new HashMap() {
     							{
     								put("id", row.get("TodoItems", "ROWID").toString());
     								put("notes", row.get("TodoItems", "Notes").toString());
     							}
     						});
     					});
     			
     			response.setStatus(200);
     			responseData.put("status", "success");
     			responseData.put("data", new JSONObject() {
     				{
     					put("hasMore", hasMore);
     					put("todoItems", todoItems);
     				}
     			});
    //POST method sends data to persist in the TodoItems table in the Data Store
     		} else if (method.equals(POST) && uri.equals("/add")) {
     			JSONParser jsonParser = new JSONParser();
     			ServletInputStream requestBody = request.getInputStream();
    
     			JSONObject jsonObject = (JSONObject) jsonParser.parse(new InputStreamReader(requestBody, "UTF-8"));
    
     			String notes = jsonObject.get("notes").toString();
    
     			ZCRowObject row = ZCRowObject.getInstance();
     			row.set("Notes", notes);
    
     			ZCRowObject todoItem = ZCObject.getInstance().getTable("TodoItems").insertRow(row);
    
     			response.setStatus(200);
     			responseData.put("status", "success");
     			responseData.put("data", new JSONObject() {
     				{
     					put("todoItem", new JSONObject() {
     						{
     							put("id", todoItem.get("ROWID").toString());
     							put("notes", todoItem.get("Notes").toString());
     						}
     					});
    
     				}
     			});
    //Delete method deletes the selected items from the Data Store
     		} else if (method.equals(DELETE)) {
     			ZCTable table = ZCObject.getInstance().getTable("TodoItems");
    
     			table.deleteRow(Long.parseLong(uri.substring(1)));
     			
     			response.setStatus(200);
     			responseData.put("status", "success");
     			responseData.put("data", new JSONObject() {
     				{
     					put("todoItem", new JSONObject() {
     						{
     							put("id", uri.substring(1));
     						}
     					});
    
     				}
     			});
    
     		} else {
     			response.setStatus(404);
     			responseData.put("status", "failure");
     			responseData.put("message", "Please check if the URL trying to access is a correct one");
     		}
     		
     	
     	} catch (Exception e) {
     		LOGGER.log(Level.SEVERE, "Exception in Main", e);
     		responseData.put("status", "failure");
     		responseData.put("message", "We're unable to process the request.");
    
     	}
     	
     	response.setContentType("application/json");
     	response.getWriter().write(responseData.toString());
    
     }
    
    }
    
    Copied  
    const express = require('express');
    const catalystSDK = require('zcatalyst-sdk-node');
    
    const app = express();
    
    app.use(express.json());
    
    app.use((req, res, next) => {
    	const catalyst = catalystSDK.initialize(req);
    	res.locals.catalyst = catalyst;
    	next();
    });
    
    //GET API. Get existing tasks if any from the server.
    app.get('/all', async (req, res) => {
    	try {
    		const { catalyst } = res.locals;
    
    		const page = parseInt(req.query.page);
    		const perPage = parseInt(req.query.perPage);
    
    		const zcql = catalyst.zcql();
    
    		const hasMore = await zcql
    			.executeZCQLQuery(`SELECT COUNT(ROWID) FROM TodoItems`)
    			.then((rows) => parseInt(rows[0].TodoItems.ROWID) > page * perPage);
    
    		const todoItems = await zcql
    			.executeZCQLQuery(
    				`SELECT ROWID,Notes FROM TodoItems LIMIT  ${
    					(page - 1) * perPage + 1
    				},${perPage}`
    			)
    			.then((rows) =>
    				rows.map((row) => ({
    					id: row.TodoItems.ROWID,
    					notes: row.TodoItems.Notes
    				}))
    			);
    
    		res.status(200).send({
    			status: 'success',
    			data: {
    				todoItems,
    				hasMore
    			}
    		});
    	} catch (err) {
    		console.log(err);
    		res.status(500).send({
    			status: 'failure',
    			message: "We're unable to process the request."
    		});
    	}
    });
    
    // POST API. Contains the logic to create a task
    app.post('/add', async (req, res) => {
    	try {
    		const { notes } = req.body;
    		const { catalyst } = res.locals;
    
    		const table = catalyst.datastore().table('TodoItems');
    
    		const { ROWID: id } = await table.insertRow({
    			Notes:notes
    		});
    
    		res.status(200).send({
    			status: 'success',
    			data: {
    				todoItem: {
    					id,
    					notes
    				}
    			}
    		});
    	} catch (err) {
    		console.log(err);
    		res.status(500).send({
    			status: 'failure',
    			message: "We're unable to process the request."
    		});
    	}
    });
    
    // DELETE API. Contains the logic to delete a task.
    app.delete('/:ROWID', async (req, res) => {
    	try {
    		const { ROWID } = req.params;
    		const { catalyst } = res.locals;
    
    		const table = catalyst.datastore().table('TodoItems');
    
    		await table.deleteRow(ROWID);
    
    		res.status(200).send({
    			status: 'success',
    			data: {
    				todoItem: {
    					id: ROWID
    				}
    			}
    		});
    	} catch (err) {
    		console.log(err);
    		res.status(500).send({
    			status: 'failure',
    			message: "We're unable to process the request."
    		});
    	}
    });
    
    module.exports = app;
    

The functions directory is now configured.