# Python -------------------------------------------------------------------------------- title: "Introduction" description: "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." last_updated: "2026-03-18T07:41:08.690Z" source: "https://docs.catalyst.zoho.com/en/tutorials/todo-list/python/introduction/" service: "All Services" related: - Project Directory Structure (/en/cli/v1/project-directory-structure/introduction/) - Java SDK (/en/sdk/java/v1/overview/) - Node.js SDK (/en/sdk/nodejs/v2/overview/) - Python SDK (/en/sdk/python/v1/overview/) -------------------------------------------------------------------------------- # To-Do List Application ### Introduction This tutorial will help you build a simple, single-page to-do list web application in Catalyst. The application will enable users to note down tasks to be done, then delete them after they are completed. The client application will look like this: You can access a working application of each type and test their functioning here: * {{%bold%}}{{%link href="https://to-do-basic.catalystserverlessapp.com/app/" %}}Basic web app{{%/link%}}{{%/bold%}} * {{%bold%}}{{%link href="https://to-do-react.catalystserverlessapp.com/app/" %}}React web app{{%/link%}}{{%/bold%}} * {{%bold%}}{{%link href="https://to-do-angular.catalystserverlessapp.com/app/" %}}Angular web app{{%/link%}}{{%/bold%}} The to-do list application utilizes components from the following Catalyst components: 1. {{%link href="/en/serverless/getting-started/introduction/" %}}**Catalyst Serverless**{{%/link%}}: - {{%link href="/en/serverless/help/functions/advanced-io/" %}}**Advanced I/O Function**{{%/link%}}: The Advanced I/O function can be coded in the {{%link href="/en/sdk/java/v1/overview/" %}}Java{{%/link%}}, {{%link href="/en/sdk/nodejs/v2/overview/" %}}Node.js{{%/link%}}, or {{%link href="/en/sdk/python/v1/overview/" %}}Python{{%/link%}} programming environment. It contains the APIs that enable the users to create, view, and delete list items. 2. {{%link href="/en/cloud-scale/getting-started/introduction/" %}}**Catalyst Cloud Scale**{{%/link%}}: - {{%link href="/en/cloud-scale/help/data-store/introduction/" %}}**Data Store**{{%/link%}}: To store the to-do list items in a table. - {{%link href="/en/cloud-scale/help/zcql/introduction/" %}}**ZCQL**{{%/link%}}: To fetch data from the Data Store through querying. - **Client**: The front end of the application that is hosted on Catalyst through {{%link href="/en/cloud-scale/help/web-client-hosting/introduction/" %}}web client hosting{{%/link%}}. You can initialize the web client as a basic Catalyst web app, an Angular app, or a React app. This tutorial will cover the steps for all three types of web clients. We will be using the native plugins offered by Catalyst to easily build, test, and deploy Angular and React apps. We will use the {{%link href="https://console.catalyst.zoho.com/" %}}Catalyst web console{{%/link%}} and the {{%link href="/en/cli/v1/cli-command-reference/" %}}Catalyst Command Line Interface{{%/link%}} (CLI) to build this application. You will be given the code for the files to be included in the function and client components in this tutorial. You will just have to copy the code given here and paste it into the appropriate files as directed. #### Application Architecture The to-do list application’s functioning can be described as follows: * **Adding a note**: A user enters a note in the client application and adds it as a to-do list item. The client component triggers the Serverless Advanced I/O function. The function saves the note as a record in the table in the Cloud Scale Data Store using an HTTP POST request. The corresponding response (note) will be added to the existing to-do list. * **Viewing a note**: When the user opens the application,the client component triggers the Advanced I/O function to fetch existing to-do list items from the Data Store using a HTTP GET request. This list is then displayed to the user. * **Deleting a note**: When the user clicks on a note in the client application, it triggers an HTTP DELETE request. The corresponding record from the Data Store is deleted, ensuring the note is also deleted from the client. The updated to-do list is displayed in the client. -------------------------------------------------------------------------------- title: "Prerequisites" description: "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." last_updated: "2026-03-18T07:41:08.690Z" source: "https://docs.catalyst.zoho.com/en/tutorials/todo-list/python/prerequisites/" service: "All Services" related: - CLI Command Reference (/en/cli/v1/cli-command-reference/) - Install CLI (/en/getting-started/installing-catalyst-cli/) - Login CLI (/en/cli/v1/login/login-from-cli/) -------------------------------------------------------------------------------- # Prerequisites Before you begin building the application, you must have the following prerequisites installed on your system: 1. {{%link href="/en/cli/v1/cli-command-reference/" %}}**Catalyst CLI**{{%/link%}}<br />Catalyst CLI contains a host of tools that enable you to initialize, develop, test, and deploy the components of your application from your local machine. We will be working with Catalyst CLI in this tutorial.<br /><br />You must perform the following actions: * **Install Catalyst CLI**: Catalyst CLI is installed through NPM. You must therefore have NPM and Node.js installed on your system before you install the CLI. Refer to the {{%link href="/en/getting-started/installing-catalyst-cli/" %}}Install Catalyst CLI help page{{%/link%}} for details on the pre-requisites and the steps to install it. * **Log in to Catalyst CLI**: After you install Catalyst CLI, you must authenticate the CLI with your Catalyst account before using it. Refer to the {{%link href="http:///en/cli/v1/login/login-from-cli/" %}}CLI Login help page{{%/link%}} for the steps to login from Catalyst CLI and the various options available for it. 2. **Install Python 3.9**<br />Catalyst currently supports any version of Python up to 3.9. If you have a higher version already installed in your system, ensure that you have Python 3.9 installed as well. You can download the package from this {{%link href="https://www.python.org/downloads/release/python-3913/" %}}page{{%/link%}}. 3. **Any IDE tool for Python function and client code development**<br />You can use any IDE to work with the Advanced I/O function and the client code. Some popular choices include Visual Studio Code, IDLE, PyCharm, and Sublime Text. Download and install an IDE of your choice on your system. {{%note%}}{{%bold%}}Note:{{%/bold%}} If you have decided to configure the client component of the Catalyst application as an Angular app, ensure you have the Angular CLI installed. You can find detailed instructions on how to do so in the {{%link href="https://angular.io/guide/setup-local#install-the-angular-cli" %}}Angular CLI documentation{{%/link%}}.{{%/note%}} {{%info image="/images/tutorials/todo-list/vscode.png"%}}If you are a Visual Studio Code IDE user, you can install the {{%bold%}}Catalyst Tools{{%/bold%}} extension, and use your IDE itself in place of the CLI. You can find more details about the Catalyst VS Code extension from this {{%link href="/en/catalyst-extensions/vs-code-extension/introduction/" %}}help section{{%/link%}}.{{%/info%}} -------------------------------------------------------------------------------- title: "Create a project" description: "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." last_updated: "2026-03-18T07:41:08.693Z" source: "https://docs.catalyst.zoho.com/en/tutorials/todo-list/python/create-project/" service: "All Services" related: - Project Directory (/en/cli/v1/project-directory-structure/introduction) - Set up Catalyst Projects (/en/getting-started/catalyst-projects) -------------------------------------------------------------------------------- # Create a Project Let’s {{%link href="/en/getting-started/catalyst-projects/#creating-a-catalyst-project" %}}create a Catalyst{{%/link%}} project from the Catalyst console. 1. Log in to the {{%link href="https://console.catalyst.zoho.com/baas/index" %}}Catalyst console{{%/link%}} and click **Create New Project**. <br /> 2. Enter the project’s name as "**ToDoList**" in the pop-up window, and click **Create**. <br /> Your project will be created; once ready, click **Access Project** to continue. <!-- <br /> <br /> --> -------------------------------------------------------------------------------- title: "Create a table" description: "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." last_updated: "2026-03-18T07:41:08.694Z" source: "https://docs.catalyst.zoho.com/en/tutorials/todo-list/python/create-table/" service: "All Services" related: - Data Store (/en/cloud-scale/help/data-store/introduction/) -------------------------------------------------------------------------------- # Create a Table Let’s now create a table in the Data Store of the *ToDoList* project. As discussed earlier, this table is used to store to-do list items created by the user. To create a table: 1. Navigate to the *Cloud Scale* section of the console, and click **Start Exploring**. <br /> 2. Navigate to **Data Store** under *Storage* and click **Create a New Table**. <br /> 3. Enter the table’s name as "**TodoItems**", then click **Create**. <br /> {{%note%}}{{%bold%}}Note:{{%/bold%}} Ensure that you enter the name exactly as instructed, because the application's code contains the same name.{{%/note%}} The table is now created and displayed in the *Data Store* page. ### Create Columns Now, let us create a column in the table to store the to-do list items. 1. Click **New Column** in the *Schema View* section of the table. <br /> 2. Enter the column’s name as "**Notes**". Select the data type as **Text** and click the **Is Mandatory** toggle switch to enable it. <br /> You can learn about the various data types supported by Catalyst and the other properties of a column from the {{%link href="/en/cloud-scale/help/data-store/columns" %}}Data Store help page{{%/link%}}. {{%note%}}{{%bold%}}Note:{{%/bold%}} Ensure that you enter the name exactly as instructed, because the application's code contains the same name.{{%/note%}} 3. Click **Create**. The column is now created and listed in the *Schema View* section. <br /> You have now configured all the necessary components from the console. You will now be working on coding the application from your local system. -------------------------------------------------------------------------------- title: "Initialize the project" description: "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." last_updated: "2026-03-18T07:41:08.694Z" source: "https://docs.catalyst.zoho.com/en/tutorials/todo-list/python/initialize-project/" service: "All Services" related: - Initialize CLI Resources (/en/cli/v1/initialize-resources/introduction) - Project Directory Structure (/en/cli/v1/project-directory-structure/introduction) -------------------------------------------------------------------------------- # Initialize the Project from the CLI You can now begin working on your Catalyst project from the CLI. The first step is to initialize the project in an empty directory. This will be the home directory of your project and all of the project files will be saved in it. You can learn more about this from the {{%link href="/en/cli/v1/project-directory-structure/introduction/" %}}Project Directory Structure help page{{%/link%}}. You can learn about initializing a project in detail from the {{%link href="/en/cli/v1/initialize-resources/introduction" %}}CLI help documentation{{%/link%}}. For the to-do list application, we will initialize the Advanced I/O function first, and initialize the Client component later. {{%note%}}{{%bold%}}Note:{{%/bold%}} Ensure that you enter the function's package name or class name exactly as instructed, because the application's code contains the same name.{{%/note%}} 1. Create a folder for the project on your local machine and navigate to it from the terminal. 2. Initialize a project by executing the following command from that directory: {{%cli%}}catalyst init{{%/cli%}} 3. Navigate using the **arrow keys** and select your preferred portal and press the **Enter** key. If you have no other organizations associated with the account, then the default one will be automatically selected. <br /> You can find out more about Catalyst's multi-org portal feature from this {{%link href="/en/getting-started/catalyst-projects/#access-the-multi-org-portal" %}}help document{{%/link%}}. 4. The CLI will now ask you to associate a Catalyst project with the directory. Associate it with the project that we created earlier from the console. Select **ToDoList** from the list and press **Enter**. <br /> 5. Select **Functions** using the **space bar**. Press the **Enter** key to initialize. As mentioned earlier, we will initialize and setup the client after we configure the function. <br /> 6. The CLI will initiate the function setup. Select **AdvancedIO** as the function type. <br /> 7. Select **Python 3.9** as the function stack. <br /> 8. Enter "**to_do_list_function**" as the package name, and "**main.py**" as the entry point, and press **Enter**. You can also just press **Enter**, to use the default names generated by Catalyst, or enter any name of your preference. <br /> The project and function initialization is now complete. The current project directory will be created in the standard structure. The {{%link href="/en/cli/v1/project-directory-structure/functions-directory" %}}functions directory{{%/link%}} will be created in your project directory with the configuration files and dependencies, along with the {{%link href="/en/cli/v1/project-directory-structure/catalyst-json" %}}{{%badge%}}**catalyst.json**{{%/badge%}}{{%/link%}} and a hidden {{%badge%}}**.catalystrc**{{%/badge%}} file. This is the structure of the *ToDoList* project’s directory without the Client component. <br /> <br /> -------------------------------------------------------------------------------- title: "Configure the Functions Directory" description: "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." last_updated: "2026-03-18T07:41:08.694Z" source: "https://docs.catalyst.zoho.com/en/tutorials/todo-list/python/configure-function/" service: "All Services" related: - Advanced I/O Functions (/en/serverless/help/functions/advanced-io/) - Functions Directory Structure (/en/cli/v1/project-directory-structure/functions-directory) -------------------------------------------------------------------------------- # Configure the Advanced I/O Function Next, we will begin coding the to-do list application by configuring the function component. The {{%link href="/en/cli/v1/project-directory-structure/functions-directory" %}}function’s directory{{%/link%}}, {{%badge%}}functions/to_do_list_function{{%/badge%}} contains: * The {{%badge%}}main.py{{%/badge%}} main function file * The {{%badge%}}catalyst-config.json{{%/badge%}} configuration file * The {{%badge%}}requirements.txt{{%/badge%}} file to mention any external libraries that you might add. You will be adding code in the {{%badge%}}main.py{{%/badge%}} 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 Next, let’s add the code in the function file. Copy the Python code and paste it in {{%badge%}}main.py{{%/badge%}} in the {{%badge%}}functions/to_do_list_function{{%/badge%}} directory of your project, and save the file. You can use any IDE of your choice to work with the application’s files. {{%note%}}{{%bold%}}Note:{{%/bold%}} 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.{{%/note%}} {{% panel_with_adjustment header="main.py" footer="button" class="language-python line-numbers" scroll="set-scroll" %}}import json import zcatalyst_sdk import logging from flask import Request, make_response, jsonify logger = logging.getLogger() def handler(request: Request): try: app = zcatalyst_sdk.initialize() logger = logging.getLogger() if request.path == "/add" and request.method == 'POST': req_data = request.get_json() notes = req_data.get("notes") table = app.datastore().table('TodoItems') row = table.insert_row({ 'Notes': notes }) todo_item = {'id': row['ROWID'], 'notes': notes} response_data = { 'status': 'success', 'data': { 'todoItem': todo_item } } return make_response(jsonify(response_data), 200) elif request.path == "/all" and request.method == 'GET': page = request.args.get('page') per_page = request.args.get('perPage') page = int(page) logger.info(page) per_page = int(per_page) logger.info(per_page) zcql_service = app.zcql() row_count = zcql_service.execute_query('SELECT COUNT(ROWID) FROM TodoItems') row_id = int(row_count[0]['TodoItems']['COUNT(ROWID)']) has_more = int(row_id) > (page) * (per_page) logger.info(has_more) query_result = zcql_service.execute_query(f'SELECT ROWID, Notes FROM TodoItems LIMIT {(page - 1) * per_page + 1},{per_page}') todo_items = [{'id': item['TodoItems']['ROWID'], 'notes': item['TodoItems']['Notes']} for item in query_result] get_resp = { 'status': 'success', 'data': { 'todoItems': todo_items, 'hasMore': has_more } } return make_response(jsonify(get_resp), 200) elif request.method == 'DELETE': row_id = request.path[1:] if row_id: datastore_service = app.datastore() table_service = datastore_service.table("ToDoItems") table_service.delete_row(row_id) row_response = { 'status': 'success', 'data': { 'todoItem': { 'id': row_id } }} logging.info(row_response) return make_response(jsonify(row_response), 200) else: message = { 'status': 'failure', 'error': 'Please provide a valid rowid in the request path' } return make_response(jsonify(message), 400) else: print('working') except Exception as err: logger.error(f"Exception in to_do_list_function :{err}") response = make_response(jsonify({ "error": "Internal server error occurred. Please try again in some time." }), 500) return response {{% /panel_with_adjustment %}} The functions directory is now configured. -------------------------------------------------------------------------------- title: "Initialize the Client" description: "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." last_updated: "2026-03-18T07:41:08.695Z" source: "https://docs.catalyst.zoho.com/en/tutorials/todo-list/python/initialize-client/" service: "All Services" related: - Client Directory Structure (/en/cli/v1/project-directory-structure/client-directory) - Initialize the Client (/en/cli/v1/initialize-resources/initialize-client/) -------------------------------------------------------------------------------- # Initialize the Client Now, let's initialize the client. You can initialize the client in one of the three types supported by Catalyst: 1. **Basic web app**: The basic client application is a simple version of the Catalyst client that is created without any external frameworks or libraries. 2. **Angular web app**: You can choose this client type if you prefer to create the To-Do List app using the Angular framework. 3. **React web app**: You can choose this client type if you prefer to create the To-Do List app using the React framework. {{%note%}}{{%bold%}}Note{{%/bold%}}: Catalyst provides native plugin support to create React and Angular apps. These plugins also handle several operations related to setting up, testing and deploying your applications.{{%/note%}} Since, you are initializing the client after the project has already been initialized, you can set it up independently by executing the following command from your project directory: {{%cli%}}catalyst client:setup{{%/cli%}} Use the arrow keys and select your preference by pressing {{%bold%}}Enter{{%/bold%}}. Based on your choice, navigate to the relevant section for the steps to initialize the web app: {{%tabs%}} {{%tab "Basic web app" %}} #### Initialize the client as a Basic web app If you chose {{%bold%}}Basic web app{{%/bold%}} while setting up the client using the Catalyst command {{%badge%}}catalyst client:setup{{%/badge%}}, Catalyst will initialize the client accordingly. Enter "{{%bold%}}ToDoApp{{%/bold%}}" as the name of the name for the client package and press {{%bold%}}Enter{{%/bold%}}. The client is now successfully initialized as a Basic web application. The {{%link href="/en/cli/v1/project-directory-structure/client-directory" %}}client directory{{%/link%}} will be created in the standard structure in the project directory. This is the structure of the ToDoList project's directory if the client is initialized as a basic web app. <br /> <br /> {{%/tab%}} {{%tab "Angular web app"%}} #### Initialize the client as an Angular web app If you chose to set up the client as an Angular app, Catalyst will initialize it using the {{%bold%}}Catalyst Angular plugin{{%/bold%}} ({{%link href="https://npm.io/package/zcatalyst-cli-plugin-angular" %}}{{%badge%}}zcatalyst-cli-plugin-angular{{%/badge%}}{{%/link%}}). 1. Name your Angular app as "{{%bold%}}app{{%/bold%}}", then type "{{%bold%}}Y{{%/bold%}}" and press the {{%bold%}}Enter{{%/bold%}} key to enable the auto-completion feature. This will provide auto-completion for your Angular terminal commands.<br /> 2. You can choose to share your application data with the Angular by entering "{{%bold%}}Y{{%/bold%}}" or opt out of it by entering "{{%bold%}}N{{%/bold%}}", then press {{%bold%}}Enter{{%/bold%}}. Since the application does not require Angular routing, type "{{%bold%}}N{{%/bold%}}" and press {{%bold%}}Enter{{%/bold%}}. 3. For this application we will be using CSS style sheet. Press the {{%bold%}}Enter{{%/bold%}} key after choosing CSS.<br /> 4. Now all the required Angular application files, configuration files, and dependencies will be installed through the Catalyst Angular plugin. 5. The client is now successfully initialized as an Angular web application. The {{%link href="/en/cli/v1/project-directory-structure/client-directory" %}}client directory{{%/link%}} will be created in the standard structure in the project directory. This is the structure of the ToDoList project's directory if the client is initialized as an Angular app. <br /> <br /> {{%/tab%}} {{%tab "React web app" %}} #### Initialize the client as a React web app If you chose to set up the client as a React app, Catalyst will initialize the React app using the Catalyst React plugin ({{%link href="https://www.npmjs.com/package/zcatalyst-cli-plugin-react" %}}{{%badge%}}zcatalyst-cli-plugin-react{{%/badge%}}{{%/link%}}). 1. Next you will be prompted to choose the type of React application. Choose "{{%bold%}}JavaScript{{%/bold%}}" as the type for this tutorial. 2. Name your React application as "{{%bold%}}app{{%/bold%}}," and press {{%bold%}}Enter{{%/bold%}}. All the required React packages such as {{%badge%}}react{{%/badge%}}, {{%badge%}}react-dom{{%/badge%}}, and {{%badge%}}react-scripts{{%/badge%}} will be installed through the Catalyst React plugin.<br /> <br /> The client is now successfully initialized as a React web application.<br /> The {{%link href="/en/cli/v1/project-directory-structure/client-directory" %}}client directory{{%/link%}} will be created in the standard structure in the project directory. This is the structure of the ToDoList project's directory if the client is initialized as a React app. {{%/tab%}} {{%/tabs%}} -------------------------------------------------------------------------------- title: "Configure the client" description: "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." last_updated: "2026-03-18T07:41:08.695Z" source: "https://docs.catalyst.zoho.com/en/tutorials/todo-list/python/configure-client/" service: "All Services" related: - Client Directory Structure (/en/cli/v1/project-directory-structure/client-directory) -------------------------------------------------------------------------------- # Configure the Client Next, let's configure the client component. Based on your earlier preference, you can navigate to the relevant section: {{%tabs%}} {{%tab "Basic web app"%}} #### Configure a Basic web app The {{%link href="/en/cli/v1/project-directory-structure/client-directory" %}}client directory{{%/link%}} contains: * The {{%badge%}}index.html{{%/badge%}} file that contains the HTML code for the front-end application. * The {{%badge%}}main.css{{%/badge%}} file that contains the CSS code for the front-end application. * The {{%badge%}}main.js{{%/badge%}} file that contains the JavaScript code. * The {{%badge%}}client-package.json{{%/badge%}} configuration file We will be coding {{%badge%}}index.html{{%/badge%}}, {{%badge%}}main.css{{%/badge%}}, and {{%badge%}}main.js{{%/badge%}}. {{%note%}}{{%bold%}}Note{{%/bold%}}: Please go through the code given in this section to make sure you fully understand it.{{%/note%}} Copy the code snippets given below and paste it in the respective files located in the {{%badge%}}client/{{%/badge%}} directory using an IDE, then save the files. {{% panel_with_adjustment header="index.html" footer="button" class="language-xml line-numbers" scroll="set-scroll" %}} &lt;!DOCTYPE html&gt; &lt;html lang="en"&gt; &lt;head&gt; &lt;meta charset="utf-8" /&gt; &lt;meta name="viewport" content="width=device-width, initial-scale=1" /&gt; &lt;meta name="theme-color" content="#000000" /&gt; &lt;meta name="description" content="A Simple Catalyst Application." /&gt; &lt;link rel="preconnect" href="https://fonts.googleapis.com" /&gt; &lt;link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /&gt; &lt;link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;700&display=swap" rel="stylesheet" /&gt; &lt;link rel="stylesheet" href="main.css" /&gt; &lt;script src="main.js"&gt;&lt;/script&gt; &lt;script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"&gt; &lt;/script&gt; &lt;title&gt;To Do&lt;/title&gt; &lt;/head&gt; &lt;body&gt; &lt;div class="container"&gt; &lt;div class="dF aI-center jC-center h-inh" id="page-loader"&gt; &lt;div class="loader&#45;-lg"&gt;&lt;/div&gt; &lt;/div&gt; &lt;div id="layout" class="dN"&gt; &lt;div class="title-container px-20"&gt; &lt;p class="text-white text-28 font-700"&gt;To Do&lt;/p&gt; &lt;/div&gt; &lt;div class="create-container"&gt; &lt;form class="dF aI-center w-full" onsubmit="createTodo(event)" autocomplete="off"&gt; &lt;input id="notes" type="text" placeholder="Enter a Task" class="input input&#45;-valid" oninput="onNotesChange(this)" /&gt; &lt;button id="create-task-btn" class="btn btn&#45;-primary ml-10" type="submit"&gt; Create Task &lt;div class="btn&#45;-primary__loader ml-5 dN" id="create-task-btn-loader"&gt;&lt;/div&gt; &lt;/button&gt; &lt;/form&gt; &lt;/div&gt; &lt;div class="task-container" id="tasks"&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;/body&gt; &lt;/html&gt; {{% /panel_with_adjustment %}} {{% panel_with_adjustment header="main.css" footer="button" class="language-css line-numbers" scroll="set-scroll" %}} body, p { margin: 0; } &#42; { box-sizing: border-box; font-family: 'Poppins', sans-serif; } input { margin: 0; resize: none; border: none; outline: none; -webkit-appearance: none; -moz-appearance: none; appearance: none; } button { padding: 0; border: none; outline: none; cursor: pointer; background: transparent; } /* Application Styles \*/ .container { height: 100vh; } .title-container { top: 0; left: 0; right: 0; z-index: 1; height: 4rem; display: flex; position: fixed; align-items: center; background: rgb(5, 150, 105); } .create-container { z-index: 1; top: 4rem; left: 0; right: 0; height: 5rem; padding: 20px; display: flex; position: fixed; background: #fff; align-items: center; } .task-container { padding-top: 9rem; } .task { display: flex; margin: 0 20px; font-size: 16px; padding: 12px 20px; align-items: center; border-radius: 5px; } .task:hover { background: rgba(5, 150, 105, 0.1); } .task__no { color: #111111; margin-right: 5px; } .task__title { flex: 1; margin-right: 5px; word-break: break-all; } .task__btn { width: 18px; height: 18px; opacity: 0.5; } .task__btn:hover { opacity: 1; } .input { width: 100%; font-size: 15px; padding: 12px 16px; border-radius: 5px; border: 1px solid #e0e0e0; } .input:focus { border: 1px solid rgb(5, 150, 105); box-shadow: 0px 0px 1px 1px rgb(5, 150, 105); } .input::-moz-placeholder { color: #919191; font-size: 15px; } .input:-ms-input-placeholder { color: #919191; font-size: 15px; } .input::placeholder { color: #919191; font-size: 15px; } .input:disabled { background: #f8f8f8; } .loader&#45;-lg { width: 60px; height: 60px; display: block; border-radius: 50%; box-sizing: border-box; border-top: 7px solid rgb(5, 150, 105); border-right: 7px solid rgb(5, 150, 105); border-bottom: 7px solid transparent; animation: spin 0.8s linear infinite; } .loader&#45;-sm { width: 25px; height: 25px; display: block; border-radius: 50%; box-sizing: border-box; border-top: 4px solid rgb(5, 150, 105); border-right: 4px solid rgb(5, 150, 105); border-bottom: 4px solid transparent; -webkit-animation: spin 0.8s linear infinite; animation: spin 0.8s linear infinite; } .loader&#45;-xs { width: 20px; height: 20px; display: block; border-radius: 50%; box-sizing: border-box; border-top: 4px solid rgb(5, 150, 105); border-right: 4px solid rgb(5, 150, 105); border-bottom: 4px solid transparent; -webkit-animation: spin 0.8s linear infinite; animation: spin 0.8s linear infinite; } .btn { flex-shrink: 0; display: flex; cursor: pointer; font-size: 15px; font-weight: 500; padding: 12px 16px; align-items: center; border-radius: 5px; } .btn&#45;-primary { color: #fff; background: rgb(5, 150, 105); } .btn&#45;-primary__loader { width: 15px; height: 15px; margin: 0 5px; display: block; border-radius: 50%; box-sizing: border-box; border-top: 3px solid #fff; border-right: 3px solid #fff; border-bottom: 3px solid transparent; -webkit-animation: spin 0.8s linear infinite; animation: spin 0.8s linear infinite; } .btn&#45;-primary:disabled { cursor: not-allowed; background: rgba(5, 150, 105, 0.7); } @keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } /* Helper Styles */ .my-5 { margin-top: 5px; margin-bottom: 5px; } .mr-5 { margin-right: 5px; } .ml-10 { margin-left: 10px; } .px-20 { padding-left: 20px; padding-right: 20px; } .p-20 { padding: 20px; } .w-full { width: 100%; } .text-28 { font-size: 28px; } .text-16 { font-size: 16px; } .text-white { color: #fff; } .text-info { color: #919191; } .dF { display: flex; } .aI-center { align-items: center; } .jC-center { justify-content: center; } .flex-1 { flex-grow: 1; } .h-inh { height: inherit; } .font-700 { font-weight: 700; } .dN { display: none; } .dB { display: block; } {{% /panel_with_adjustment %}} {{% panel_with_adjustment header="main.js" footer="button" class="language-javascript line-numbers" scroll="set-scroll" %}} var page = 1; var todoItems = []; var hasMore = false; window.onload = () =&gt; { getTodos(1); toggleCreateTaskBtn(''); }; //GET API. Contains the logic to fetch existing to-do items. function getTodos() { $.ajax({ url: &#96;/server/to_do_list_function/all?page=${page}&perPage=200&#96;, //Ensure that 'to_do_list_function' is the package name of your function. success: function (data) { const { data: { todoItems, hasMore } } = data; window.todoItems = [ &#46;&#46;&#46;new Map( Array.from(window.todoItems) .concat(todoItems) .map((item) =&gt; [item.id, item]) ).values() ]; window.hasMore = hasMore; renderTodo(); }, error: function (err) { console.log(err); }, complete: function () { $('#infinite-scroll-loader').removeClass('dB').addClass('dN'); $('#page-loader').removeClass('dF').addClass('dN'); $('#layout').removeClass('dN').addClass('dB'); } }); } function onMouseEnter(element) { const id = element.id; const delBtn = $(&#96;#${id}-del&#96;); if (delBtn && delBtn.attr('data-deleting')) { delBtn.removeClass('dN').addClass('dB'); } } function onMouseLeave(element) { const id = element.id; const delBtn = $(&#96;#${id}-del&#96;); if (delBtn && delBtn.attr('data-deleting')) { delBtn.removeClass('dB').addClass('dN'); } } function onNotesChange(element) { toggleCreateTaskBtn($(&#96;#${element.id}&#96;).val()); } function toggleCreateTaskBtn(value) { if (value) { $('#create-task-btn').attr('disabled', false); } else { $('#create-task-btn').attr('disabled', true); } } // DELETE API. Contains the logic to delete a to-do item. function deleteTodo(id) { $(&#96;#${id}-del&#96;).attr({ disabled: true, 'data-deleting': true, class: 'dN' }); $(&#96;#${id}-del-loader&#96;).removeClass('dN').addClass('dB'); $.ajax({ method: 'DELETE', url: &#96;/server/to_do_list_function/${id}&#96;, success: function () { todoItems = todoItems.filter((obj) =&gt; obj.id !== id); renderTodo(); }, error: function (err) { console.log(err); $(&#96;#${id}-del&#96;).attr({ disabled: false, 'data-deleting': false, class: 'dB' }); $(&#96;#${id}-del-loader&#96;).removeClass('dB').addClass('dN'); } }); } function renderTodo() { if (!todoItems.length) { $('#tasks').html( &#96; &lt;div class='p-20 dF jC-center'&gt; &lt;p class='text-info text-16'&gt; No tasks available, Create a new task. &lt;/p&gt; &lt;/div&gt; &#96; ); } else { let html = ''; todoItems.forEach((item, index) =&gt; { html += &#96;&lt;div class='task' id=${item.id} onmouseenter=&quot;onMouseEnter(this)&quot; onMouseLeave=&quot;onMouseLeave(this)&quot; &gt; &lt;p class='task__no'&gt;${index + 1 + ') '}&lt;/p&gt; &lt;p class='task__title'&gt;${item.notes}&lt;/p&gt; &lt;div class='loader&#45;-xs dN' id="${item.id}-del-loader" &gt;&lt;/div&gt; &lt;button id="${item.id + '-del'}" class="dN" onclick="deleteTodo('${item.id }')" data-deleting=false &gt; &lt;svg class=&#39;task__btn&#39; fill=&#39;none&#39; stroke=&#39;currentColor&#39; viewBox=&#39;0 0 24 24&#39; xmlns=&#39;http://www.w3.org/2000/svg&#39; &gt; &lt;path stroke-linecap=&#39;round&#39; stroke-linejoin=&#39;round&#39; stroke-width=&#39;2&#39; d=&#39;M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16&#39; &gt;&lt;/path&gt; &lt;/svg&gt; &lt;/button&gt; &lt;/div&gt;&#96;; }); $('#tasks').html(html); const observer = new IntersectionObserver((entries) =&gt; { if (entries[0].isIntersecting && hasMore) { page += 1; $('#tasks') .append(&#96; &lt;div class="dF jC-center my-5" id="infinite-scroll-loader"&gt; &lt;div class="loader&#45;-sm"&gt;&lt;/div&gt; &lt;/div&gt; &#96;); getTodos(); } }); observer.observe( document.getElementById(todoItems[todoItems.length - 1].id) ); } } //POST API. Contains the logic to create a to-do item. function createTodo(event) { event.preventDefault(); const notes = $(&#96;#notes&#96;).val(); $(&#96;#notes&#96;).attr({ readOnly: true }); $('#create-task-btn').attr('disabled', true); $('#create-task-btn-loader').removeClass('dN').addClass('dB'); $.ajax({ method: 'POST', contentType: 'application/json', url: '/server/to_do_list_function/add', //Ensure that 'to_do_list_function' is the package name of your function. data: JSON.stringify({ notes }), success: function (data) { const { data: { todoItem } } = data; todoItems = [todoItem].concat(todoItems); renderTodo(); }, error: function (error) { console.log(error); }, complete: function () { $(&#96;#notes&#96;) .attr({ readOnly: false }) .val(''); $('#create-task-btn-loader').removeClass('dB').addClass('dN'); } }); return false; } {{%/panel_with_adjustment %}} <br /> {{%note%}}{{%bold%}}Note{{%/bold%}}: The Python function package name {{%badge%}}to_do_list_function{{%/badge%}} has been added in the code for {{%badge%}}main.js{{%/badge%}} in the lines {{%bold%}}13{{%/bold%}} and {{%bold%}}182{{%/bold%}}. If you have provided a different package name, ensure you replace accordingly.{{%/note%}} The client directory is now configured. Let's quickly go over the working of the function and the client code: 1. {{%bold%}}POST Operation{{%/bold%}} * When the user enters a to-do list item in the app and saves it, the {{%badge%}}submit{{%/badge%}} event associated with the {{%bold%}}Create Task{{%/bold%}} button triggers an Ajax call to the POST API. * The {{%badge%}}main.js{{%/badge%}} in the client handles the Ajax operation and the URL, and it calls the POST API defined in the {{%badge%}}main.py{{%/badge%}} function file. * The POST API defined in {{%badge%}}main.py{{%/badge%}} inserts the data as a record in the _TodoItems_ table in the Data Store. The list item is inserted as the value of the _Notes_ column. * After a record insertion is done, the response (new task) will be added to the to-do list. 2. {{%bold%}}GET Operation{{%/bold%}} * The {{%badge%}}reload{{%/badge%}} event triggers an Ajax call to the GET API. The URL and the Ajax operation are handled in {{%badge%}}main.js{{%/badge%}}. * The GET API defined in {{%badge%}}main.py{{%/badge%}} obtains all the records from the Data Store by executing a ZCQL query. The ZCQL query contains the fetch operation along with the start-limit and the end-limit of the number of records that can be fetched. * The response containing the records (tasks) will be added to the existing to-do list. 3. {{%bold%}}DELETE Operation{{%/bold%}} * The {{%badge%}}main.js{{%/badge%}} handles the Ajax call to the DELETE API. When the user hovers on a particular task, and clicks on the appearing delete-bin icon in the client app, the DELETE API is triggered. * The DELETE API defined in {{%badge%}}main.py{{%/badge%}} then executes the delete operation for the record in the _TodoItems_ table matching the ROWID and sends the response back to the client. * The {{%badge%}}main.js{{%/badge%}} removes the respective record (deleted task) from the to-do list and displays the updated to-do list in the client app upon a successful delete operation. {{%/tab%}} {{%tab "Angular web app"%}} #### Configure an Angular web app The Angular web client directory contains the following files: * The root directory of the client contains a {{%link href="/en/cli/v1/project-directory-structure/client-directory" %}}{{%badge%}}client-package.json{{%/badge%}}{{%/link%}}file, which is a configuration file defining the name, version, and default home page of the client component. * {{%link href="https://angular.io/docs" %}}Native Angular files{{%/link%}} such as {{%bold%}}{{%badge%}}angular.json{{%/badge%}}{{%/bold%}}, {{%bold%}}{{%badge%}}karma.conf.js{{%/badge%}}{{%/bold%}}, {{%bold%}}{{%badge%}}tsconfig.app.json{{%/badge%}}{{%/bold%}}, {{%bold%}}{{%badge%}}tsconfig.json{{%/badge%}}{{%/bold%}}, {{%bold%}}{{%badge%}}tsconfig.spec.json{{%/badge%}}{{%/bold%}} files, and the {{%bold%}}{{%badge%}}dist{{%/badge%}}{{%/bold%}} directory. * The root directory of the client also contains the {{%link href="https://docs.npmjs.com/files/package.json" %}}{{%badge%}}package.json{{%/badge%}}{{%/link%}} dependency file, and a {{%link href="https://git-scm.com/docs/gitignore" %}}{{%badge%}}.gitignorefile{{%/badge%}}{{%/link%}}. * The {{%badge%}}**src**{{%/badge%}} folder contains the following files and directories as per the default project structure of the Angular app: * Native Angular files such as {{%bold%}}{{%badge%}}favicon.ico{{%/badge%}}{{%/bold%}}, {{%bold%}}{{%badge%}}main.ts{{%/badge%}}{{%/bold%}}, {{%bold%}}{{%badge%}}polyfills.ts{{%/badge%}}{{%/bold%}}, and {{%bold%}}{{%badge%}}test.ts{{%/badge%}}{{%/bold%}} files along with the assets, and environment directories. * {{%badge%}}**index.html**{{%/badge%}}: The default entry point of the to-do list application. * {{%badge%}}**styles.css**{{%/badge%}}: Contains all the style elements of the to-do list application. * The files present in the {{%badge%}}**src/app/**{{%/badge%}} directory are: * Native Angular files such as {{%bold%}}{{%badge%}}app.component.css{{%/badge%}}{{%/bold%}} and {{%bold%}}{{%badge%}}app.component.spec.ts{{%/badge%}}{{%/bold%}}. * {{%badge%}}**app.component.html**{{%/badge%}}: Contains the HTML component of each event generated by the user. * {{%badge%}}**app.component.ts**{{%/badge%}}: Contains the logic of the to-do list application. * {{%badge%}}**app.module.ts**{{%/badge%}}: Contains the meta of all the components used in building the to-do list application. We will be coding the {{%badge%}}index.html{{%/badge%}}, {{%badge%}}styles.css{{%/badge%}}, {{%badge%}}app.component.html{{%/badge%}}, {{%badge%}}app.component.ts{{%/badge%}}, and the {{%badge%}}app.module.ts{{%/badge%}} files. #### Create a Task Component You must create a {{%badge%}}task{{%/badge%}} component that contains the logic associated with each to-do task the end- user enters. Execute the following command in the {{%bold%}}{{%badge%}}client/src/app/{{%/badge%}}{{%/bold%}} directory to create the {{%badge%}}task{{%/badge%}} component: {{%cli%}}ng generate component task{{%/cli%}} This will create a folder named {{%badge%}}task{{%/badge%}} that contains: * The {{%badge%}}task.css{{%/badge%}} and the {{%badge%}}task.spec.ts{{%/badge%}} files native to Angular. * {{%badge%}}**task.html**{{%/badge%}}: Contains the HTML component of each to-do task entered by the user. * {{%badge%}}**task.ts**{{%/badge%}}: Contains the logic of each to-do task entered by the user. We will be coding the {{%badge%}}task.html{{%/badge%}} and {{%badge%}}task.ts{{%/badge%}} files as well. #### Install the Axios Package We will also need the {{%badge%}}axios{{%/badge%}} package to create a request from client to server. To install {{%badge%}}axios{{%/badge%}}, navigate to the client directory ({{%badge%}}client/{{%/badge%}}) and execute the following command: {{%cli%}} npm install axios{{%/cli%}} This will install the axios module and save the dependencies. <br /> You can now begin adding code in your files. {{%note%}}{{%bold%}}Note{{%/bold%}}: Please go through the code given in this section to ensure you fully understand it.{{%/note%}} <br /> Copy the code given below and paste it in {{%badge%}}index.html{{%/badge%}} and {{%badge%}}styles.css{{%/badge%}} files located in client directory ({{%badge%}}client/src/{{%/badge%}}) respectively using an IDE and save the file. {{% panel_with_adjustment header="index.html" footer="button" class="language-xml line-numbers" scroll="set-scroll" %}}&lt;!DOCTYPE html&gt; &lt;html lang="en"&gt; &lt;head&gt; &lt;link rel="preconnect" href="https://fonts.googleapis.com" /&gt; &lt;link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /&gt; &lt;link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;700&display=swap" rel="stylesheet" /&gt; &lt;meta charset="utf-8" /&gt; &lt;title&gt;To Do&lt;/title&gt; &lt;base href='/app/'/&gt; &lt;/head&gt; &lt;body&gt; &lt;app-root&gt;&lt;/app-root&gt; &lt;/body&gt; &lt;/html&gt; {{% /panel_with_adjustment %}} {{% panel_with_adjustment header="styles.css" footer="button" class="language-css line-numbers" scroll="set-scroll" %}}body, p { margin: 0; } &#42; { box-sizing: border-box; font-family: "Poppins", sans-serif; } input { margin: 0; resize: none; border: none; outline: none; -webkit-appearance: none; -moz-appearance: none; appearance: none; } button { padding: 0; border: none; outline: none; cursor: pointer; background: transparent; } /* Main Classess \*/ .container { height: 100vh; } .title-container { top: 0; left: 0; right: 0; z-index: 1; height: 4rem; display: flex; position: fixed; align-items: center; background: rgb(5, 150, 105); } .create-container { z-index: 1; top: 4rem; left: 0; right: 0; height: 5rem; padding: 20px; display: flex; position: fixed; background: #fff; align-items: center; } .task-container { padding-top: 9rem; } .task { display: flex; margin: 0 20px; font-size: 16px; padding: 12px 20px; align-items: center; border-radius: 5px; } .task:hover { background: rgba(5, 150, 105, 0.1); } .task__no { color: #111111; margin-right: 5px; } .task__title { flex: 1; margin-right: 5px; word-break: break-all; } .task__btn { width: 18px; height: 18px; opacity: 0.5; } .task__btn:hover { opacity: 1; } .input { width: 100%; font-size: 15px; padding: 12px 16px; border-radius: 5px; border: 1px solid #e0e0e0; } .input:focus { border: 1px solid rgb(5, 150, 105); box-shadow: 0px 0px 1px 1px rgb(5, 150, 105); } .input::-moz-placeholder { color: #919191; font-size: 15px; } .input:-ms-input-placeholder { color: #919191; font-size: 15px; } .input::placeholder { color: #919191; font-size: 15px; } .input:disabled { background: #f8f8f8; } .loader&#45;-lg { width: 60px; height: 60px; display: block; border-radius: 50%; box-sizing: border-box; border-top: 7px solid rgb(5, 150, 105); border-right: 7px solid rgb(5, 150, 105); border-bottom: 7px solid transparent; animation: spin 0.8s linear infinite; } .loader&#45;-sm { width: 25px; height: 25px; display: block; border-radius: 50%; box-sizing: border-box; border-top: 4px solid rgb(5, 150, 105); border-right: 4px solid rgb(5, 150, 105); border-bottom: 4px solid transparent; -webkit-animation: spin 0.8s linear infinite; animation: spin 0.8s linear infinite; } .loader&#45;-xs { width: 20px; height: 20px; display: block; border-radius: 50%; box-sizing: border-box; border-top: 4px solid rgb(5, 150, 105); border-right: 4px solid rgb(5, 150, 105); border-bottom: 4px solid transparent; -webkit-animation: spin 0.8s linear infinite; animation: spin 0.8s linear infinite; } .btn { flex-shrink: 0; display: flex; cursor: pointer; font-size: 15px; font-weight: 500; padding: 12px 16px; align-items: center; border-radius: 5px; } .btn&#45;-primary { color: #fff; background: rgb(5, 150, 105); } .btn&#45;-primary__loader { width: 15px; height: 15px; margin: 0 5px; display: block; border-radius: 50%; box-sizing: border-box; border-top: 3px solid #fff; border-right: 3px solid #fff; border-bottom: 3px solid transparent; -webkit-animation: spin 0.8s linear infinite; animation: spin 0.8s linear infinite; } .btn&#45;-primary:disabled { background: rgba(5, 150, 105, 0.7); } @keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } /* Helper Classes */ .my-5 { margin-top: 5px; margin-bottom: 5px; } .mr-5 { margin-right: 5px; } .ml-10 { margin-left: 10px; } .px-20 { padding-left: 20px; padding-right: 20px; } .p-20 { padding: 20px; } .w-full { width: 100%; } .text-28 { font-size: 28px; } .text-16 { font-size: 16px; } .text-white { color: #fff; } .text-info { color: #919191; } .dF { display: flex; } .aI-center { align-items: center; } .jC-center { justify-content: center; } .flex-1 { flex-grow: 1; } .h-inh { height: inherit; } .font-700 { font-weight: 700; } {{% /panel_with_adjustment %}} <br /> Copy the code given below and paste it in {{%badge%}}app.component.html{{%/badge%}}, {{%badge%}}app.component.ts{{%/badge%}}, {{%badge%}}app.module.ts{{%/badge%}} files respectively located in the client directory ({{%badge%}}client/src/app/{{%/badge%}}) using an IDE and save the file. {{% panel_with_adjustment header="app.component.html" footer="button" class="language-xml line-numbers" scroll="set-scroll" %}}&lt;div class="container"&gt; &lt;div class="dF aI-center jC-center h-inh" *ngIf="fetchState === 'init'"&gt; &lt;div class="loader&#45;-lg"&gt;&lt;/div&gt; &lt;/div&gt; &lt;div *ngIf="fetchState !== 'init'"&gt; &lt;div class="title-container px-20"&gt; &lt;p class="text-white text-28 font-700"&gt;To Do&lt;/p&gt; &lt;/div&gt; &lt;div class="create-container"&gt; &lt;form class="dF aI-center w-full" (ngSubmit)="createTodo()" autocomplete="off" &gt; &lt;input type="text" name="notes" [(ngModel)]="notes" placeholder="Enter a Task" class="input input&#45;-valid" [readOnly]="submitting" /&gt; &lt;button class="btn btn&#45;-primary ml-10" type="submit" [disabled]="notes.length === 0 || submitting" &gt; Create Task &lt;div class="btn&#45;-primary__loader ml-5" *ngIf="submitting"&gt;&lt;/div&gt; &lt;/button&gt; &lt;/form&gt; &lt;/div&gt; &lt;div class="task-container"&gt; &lt;div class="p-20 dF jC-center" *ngIf="todoItems.length === 0"&gt; &lt;p class="text-info text-16"&gt;No tasks available, Create a new task.&lt;/p&gt; &lt;/div&gt; &lt;div *ngIf="todoItems.length !== 0"&gt; &lt;app-task *ngFor="let item of todoItems; let i = index" [notes]="item.notes" [index]="i + 1" [id]="item.id" [isLast]="i === todoItems.length - 1" (removeTodo)="removeTodo($event)" (changePage) = "changePage()" #task &gt; &lt;/app-task&gt; &lt;/div&gt; &lt;div class="dF jC-center my-5" *ngIf="fetchState === 'loading'"&gt; &lt;div class="loader&#45;-sm"&gt;&lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; {{% /panel_with_adjustment %}} {{% panel_with_adjustment header="app.component.ts" footer="button" class="language-javascript line-numbers" scroll="set-scroll" %}}import axios from 'axios'; import { Component, OnInit } from '@angular/core'; @Component({ selector: 'app-root', templateUrl: './app.component.html', }) export class AppComponent implements OnInit { notes: string; page: number; hasMore: boolean; todoItems: Array&lt;{ id: string; notes: string; }&gt;; submitting: boolean; fetchState: 'init' | 'fetched' | 'loading'; constructor() { this.notes = ''; this.hasMore = false; this.page = 1; this.todoItems = []; this.submitting = false; this.fetchState = 'init'; } //GET API. The existing to-do items from the Datastore is being fetched. getTodos = (): void =&gt; { axios .get('/server/to_do_list_function/all', { //Ensure that 'to_do_list_function' is the package name of your function. params: { page: this.page, perPage: 200, }, }) .then((response) =&gt; { const { data: { todoItems, hasMore }, } = response.data; if (this.page === 1) { this.todoItems = todoItems as Array&lt;{ id: string; notes: string }&gt;; } else { this.todoItems = [ &#46;&#46;&#46;new Map( this.todoItems.concat(todoItems).map((item) =&gt; [item.id, item]) ).values(), ]; } this.hasMore = hasMore; this.fetchState = 'fetched'; }) .catch((err) =&gt; { console.error(err.response.data); }); }; //POST API. A new to-do item is being created. createTodo = (): void =&gt; { this.submitting = true; axios .post(&#96;/server/to_do_list_function/add&#96;, { //Ensure that 'to_do_list_function' is the package name of your function. notes: this.notes, }) .then((response) =&gt; { const { data: { todoItem }, } = response.data; this.notes = ''; this.todoItems = [{ &#46;&#46;&#46;todoItem }].concat(this.todoItems); }) .catch((err) =&gt; { console.error(err.response.data); }) .finally(() =&gt; { this.submitting = false; }); }; removeTodo = (id: string): void =&gt; { this.todoItems = this.todoItems.filter((obj) =&gt; obj.id !== id); }; changePage = (): void =&gt; { if (this.hasMore) { this.page += 1; this.fetchState = 'loading'; this.getTodos(); } }; ngOnInit() { this.fetchState = 'init'; this.getTodos(); } } {{% /panel_with_adjustment %}} <br /> {{%note%}}{{%bold%}}Note{{%/bold%}}: The Python function package's name {{%badge%}}to_do_list_function{{%/badge%}} has been added in the code for {{%badge%}}app.component.ts{{%/badge%}} in the lines {{%bold%}}33{{%/bold%}}, and {{%bold%}}65{{%/bold%}}. If you have provided a different package name, ensure you replace accordingly.{{%/note%}} <br /> {{% panel_with_adjustment header="app.module.ts" footer="button" class="language-javascript line-numbers" scroll="set-scroll" %}}import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { FormsModule } from '@angular/forms'; import { AppComponent } from './app.component'; import { Task } from './task/task'; @NgModule({ declarations: [AppComponent, Task], imports: [BrowserModule, FormsModule], providers: [], bootstrap: [AppComponent], }) export class AppModule {} {{% /panel_with_adjustment %}} <br /> Copy the code given below and paste it in {{%badge%}}task.html{{%/badge%}} and {{%badge%}}task.ts{{%/badge%}}, files located in the client directory ({{%badge%}}client/src/app/task/{{%/badge%}}) using an IDE and save the file. <br /> {{% panel_with_adjustment header="task.html" footer="button" class="language-xml line-numbers" scroll="set-scroll" %}}&lt;div class="task" ref="{ref}" (mouseenter)="mouseEnter()" (mouseleave)="mouseLeave()" &gt; &lt;p class="task__no"&gt;{{ index }} )&lt;/p&gt; &lt;p class="task__title"&gt;{{ notes }}&lt;/p&gt; &lt;div class="loader&#45;-xs" *ngIf="deleting === true"&gt;&lt;/div&gt; &lt;button *ngIf="!deleting && options" (click)="deleteTodo()"&gt; &lt;svg class="task__btn" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" &gt; &lt;path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" &gt;&lt;/path&gt; &lt;/svg&gt; &lt;/button&gt; &lt;/div&gt; {{% /panel_with_adjustment %}} {{% panel_with_adjustment header="task.ts" footer="button" class="language-javascript line-numbers" scroll="set-scroll" %}}import axios from 'axios'; import { Component, EventEmitter, Input, Output, ElementRef, AfterViewInit, OnDestroy, } from '@angular/core'; @Component({ selector: 'app-task', templateUrl: './task.html', }) export class Task implements OnDestroy, AfterViewInit { @Input() id: string; @Input() notes: string; @Input() index: number; @Input() isLast: boolean; @Output() removeTodo: EventEmitter&lt;string&gt;;; @Output() changePage: EventEmitter&lt;void&gt;; public options: boolean; public deleting: boolean; private observer?: IntersectionObserver; constructor(private element: ElementRef) { this.id = ''; this.index = 0; this.notes = ''; this.isLast = false; this.options = false; this.deleting = false; this.removeTodo = new EventEmitter(); this.changePage = new EventEmitter(); } mouseEnter = () =&gt; { this.options = true; }; mouseLeave = () =&gt; { this.options = false; }; deleteTodo = () =&gt; { this.deleting = true; //DELETE API. The call to delete the to-do item from the data store occurs here. axios .delete(&#96;/server/to_do_list_function/${this.id}&#96;) //Ensure that 'to_do_list_function' is the package name of your function. .then((response) =&gt; { const { data: { todoItem: { id }, }, } = response.data; this.removeTodo.emit(id); }) .catch((err) =&gt; { console.log(err.response.data); }) .finally(() =&gt; { this.deleting = false; }); }; ngAfterViewInit() { if (this.isLast && this.element) { this.observer = new IntersectionObserver((entries) =&gt; { if (entries[0].isIntersecting) { this.changePage.emit(); } }); this.observer.observe(this.element.nativeElement); } } ngOnDestroy() { if (this.observer) { this.observer.disconnect(); } } } {{% /panel_with_adjustment %}} {{%note%}}{{%bold%}}Note{{%/bold%}}: The Python function package's name {{%badge%}}to_do_list_function{{%/badge%}} has been added in the code for {{%badge%}}task.ts{{%/badge%}} in the lines {{%bold%}}51{{%/bold%}}. If you have provided a different package name, ensure you replace accordingly.{{%/note%}} The client directory is now configured. Let's quickly go over the working of the function and the client code: 1. {{%bold%}}POST Operation{{%/bold%}} * When the user enters a to-do list item in the app and saves it, the {{%badge%}}submit{{%/badge%}} event {{%bold%}}Create Task{{%/bold%}} button triggers an Ajax call to the POST API. * The {{%badge%}}app.component.ts{{%/badge%}} in the client handles the Ajax operation and the URL, and it calls the POST API defined in the {{%badge%}}main.py{{%/badge%}} function file. * The POST API defined in {{%badge%}}main.py{{%/badge%}} inserts the data as a record in the _TodoItems_ table in the Data Store. The list item is inserted as the value of the _Notes_ column. * After a record insertion is done, the response (new task) will be added to the to-do list. 2. {{%bold%}}GET Operation{{%/bold%}} * The {{%badge%}}reload{{%/badge%}} event triggers an Ajax call to the GET API. The URL and the Ajax operation are handled in {{%badge%}}app.component.ts{{%/badge%}}. * The GET API defined in {{%badge%}}main.py{{%/badge%}} obtains all the records from the Data Store by executing a ZCQL query. The ZCQL query contains the fetch operation along with the start-limit and the end-limit of the number of records that can be fetched. * The response containing the records (tasks) will be added to the existing to-do list. 3. {{%bold%}}DELETE Operation{{%/bold%}} * When the user clicks on a list item in the client app, the DELETE API is triggered. * The {{%badge%}}task.ts{{%/badge%}} handles the Ajax call to the DELETE API. When the user hovers on a particular task, and clicks on the delete-bin icon that appears in the client, the DELETE API is triggered. * The DELETE API defined in {{%badge%}}main.py{{%/badge%}} then executes the delete operation for the record in the _TodoItems_ table matching the ROWID and sends the response back to the client. * The {{%badge%}}app.component.ts{{%/badge%}} removes the respective record (deleted task) from the To-Do list and displays the updated list in the client app upon a successful delete operation. <br /> {{%/tab%}} {{%tab "React web app"%}} #### Configure a React web app The React web {{%link href="/en/cli/v1/project-directory-structure/client-directory" %}}client directory{{%/link%}} contains the following files: * The root directory of the client contains a {{%badge%}}client-package.json{{%/badge%}} file which is a configuration file defining the name, version, and default home page of the client component. * The {{%badge%}}**app**{{%/badge%}} folder contains two subfolders as per the default project structure of a React app: * The {{%badge%}}public{{%/badge%}} folder is generally used to hold files that can be openly accessed by browsers through public URLs, such as icon files of the web app and {{%badge%}}index.html{{%/badge%}}. * The {{%badge%}}src{{%/badge%}} folder contains the application's source files that will be included in the {{%badge%}}build{{%/badge%}} folder when we compile the React app. The {{%badge%}}app{{%/badge%}} folder also contains the {{%link href="https://docs.npmjs.com/files/package.json" %}}{{%badge%}}package.json{{%/badge%}}{{%/link%}} dependency file, and a {{%link href="https://git-scm.com/docs/gitignore" %}}{{%badge%}}.gitignore{{%/badge%}}{{%/link%}} file. * The {{%badge%}}**public**{{%/badge%}} folder contains the following files: * {{%link href="https://reactjs.org/docs/getting-started.html?#try-react" %}}Native React files{{%/link%}} such as {{%bold%}}{{%badge%}}favicon.ico{{%/badge%}}{{%/bold%}}, {{%bold%}}{{%badge%}}logo192.png{{%/badge%}}{{%/bold%}}, {{%bold%}}{{%badge%}}manifest.json{{%/badge%}}{{%/bold%}}, {{%bold%}}{{%badge%}}logi512.png{{%/badge%}}{{%/bold%}} and {{%bold%}}{{%badge%}}robots.txt{{%/badge%}}{{%/bold%}}. These files will not be necessary for rendering the to-do list application. * {{%badge%}}**index.html**{{%/badge%}}: The default entry point of the to-do list application. * The files present in the {{%badge%}}**src**{{%/badge%}} folder include: * Native React files such as {{%bold%}}{{%badge%}}setupTests.js{{%/badge%}}{{%/bold%}}, {{%bold%}}{{%badge%}}index.js{{%/badge%}}{{%/bold%}}, {{%bold%}}{{%badge%}}reportWebVitals.js{{%/badge%}}{{%/bold%}}, {{%bold%}}{{%badge%}}logo.svg{{%/badge%}}{{%/bold%}}, and {{%bold%}}{{%badge%}}App.test.js{{%/badge%}}{{%/bold%}}. * {{%badge%}}**App.js**{{%/badge%}}: Contains the logic of to-do list. * {{%badge%}}**index.css**{{%/badge%}}: Contains the styling elements of the native elements. * {{%badge%}}**App.css**{{%/badge%}}: Contains the styling elements of the application. * You will also be creating an additional file {{%badge%}}**helper.css**{{%/badge%}}. This file will contain utility classes, that will be responsible for certain minor styling. We will be coding {{%badge%}}index.html{{%/badge%}}, {{%badge%}}App.js{{%/badge%}}, {{%badge%}}helper.css{{%/badge%}}, {{%badge%}}index.css{{%/badge%}}, and the {{%badge%}}App.css{{%/badge%}} files. #### Install the Axios Package We will need the {{%badge%}}axios{{%/badge%}} package to create a request from client to server. To install {{%badge%}}axios{{%/badge%}}, navigate to the client directory ({{%badge%}}app/{{%/badge%}}) and execute the following command: {{%cli%}} npm install axios{{%/cli%}} This will install the axios module and save the dependencies. {{%note%}}{{%bold%}}Note{{%/bold%}}: Please go through the code given in this section to ensure you fully understand it.{{%/note%}} <br /> Copy the code given below and paste it in {{%badge%}}index.html{{%/badge%}} located in client directory ({{%badge%}}app/public/{{%/badge%}}) using an IDE and save the file. {{% panel_with_adjustment header="index.html" footer="button" class="language-xml line-numbers" scroll="set-scroll" %}}&lt;!DOCTYPE html&gt; &lt;html lang="en"&gt; &lt;head&gt; &lt;meta charset="utf-8" /&gt; &lt;meta name="viewport" content="width=device-width, initial-scale=1" /&gt; &lt;meta name="theme-color" content="#000000" /&gt; &lt;meta name="description" content="A Simple Catalyst Application." /&gt; &lt;link rel="preconnect" href="https://fonts.googleapis.com" /&gt; &lt;link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /&gt; &lt;link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;700&display=swap" rel="stylesheet" /&gt; &lt;title&gt;To Do&lt;/title&gt; &lt;/head&gt; &lt;body&gt; &lt;noscript&gt;You need to enable JavaScript to run this app.&lt;/noscript&gt; &lt;div id="root"&gt;&lt;/div&gt; &lt;/body&gt; &lt;/html&gt; {{% /panel_with_adjustment %}} <br /> Copy the respective code given below and paste it in {{%badge%}}App.js{{%/badge%}}, {{%badge%}}index.css{{%/badge%}}, {{%badge%}}App.css{{%/badge%}} and {{%badge%}}helper.css{{%/badge%}} respectively located in client directory ({{%badge%}}app/src/{{%/badge%}}) using an IDE and save the file. <br /> {{% panel_with_adjustment header="App.js" footer="button" class="language-javascript line-numbers" scroll="set-scroll" %}}import './App.css'; import './helper.css'; import axios from 'axios'; import { forwardRef, useCallback, useEffect, useRef, useState } from 'react'; //This segment contains the logic that displays each individual task present in the to-do list const Task = forwardRef(({ id, notes, index, removeTask }, ref) =&gt; { const [deleting, setDeleting] = useState(false); const [showOptions, setShowOptions] = useState(false); const onMouseEnter = useCallback(() =&gt; { setShowOptions(true); }, []); const onMouseLeave = useCallback(() =&gt; { setShowOptions(false); }, []); //Contains the logic to delete the taks from the Datastore const deleteTask = useCallback(() =&gt; { setDeleting(true); axios .delete(&#96;/server/to_do_list_function/${id}&#96;) //Ensure that 'to_do_list_function' is the package name of your function. .then((response) =&gt; { const { data: { todoItem: { id } } } = response.data; removeTask(id); }) .catch((err) =&gt; { console.log(err.response); }).finally(()=&gt;{ setDeleting(false) }) }, [id, removeTask]); return ( &lt;div className='task' ref={ref} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave} &gt; &lt;p className='task__no'&gt;{index + 1 + ') '}&lt;/p&gt; &lt;p className='task__title'&gt;{notes}&lt;/p&gt; {deleting ? ( &lt;div className='loader&#45;-xs'&gt;&lt;/div&gt; ) : ( showOptions && ( &lt;button onClick={deleteTask}&gt; &lt;svg className='task__btn' fill='none' stroke='currentColor' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg' &gt; &lt;path strokeLinecap='round' strokeLinejoin='round' strokeWidth='2' d='M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16' &gt;&lt;/path&gt; &lt;/svg&gt; &lt;/button&gt; ) )} &lt;/div&gt; ); }); //This segment contains the logic for loading the application function App() { const observer = useRef(null); const [page, setPage] = useState(1); const [notes, setNotes] = useState(''); const [todoItems, setTodoItems] = useState([]); const [hasMore, setHasMore] = useState(false); const [submitting, setSubmitting] = useState(false); const [fetchState, setFetchState] = useState('init'); const onChange = useCallback((event) =&gt; { const { value } = event.target; setNotes(value); }, []); useEffect(() =&gt; { if (fetchState !== 'fetched') { axios .get('/server/to_do_list_function/all', { //Ensure that 'to_do_list_function' is the package name of your function. params: { page, perPage: 200 } //The parameters contain the start limit and the end limit of data (tasks) that can be fetched from the data store }) .then((response) =&gt; { const { data: { todoItems, hasMore } } = response.data; if (page === 1) { setTodoItems(todoItems); } else { setTodoItems((prev) => [ ...new Map( Array.from(prev) .concat(todoItems) .map((item) => [item.id, item]) ).values() ]); } setHasMore(hasMore); setFetchState('fetched'); }) .catch((err) => { console.log(err.response); }); } }, [fetchState, page]); const lastElement = useCallback( (node) =&gt; { if (fetchState !== 'fetched') { return; } if (observer.current) { observer.current.disconnect(); } observer.current = new IntersectionObserver((entries) =&gt; { if (entries[0].isIntersecting && hasMore) { setPage((c) =&gt; c + 1); setFetchState('loading'); } }); if (node) { observer.current.observe(node); } }, [fetchState, hasMore] ); /// This segment contains the logic for creating a new task const createTodo = useCallback( (event) =&gt; { event.preventDefault(); setSubmitting(true); axios .post('/server/to_do_list_function/add', { //Ensure that 'to_do_list_function' is the package name of your function. notes }) .then((response) =&gt; { const { data: { todoItem } } = response.data; setNotes(''); setTodoItems((prev) =&gt; [{ &#46;&#46;&#46;todoItem }].concat(Array.from(prev))); }) .catch((err) =&gt; { console.log(err); }) .finally(() =&gt; { setSubmitting(false); }); }, [notes] ); //This segment contains the logic for deleting a task from the application const removeTask = useCallback((id) =&gt; { setTodoItems((prev) =&gt; Array.from(prev).filter((obj) =&gt; obj.id !== id)); }, []); return ( &lt;div className='container'&gt; {fetchState === 'init' ? ( &lt;div className='dF aI-center jC-center h-inh'&gt; &lt;div className='loader&#45;-lg'&gt;&lt;/div&gt; &lt;/div&gt; ) : ( &lt;&gt; &lt;div className='title-container px-20'&gt; &lt;p className='text-white text-28 font-700'&gt;To Do&lt;/p&gt; &lt;/div&gt; &lt;div className='create-container'&gt; &lt;form className='dF aI-center w-full' onSubmit={createTodo}&gt; &lt;input type='text' value={notes} onChange={onChange} placeholder='Enter a Task' className='input input--valid' readOnly={submitting} /&gt; &lt;button className='btn btn&#45;-primary ml-10' disabled={!notes.length || submitting} type='submit' &gt; Create Task {submitting && ( &lt;div className='btn&#45;-primary__loader ml-5'&gt;&lt;/div&gt; )} &lt;/button&gt; &lt;/form&gt; &lt;/div&gt; &lt;div className='task-container'&gt; {todoItems.length ? ( todoItems.map((item, index) =&gt; ( &lt;Task key={item.id} {&#46;&#46;&#46;item} ref={index === todoItems.length - 1 ? lastElement : null} index={index} removeTask={removeTask} /&gt; )) ) : ( &lt;div className='p-20 dF jC-center'&gt; &lt;p className='text-info text-16'&gt; No tasks available, Create a new task. &lt;/p&gt; &lt;/div&gt; )} {fetchState === 'loading' && ( &lt;div className='dF jC-center my-5'&gt; &lt;div className='loader&#45;-sm'&gt;&lt;/div&gt; &lt;/div&gt; )} &lt;/div&gt; &lt;/&gt; )} &lt;/div&gt; ); } export default App; {{% /panel_with_adjustment %}} <br /> {{%note%}}{{%bold%}}Note{{%/bold%}}: The Python function package's name {{%badge%}}to_do_list_function{{%/badge%}} has been added in the code in {{%badge%}}App.js{{%/badge%}} in the lines {{%bold%}}26{{%/bold%}}, {{%bold%}}97{{%/bold%}} and {{%bold%}}153{{%/bold%}}. If you have provided a different package name, ensure you replace accordingly.{{%/note%}} <br /> {{% panel_with_adjustment header="index.css" footer="button" class="language-css line-numbers" scroll="set-scroll" %}}body, p { margin: 0; } &#42; { box-sizing: border-box; font-family: 'Poppins', sans-serif; } input { margin: 0; resize: none; border: none; outline: none; -webkit-appearance: none; -moz-appearance: none; appearance: none; } button { padding: 0; border: none; outline: none; cursor: pointer; background: transparent; } {{% /panel_with_adjustment %}} {{% panel_with_adjustment header="App.css" footer="button" class="language-css line-numbers" scroll="set-scroll" %}}.container { height: 100vh; } .title-container { top: 0; left: 0; right: 0; z-index: 1; height: 4rem; display: flex; position: fixed; align-items: center; background: rgb(5, 150, 105); } .create-container { z-index: 1; top: 4rem; left: 0; right: 0; height: 5rem; padding: 20px; display: flex; position: fixed; background: #fff; align-items: center; } .task-container { padding-top: 9rem; } .task { display: flex; margin: 0 20px; font-size: 16px; padding: 12px 20px; align-items: center; border-radius: 5px; } .task:hover { background: rgba(5, 150, 105, 0.1); } .task__no { color: #111111; margin-right: 5px; } .task__title { flex: 1; margin-right: 5px; word-break: break-all; } .task__btn { width: 18px; height: 18px; opacity: 0.5; } .task__btn:hover { opacity: 1; } .input { width: 100%; font-size: 15px; padding: 12px 16px; border-radius: 5px; border: 1px solid #e0e0e0; } .input:focus { border: 1px solid rgb(5, 150, 105); box-shadow: 0px 0px 1px 1px rgb(5, 150, 105); } .input::-moz-placeholder { color: #919191; font-size: 15px; } .input:-ms-input-placeholder { color: #919191; font-size: 15px; } .input::placeholder { color: #919191; font-size: 15px; } .input:disabled { background: #f8f8f8; } .loader&#45;-lg { width: 60px; height: 60px; display: block; border-radius: 50%; box-sizing: border-box; border-top: 7px solid rgb(5, 150, 105); border-right: 7px solid rgb(5, 150, 105); border-bottom: 7px solid transparent; animation: spin 0.8s linear infinite; } .loader&#45;-sm { width: 25px; height: 25px; display: block; border-radius: 50%; box-sizing: border-box; border-top: 4px solid rgb(5, 150, 105); border-right: 4px solid rgb(5, 150, 105); border-bottom: 4px solid transparent; -webkit-animation: spin 0.8s linear infinite; animation: spin 0.8s linear infinite; } .loader&#45;-xs { width: 20px; height: 20px; display: block; border-radius: 50%; box-sizing: border-box; border-top: 4px solid rgb(5, 150, 105); border-right: 4px solid rgb(5, 150, 105); border-bottom: 4px solid transparent; -webkit-animation: spin 0.8s linear infinite; animation: spin 0.8s linear infinite; } .btn { flex-shrink: 0; display: flex; cursor: pointer; font-size: 15px; font-weight: 500; padding: 12px 16px; align-items: center; border-radius: 5px; } .btn&#45;-primary { color: #fff; background: rgb(5, 150, 105); } .btn&#45;-primary__loader { width: 15px; height: 15px; margin: 0 5px; display: block; border-radius: 50%; box-sizing: border-box; border-top: 3px solid #fff; border-right: 3px solid #fff; border-bottom: 3px solid transparent; -webkit-animation: spin 0.8s linear infinite; animation: spin 0.8s linear infinite; } .btn&#45;-primary:disabled { background: rgba(5, 150, 105, 0.7); } @keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } {{% /panel_with_adjustment %}} In the ({{%badge%}}app/src/{{%/badge%}}) directory, create a new file named ({{%badge%}}helper.css{{%/badge%}}), then copy and paste the below code into it. {{% panel_with_adjustment header="helper.css" footer="button" class="language-css line-numbers" scroll="set-scroll" %}}.my-5 { margin-top: 5px; margin-bottom: 5px; } .mr-5 { margin-right: 5px; } .ml-10 { margin-left: 10px; } .px-20 { padding-left: 20px; padding-right: 20px; } .p-20 { padding: 20px; } .w-full { width: 100%; } .text-28 { font-size: 28px; } .text-16 { font-size: 16px; } .text-white { color: #fff; } .text-info { color: #919191; } .dF { display: flex; } .aI-center { align-items: center; } .jC-center { justify-content: center; } .flex-1 { flex-grow: 1; } .h-inh { height: inherit; } .font-700 { font-weight: 700; } {{% /panel_with_adjustment %}} The client directory is now configured. Let's quickly go over the working of the function and the client code: 1. {{%bold%}}POST Operation{{%/bold%}} * When the user enters a to-do list item in the app and saves it, the {{%badge%}}submit{{%/badge%}} event associated with the {{%bold%}}Create Task{{%/bold%}} button triggers an Ajax call to the POST API. * The {{%badge%}}App.js{{%/badge%}} in the client handles the Ajax operation and the URL, and it calls the POST API defined in the {{%badge%}}main.py{{%/badge%}} function file. * The POST API defined in {{%badge%}}main.py{{%/badge%}} inserts the data as a record in the _TodoItems_ table in the Data Store. The list item is inserted as the value of the _Notes_ column. * After a record insertion is done, the response (new task) will be added to the to-do list. 2. {{%bold%}}GET Operation{{%/bold%}} * The {{%badge%}}reload{{%/badge%}} event triggers an Ajax call to the GET API. The URL and the Ajax operation are handled in {{%badge%}}App.js{{%/badge%}}. * The GET API defined in {{%badge%}}main.py{{%/badge%}} obtains all the records from the Data Store by executing a ZCQL query. The ZCQL query contains the fetch operation along with the start-limit and the end-limit of the number of records that can be fetched. * The response containing the records (tasks) will be added to the existing to-do list. 3. {{%bold%}}DELETE Operation{{%/bold%}} * The {{%badge%}}App.js{{%/badge%}} handles the Ajax call to the DELETE API. When the user hovers on a particular task, and clicks on the appearing delete-bin icon in the client app, the DELETE API is triggered. * The DELETE API defined in {{%badge%}}main.py{{%/badge%}} then executes the delete operation for the record in the _TodoItems_ table matching the ROWID and sends the response back to the client. * The {{%badge%}}App.js{{%/badge%}} removes the respective record (deleted task) from the to-do list and displays the updated to-do list in the client app upon a successful delete operation. <br /> {{%/tab%}} {{%/tabs%}} -------------------------------------------------------------------------------- title: "Test the application" description: "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." last_updated: "2026-03-18T07:41:08.699Z" source: "https://docs.catalyst.zoho.com/en/tutorials/todo-list/python/test-application/" service: "All Services" related: - Serve CLI Resources (/en/cli/v1/serve-resources/introduction) - Data Store (/en/cloud-scale/help/data-store/introduction) -------------------------------------------------------------------------------- # Test the Application Before you deploy the application to the remote console, you can test the application on a local server and check if everything works using the Catalyst CLI. For detailed information on {{%badge%}}catalyst serve{{%/badge%}}, refer to the {{%link href="/en/cli/v1/serve-resources/introduction" %}}Serve Resources help page{{%/link%}}. To serve the Catalyst project locally, execute the following command from your project directory: {{%cli%}} catalyst serve {{%/cli%}} The to-do list application will now be served at default port 3000. The local endpoint URLs of the components are displayed. {{%tabs%}} {{%tab "Basic web app"%}} {{%/tab%}} {{%tab "Angular web app"%}} {{%/tab%}} {{%tab "React web app"%}} {{%/tab%}} {{%/tabs%}} {{%note%}}{{%bold%}}Note{{%/bold%}}: Every time you access the home page or any of the sub-pages of your client, or the function, the CLI will display a live log of the URL accessed, along with the HTTP method that is used to access it.{{%/note%}} <br /> You can now open the client component's local URL in a browser to access the application. Let's test the application by adding a task to the to-do list. Enter a task and click {{%bold%}}Create Task{{%/bold%}}. The task will be added and displayed in the app. <br/> Let's verify this. Open the _TodoItems_ table in the {{%bold%}}Data Store{{%/bold%}} from your Catalyst console. Click the {{%bold%}}Data View{{%/bold%}} tab to view the record. A row will be created for the list item that was added. <br /> Let's add a few more tasks to the to-do list app. We can now test the delete operation. When you hover on a task, the delete-bin icon appears in the top-right corner. The delete operation is executed when you click the icon. The task will be removed from the list. The app displays the updated list. <br /> Let's verify if the record was deleted in the Data Store. Open the _TodoItems_ table in the {{%bold%}}Data Store{{%/bold%}} and click {{%bold%}}Data View{{%/bold%}} to view the records. The row for the list of items will be deleted from the table. If this setup is working correctly, we can deploy the application to production. <br /> -------------------------------------------------------------------------------- title: "Deploy the project" description: "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." last_updated: "2026-03-18T07:41:08.699Z" source: "https://docs.catalyst.zoho.com/en/tutorials/todo-list/python/deploy-project/" service: "All Services" related: - Deploy CLI Resources (/en/cli/v1/deploy-resources/introduction/) - Web Client Hosting (/en/cloud-scale/help/web-client-hosting/introduction) -------------------------------------------------------------------------------- # Deploy the Project To {{%link href="/en/cli/v1/deploy-resources/introduction/" %}}deploy your Catalyst project{{%/link%}} from the CLI, run the following command in your terminal from your project directory: {{%cli%}} catalyst deploy{{%/cli%}} The function is deployed first, followed by the client component. The URL endpoints of the components are displayed below: {{%tabs%}} {{%tab "Basic web app"%}} {{%/tab%}} {{%tab "Angular web app"%}} {{%/tab%}} {{%tab "React web app"%}} {{%/tab%}} {{%/tabs%}} You can now open the client component's URL in a browser to access the deployed application. The to-do list application can now be accessed from its {{%link href="/en/cloud-scale/help/web-client-hosting/key-concepts/#default-web-application-url" %}}web app URL{{%/link%}}. The to-do list application is now functional and will work without any errors.