All apps that are to be published in the Freshworks Marketplace go through a review where they are vetted for code quality, correctness, and security. Here are a broad set of guidelines to be followed while developing apps.
App Best Practices
In order to deliver a rich user experience to customers, we recommend that you adhere to the following best practices adopted for:
Use Interface APIs in apps to provide notification of an operation’s status. Also, notification messages should be clear and concise.
- Categorize notifications according to their types to make them meaningful (Success, Information, Warning, or Error).
- Keep the notification messages simple and easy to understand.
- Ensure that failure messages suggest an appropriate next step for users. For example: Retry at a later time, Contact the technical support team, or Refresh the page.
- Avoid passing API error responses or the corresponding text in notification messages as they can be technical in nature and ambiguous.
- Do not use console logs as an alternative to notifications.
Poor input validation can break an app or introduce security vulnerabilities. For fields in a standard installation page, you can set validations in the iparams.json file. Also, appropriate validation checks must be included for custom installation pages and forms that accept user inputs.
- Validate values based on the field types. For example, it should not be possible to enter alphabetic characters in numeric fields.
- If the validation fails, the app should provide clear and concise messages and suggestions on how to address the errors.
- Provide helpful hints for complex form fields with appropriate example values.
- Mark required fields clearly.
DO NOT use regex patterns randomly. Although their brevity adds value, an incorrect pattern could do more harm than good.
Lean Apps and Packaging
Remove unreachable code, unused variables, unwanted resources, and unused libraries to keep your app lean. Any source code that interacts with the Freshworks SDK (for reference, see client) must be packed within the app and submitted for review so that app users can tell what data from their account is used by the app. Business logic that uses this data can remain outside of the app package if required.
If a large part of the app source code is hosted outside the app, reviewers are likely to seek further information on the overall app architecture and how the business logic makes use of any personal information sourced by the app from a user’s account.
- Modularize code to enable better code organization, readability, and extensibility. Modules separate functions of the application into highly organized and manageable pieces.
- Use CDNs instead of locally packed libraries for code that does not use Freshworks platform APIs.
- Remove unused npm modules from the code.
- Ensure that the app uninstallation process undoes and cleans up the results of set-up actions, such as registering an external webhook.
- Use the fdk pack command to pack the application after testing.
DO NOT access any code that makes use of the Freshworks platform APIs through CDN.
Freshworks products are known for their intuitive and visually appealing user experience. As an extension of the products, apps should blend into the product visually and not stand-out.
- Ensure that you adhere to the UI style guide.
- Perform cross-browser testing to ensure uniformity in the UI.
Coding Best Practices
For you to develop maintainable and reliable apps, we recommend that you adhere to the following best practices adopted for:
Although console logs help debug code, avoid indiscriminate use of console logs.
- Ensure that logs convey a meaningful message and proper context.
- Make use of different log levels (Info, Debug, Warn, and Error) to categorize logs. For example, exceptions should be logged with console.error() as shown in the image.
- Do not log secure data such as keys, tokens, credentials, and so on.
- Do not use console.log() extensively as it leads to clutter.
Asynchronous Control Flows
The Freshworks platform APIs are asynchronous in nature and it is essential to sequence them appropriately.
- Use promises to wrap asynchronous operations. Two or more asynchronous operations can be run back-to-back using promise chains. You can break the chain to obtain and process intermediate values.
- Check the browser compatibility when using external promise libraries, such as bluebird and Q, or native promise methods such as promise.all().
- Check the compatibility of modules with older environments when implementing complex control flows for asynchronous operations, such as the async module.
- Avoid callback hell.
- Do not use setTimeout() to cope with asynchronous flows.
- Do not access variables passed to or declared in the promise chain outside the promise.
Errors in code must be appropriately handled based on the control flow (synchronous or asynchronous). Ensure that you test failure cases extensively.
- Use the try..catch..finally block to handle synchronous errors.
- Handle errors in asynchronous operations or promises with the same level of detail as for synchronous operations. You can use the err parameter in callbacks and the catch block in promises for error handling.
- Ensure that the serverless component of the app returns a proper error response (during Server Method Invocation and App Setup Events) to the front end. This ensures business continuity in the face of unexpected errors and prevents negative experiences.
DO NOT wrap everything inside a single try..catch block. Ensure that the code reacts appropriately to different exceptions.
Code Minification and Readability
Any code submitted as part of an app (with libraries included as assets) must not be in the minified or transpiled form. It has to be in a readable format to expedite code review and app certification processes.
- Ensure that submitted code is readable (with consistent naming schemes and indentation and follows the DRY principle).
- Although good code is self-documenting, use in-line documentation.
- If you use React, Ember, Vue, or any other front-end framework, include the source files of your app in the src directory. The contents of the src directory are packed along with the app when the fdk pack command is run and can be submitted for review.
Use ES5 standards for client-side code as ES6 standards disrupt backward compatibility.
The following security-related guidelines are mandatory for all apps. We recommend that you adhere to the following best practices adopted for:
If your installation parameters contain sensitive customer data, ensure that you secure them.
- Define iparams that store keys, credentials, or sensitive data as secure iparams.
- In the front end, ensure that you access secure iparams only by reference, using templating as part of Request API calls, as shown in the following snippet.
- Do not hardcode keys, credentials, or sensitive data in the app.
- In the front end, do not use client.iparams.get() to fetch secure iparams. However, you can use client.iparams.get() to access non-secure iparams.
Apps depend on REST API calls, specifically when they are integrated with other products. Request API not only acts as a proxy to address CORS but also helps secure API calls. Using $.ajax calls instead of the platform Request API could lead to security vulnerabilities, especially when using secure iparams.
- Take into account the pagination settings and rate limits when the app makes calls to the REST APIs.
- Use the Request API’s templating feature to securely inject iparams in authorization headers (front end).
- If an app works with an external custom web service (such as one hosted with a cloud provider), the web service must be authenticated for any access so that privileged information is secure and only accessible through the app.
- Any external web services used must be capable of supporting considerable load and must have reliable uptime. Always prefer HTTPS over HTTP.
- Do not hardcode keys, tokens, or credentials in REST API calls.
- Avoid making HTTP requests as they are vulnerable to MITM attacks.
The code coverage summary lets you know the extent to which you have tested your app. For this, execute the fdk run command on the terminal. You can test your app for both positive and negative cases with equal importance.
Once you exit testing, the CLI prints the code coverage summary with which you can gauge how extensive your testing was. Also, you can access the coverage report available in the path shown in the image.
Each component in the coverage summary should be at least 80% for apps that are submitted to Freshworks Marketplace (not so in the image).
To improve code coverage, you would need to test each and every functionality in your app for both success and failure cases, thereby detecting hidden bugs and improving the quality of the app.
The main components of the coverage summary are:
- Statement coverage: Checks if each statement in the code has been executed at least once.
- Branch coverage: Checks if each branch of each control structure has been executed. For example, in an IF statement, both the true and false branches need to be tested.
- Function coverage: Checks if each function (or subroutine) has been called in the code.
- Line coverage: Checks if each line of code has been executed (excluding comments/ conditionals).
Note: Use the fdk pack command to pack the application after testing.
DO test positive and negative cases with equal importance.
DO NOT manually zip the app. Manual packing can corrupt or miss artifacts, pertaining to coverage metrics, in the .fdk directory and .report.json file.