Developing a Jira App: Three Tips for Atlassian Forge App Development
UI Kit vs. Custom UI
You have two choices for building the user interface for your Forge app – the UI kit, and custom UI. The UI kit allows you to create a straightforward UI using a set of Atlassian-supplied components, put together using relatively simple code. Custom UI gives you more room to use Atlassian-supplied and third-party hooks, components and such, while also allowing you to employ more complex HTML, CSS, static resources (such as images) and source code, all of which can be hosted in Atlassian’s cloud with custom UI but not the UI kit. Both types of UI involve writing code in a React-like pattern, which makes sense in that various components available from Atlassian are based on React.
NOTE: My recommendation is that you use custom UI from the beginning. Custom UI imposes fewer limitations on you, the developer, and if you already have experience with React, then the effort to use custom UI is not much more than for the UI kit. That being said, it is possible to use both custom UI and the UI kit in the same app [1].
React Support
Currently, React, the front-end framework on which Forge is based, is on version 18.x. However, Atlassian Design System components [2] explicitly support React 16.x only [3]. Since React’s policy is generally to stop releasing new fixes (except for critical bugs) for a given major version once a newer major version has been released [4], you really should choose the most recent available version of React.
Unfortunately, when attempting to use Design System components with React 17 or later, you might get errors like the following while running npm install:
npm ERR! code ERESOLVE
npm ERR! ERESOLVE could not resolve
npm ERR!
npm ERR! While resolving: @atlaskit/button@16.7.3
npm ERR! Found: react@18.2.0
npm ERR! node_modules/react
npm ERR! react@"^18.2.0" from the root project
npm ERR! peer react@">=16.8.0" from @emotion/react@11.10.6
npm ERR! node_modules/@emotion/react
npm ERR! @emotion/react@"^11.7.1" from @atlaskit/button@16.7.3
npm ERR! node_modules/@atlaskit/button
npm ERR! @atlaskit/button@"^16.7.3" from the root project
npm ERR! @emotion/react@"^11.7.1" from @atlaskit/focus-ring@1.3.2
npm ERR! node_modules/@atlaskit/focus-ring
npm ERR! @atlaskit/focus-ring@"^1.3.0" from @atlaskit/button@16.7.3
npm ERR! node_modules/@atlaskit/button
npm ERR! @atlaskit/button@"^16.7.3" from the root project
npm ERR! 9 more (@atlaskit/icon, @atlaskit/motion, @atlaskit/popup, ...)
npm ERR! 24 more (@emotion/use-insertion-effect-with-fallbacks, ...)
You can work around this issue with the --legacy-peer-deps command-line switch provided by npm install. Importantly, while this will mask errors like the above, there’s always the chance that React 17 or later will deprecate or otherwise eliminate features on which Design System components rely. I strongly suggest that you keep an eye on front-end console logs for warnings or errors that indicate such potential issues.
Checking Your Forge App’s License
If you plan to license your Forge app to users as a paid app [5], you’ll need to write code to check that the license for your app is valid. Once you know what to do, it’s a fairly simple process.
NOTE: The following assumes that your app is using custom UI, and also that you’re familiar with the React framework.
First, in your back-end code, create a custom UI resolver function [6] like the following, which is modeled after [7]. Note that we’ll be creating a LICENSE_OVERRIDE Forge environment variable that will help us test license checks.
Of course, the front end for your app runs in the user’s browser. Thus, it could be tampered with to skip license checks. To prevent such checks from being skipped, you’ll want to add isLicenseActive() calls to any back-end code that you don’t wish unlicensed users to access, then deny such access if it returns false. It’s up to you how you’d like your back end to indicate such denial of access to your front end.
Next, in your back-end code, add that function to the resolver definitions:
Then, in your front-end code, let’s use TanStack Query [8], a very handy data-fetching library, to create a hook named useIsLicenseActive() for calling the above back-end function:
Now, call useIsLicenseActive() as appropriate from your front-end code:
Finally, set the LICENSE_OVERRIDE Forge environment variable appropriately. If you’re using the forge tunnel command to test your local copy of code, you can set the variable’s value using the export command in the Unix-like shell of your choice. If you’re not using forge tunnel, then use the forge variables set command to set the variable’s value for whichever environment you wish to test. Generally, you will not want to override license checks by setting this variable in your app’s Production environment.
NOTE: If you’re tunneling from a Windows shell, you’ll need to do the following to use the LICENSE_OVERRIDE variable or any other environment variable. Otherwise, the variables will not be properly set for your use.
Run the forge variables set -e development LICENSE_OVERRIDE <desired value> command to set the variable’s value in the Development environment.
Run the forge deploy command to deploy the variable (and your code) to the Development environment.
Run the forge tunnel command. Your local copy of the app will be run, but environment variable values will be set as you specified above.
Surprise! Bonus Tip (Well, Several Tips): Personal Data Handling Requirements
Atlassian sells its services to customers around the world, some of whom are in the European Union (EU). Thus, Atlassian requires that Forge apps handle user personal data in accordance with the EU’s General Data Protection Regulation (GDPR). User personal data is any data that could be used, by itself or in conjunction with other data, to identify an individual user. Here are some tips on working with user personal data:
Don’t store user personal data in your app or on external systems. Instead, query user personal data as needed.
Don’t write user personal data to logs.
If you absolutely must do any of the above, then I highly recommend hashing or masking the user personal data that’s stored or logged.
If user personal data is stored, logged or otherwise collected in any fashion, regardless of where your app stores or logs that data, Atlassian will require you to note in your app’s Privacy Policy that your app collects such data. The app must also report its collection of such data via Atlassian’s personal data reporting API.
If your app stores user personal data, the app must implement a mechanism to detect that a request has been made to change or delete user personal data (via the API mentioned above), then act on that request.
In some cases, you may be required to get user consent when collecting user personal data.
You must take reasonable steps to secure any user personal data that’s stored, logged or otherwise collected.
Long story short: Don’t collect user personal data if you can possibly help it.
For more information on the above – such as how to interact with the personal data reporting API, how to explain your data privacy practices to users, and what user personal data should or should not be logged – please visit [9], [10] and [11]. Remember, you and your team are responsible for your app’s security!
Thanks!
I hope you found these tips helpful. Now, go forth and rock those apps!
Want to learn more about how Jira and Atlassian can help your company? Contact Moser today.
References
[1] https://community.developer.atlassian.com/t/how-to-combine-ui-kit-and-custom-ui-togather/46442
[2] https://atlassian.design/components
[3] https://community.developer.atlassian.com/t/atlaskit-react-17/56836
[4] https://github.com/reactjs/react.dev/issues/1745#issuecomment-466767389
[5] https://developer.atlassian.com/platform/marketplace/listing-forge-apps/#building-a-paid-app
[6] https://developer.atlassian.com/platform/forge/runtime-reference/custom-ui-resolver/
[8] https://tanstack.com/query/latest/docs/react/overview
[9] https://developer.atlassian.com/platform/forge/user-privacy-guidelines/
[10] https://developer.atlassian.com/platform/marketplace/data-privacy-guidelines/
[11] https://developer.atlassian.com/platform/forge/logging-guidelines/