Choose the right plan
- Before you start creating an application in AppCreator, it's important to understand what the AppCreator platform can offer and the details for each plan.
- As a rule of thumb, it's ideal to have at least 5-10% more scope than required. This will ensure that your application can scale in case there is a spike in user activity.
- Identify who will be using the app and design according to their requirements. For example, you can choose a customer portal plan to support the needs of your customers, vendors, and partners.
Security best practices
Users and permissions
- Only the required users should be added to the application, and only the necessary modules should be shared.
- Users or developers who no longer require access to the application should be removed.
- When more than one user is building the app, add the required users as developers instead of sharing the admin credentials.
- Access to personally identifiable information (PII) and electronic protected health information (ePHI) data can be given to the necessary permission sets alone.
- Export, Delete, View All, and Bulk Edit permissions must be limited.
- Permission sets that are not required for users and customers can be deleted.
Forms and reports
- Fields that collect or contain PII and ePHI data should be encrypted.
- Sensitive information can be masked in reports wherever applicable.
- Filters and criteria can be added to reports to ensure only the required information is displayed to users.
- Any sensitive information that is stored can be deleted when it is no longer required.
- Confirmation messages can be associated with custom action buttons.
- It is not advisable to collect or store sensitive information like Social Security numbers or passwords. If collected, the purpose should be clearly stated in the form.
- It is best to avoid storing API credentials in the application.
- Help text or tooltips can be associated with fields to elaborate on the reason for collecting the data.
- Note fields can be added to forms to specify to users the purpose for the data collection.
- It is advisable to deactivate published forms when public access is no longer required.
- A Decision box can be used to obtain consent for data collection.
- OAuth-based authentication should be used wherever possible when invoking APIs.
- When collecting IP address, it is advisable to inform users of this and obtain their consent. IP address can also be encrypted.
- Actions in reports, like Add, Delete, and Duplicate, must be reviewed, and can be removed if not required.
- The history of changes made to records can also be tracked using the Audit Trail feature.
Workflows and scripting
- Input validations should be done for fields wherever possible.
- Any hyperlinks used in the application should be verified.
- Content in SMS and email tasks should be reviewed.
- Business logic, like functions and Schedule workflows, can be executed inside the workflow builder itself before being incorporated in the main flow.
Pages
- HTML and JavaScript code should be reviewed.
- Use comments wherever possible.
- For pages that work based on parameters, be sure to include null checks to prevent the page from displaying errors or irrelevant data.
- Avoid duplicating CSS styles.
Application best practices
General practices
- Define the application structure, identify the data model—fields, the relationships, encryption of fields, and take into account the pain points of the current process before beginning to build the application.
- Document the application structure. This will make it easier to make changes later.
- Assign meaningful names for all components—Forms, Fields, Reports, Workflows, Pages, Variables, Functions, and Permission Sets.
- Use lookup fields to create relationships between the forms to avoid redundant data across forms.
- Set properties like Mandatory and No Duplicate values to fields to maintain data integrity.
- Delete components that are not required from the application.
- Specify delete behavior of records—what will happen to any related records when a particular record is deleted. For example, if an employee is deleted, what will happen to the tasks assigned to them?
- Make changes to the application in a sandbox environment and test them before publishing.
- Use the scheduled app backup feature to create periodic backups.
Deluge best practices
Deluge is AppCreator's built-in scripting language. It is easy to learn, and comes with an integrated development environment which enables you to drag and drop Deluge code snippets while coding.
General practices
- Debugging statements like "info" can be used wherever applicable to weed out errors.
- Avoid heavy nesting of scripts and optimize loops.
- Use the aggregate record task effectively. Fetch all records only when needed.
- Use the built-in functions wherever possible—these are optimized for performance.
- Try catch statements can be used to handle exceptions in the code.
- Variables can be given meaningful names.
- Make use of system variables like zoho.appUri and zoho.appName so that the application link name, or the owner name changes do not affect your functionality.
- This is especially useful for Deluge script used inside HTML pages for embedding components.
- Functions can be used to create reusable code blocks out of frequently used or duplicated code.
- Unwanted and commented code can be removed to keep the code base clean for easier maintenance.
- Scripts can be indented and comments can be added to them to provide a short overview of the logic and purpose.
- Keep the code modular.
- Syntax Assist can be used to help configure tasks.
- Versioning is also available to track the various versions and code changes.
- When making use of the invokeURL Deluge task to make API calls, the response is not stored or processed in the data AppCreator retains, which makes Deluge safe from any vulnerabilities.
- Any errors in Deluge scripting will only affect the application logic, and will not cause any vulnerabilities.
- Syntax errors that arise in Deluge will be displayed when a user tries to save the script. The code will not be saved or executed until the error is rectified.
- Logical errors that occur due to poor programming practices and mistakes must be traced and fixed.
- Any Deluge code written must be optimized to ensure the Deluge statement limits are not reached. These have been set to prevent accidental misuse of resources.
- To improve performance, make use of the predefined Deluge functions: equalsIgnoreCase, equals for comparison rather than "==".
Scripts to be avoided
The following are a few scripts that can be avoided. Alternatives for them are provided.
Example 1:
Instead of checking the count from the fetch statement, you can directly use the fetch collection inside the loop.
fetchInvoices = Invoice[Client == input.Client && Location.equalsIgnoreCase("Chennai") && Bill_Payment_Status == "Not Paid"];
if(fetchInvoices.count() > 0)
{
for each invoice in fetchInvoices
{
}
}
//------------------------------------------
//Instead use the below script
//------------------------------------------
for each invoice in Invoice[Client == input.Client && Location.equalsIgnoreCase("Chennai") && Bill_Payment_Status == "Not Paid"]{
}
if(fetchInvoices.count() > 0)
{
for each invoice in fetchInvoices
{
}
}
//------------------------------------------
//Instead use the below script
//------------------------------------------
for each invoice in Invoice[Client == input.Client && Location.equalsIgnoreCase("Chennai") && Bill_Payment_Status == "Not Paid"]{
}
Example 2:
You can use built-in functions to avoid looping.
paymentList=List();
for each invoice in PaymentAgainstInvoice[ID == rushPayment]
{
paymentList.add(invoice.ID);
}
//------------------------------------------
//Instead use the below script
//------------------------------------------
paymentList = PaymentAgainstInvoice[ID == rushPayment].ID.getAll();
for each invoice in PaymentAgainstInvoice[ID == rushPayment]
{
paymentList.add(invoice.ID);
}
//------------------------------------------
//Instead use the below script
//------------------------------------------
paymentList = PaymentAgainstInvoice[ID == rushPayment].ID.getAll();
Example 3:
Here is another way you can use built-in functions to avoid looping.
fethcInvoice = Invoice[ID == input.invoiceId];
for each lineitmcount in fethcfrominv.Line_Items
{
contlines = contlines + 1;
}
//--------------------------------------------
//instead use the below script
//------------------------------------------
count=fethcInvoice = Invoice[ID == input.invoiceId].count(ID);
for each lineitmcount in fethcfrominv.Line_Items
{
contlines = contlines + 1;
}
//--------------------------------------------
//instead use the below script
//------------------------------------------
count=fethcInvoice = Invoice[ID == input.invoiceId].count(ID);
Example 4:
This code example shows you how to avoid unnecessary update operations.
billId = insert into Bill
[
Added_User=zoho.loginuser
Total_Price=Total_Price
Bill_Status=Bill_Status
];
fetchBill = Bill[ID == billId];
if(patient_Type == "Free")
{
fetchBill.Total_Price=0;
fetchBill.Bill_Status="Closed";
}
//--------------------------------------------
//Instead of using the above code we can perform the check before inserting the data to the Bill form. This will avoid the unnecssary update operation
//--------------------------------------------
if(patient_Type == "Free")
{
Total_Price=0;
Bill_Status="Closed";
}
billId = insert into Bill
[
Added_User=zoho.loginuser
Total_Price=Total_Price
Bill_Status=Bill_Status
];
[
Added_User=zoho.loginuser
Total_Price=Total_Price
Bill_Status=Bill_Status
];
fetchBill = Bill[ID == billId];
if(patient_Type == "Free")
{
fetchBill.Total_Price=0;
fetchBill.Bill_Status="Closed";
}
//--------------------------------------------
//Instead of using the above code we can perform the check before inserting the data to the Bill form. This will avoid the unnecssary update operation
//--------------------------------------------
if(patient_Type == "Free")
{
Total_Price=0;
Bill_Status="Closed";
}
billId = insert into Bill
[
Added_User=zoho.loginuser
Total_Price=Total_Price
Bill_Status=Bill_Status
];
Resources
Mobile app best practices
- Triggered actions and events, like tap of a record, swipe left, and swipe right, can be determined and changed.
- Columns in the quick view and detail view can be set according to the app context.
- Custom logo can be uploaded to the applications.
- Applications can be rebranded and published separately, offline, for users and customers.