Product Engineering12 min read

Reports for Jira: How Forge SQL Made Jira Reporting Faster and Safer

Discover how Reports for Jira moved to Forge SQL and server-side processing to speed up dashboards, reduce browser load, and support safer migrations—all while keeping compute and storage within Atlassian Cloud.

Reports Dashboard.png

Jira Cloud holds the data that teams need to report on: issue volumes, resolution times, SLA performance, workload distribution, sprint progress, customer request trends, and the small operational signals that decide whether a team is in control or guessing. Turning that data into a useful chart sounds simple. In practice, reporting is where a cloud app quickly becomes a data pipeline.

Reports for Jira started with a browser-heavy architecture. The browser fetched Jira issue data, transformed raw REST API responses into chart-ready datasets, and rendered dashboards locally. That was the right first architecture for a Forge app in 2024. It let us ship quickly, keep the app simple, and stay inside Atlassian Cloud. But as dashboards grew from hundreds to thousands of issues, the browser became the wrong place to do that much work.

This is the story of why we moved the data pipeline from the browser to Forge server-side resolvers backed by Forge SQL, what improved, what trade-offs remain, and how that architecture shapes the product path for secure Jira reporting.

What Reports for Jira is built for

Reports for Jira is a Forge-native reporting app for teams that need secure Jira dashboards, custom charts, tabular reports, KPI cards, Jira dashboard gadgets, and stakeholder-ready exports without moving raw Jira data into a vendor-managed backend.

The Marketplace value proposition is deliberately straightforward: create Jira reports, dashboards, charts and gadgets with templates, JQL filters, PDF export, and Excel export. Underneath that simple promise is a harder engineering requirement: make reporting feel fast and trustworthy while respecting Forge platform boundaries.

The product is especially useful for project leads, delivery managers, Jira administrators, ITSM teams, and business stakeholders who need different views of the same work data: workload distribution, request volumes, project progress, SLA performance, resolution time, and service health.

The customer should not have to care whether the chart is backed by a browser transform, a resolver, KVS, or SQL. They should care that the report opens reliably, the numbers are current enough to act on, and the app fits their admin and compliance model.

Reports Dashboard.png
Reports Portal.png
Frame 194 (1).png
Frame 195.png

Why the browser architecture made sense at first

When we first shipped Reports for Jira, Forge storage options were more limited. Key-value storage was excellent for settings, preferences, and small JSON documents. It was not designed to behave like a reporting database. Custom Entities improved structure and indexing, but relational queries, joins, and analytical aggregations still had to live mostly in application code.

At the same time, Forge resolvers had a practical execution window that made large dashboard loads difficult. Fetching thousands of Jira issues means network round trips to Jira, pagination, permission-aware API calls, transformation, and response serialization. For small datasets, that is routine. For 5,000-issue dashboards, it is a real workload.

So the first version pushed much of that work to the browser. The frontend called Jira REST APIs, received raw issue JSON, transformed it into chart data, and rendered the dashboard. That kept the resolver path light and gave us a fast way to deliver a useful app while staying fully on Forge.

For a young product, this was not a mistake. It was a sensible trade-off: use the browser when datasets are small, keep the backend simple, and avoid introducing complexity before the product proves it needs it.

Where the old model started to hurt

Reporting workloads changed the math. Customers started building larger dashboards, denser views, and customer-facing portal reports. The browser was no longer just rendering UI; it was becoming a temporary ETL engine.

  • Large raw payloads. A 5,000-issue dashboard could require the browser to process roughly 100 MB of raw Jira JSON before any useful chart appeared.
  • Main-thread pressure. Even when the request succeeded, transformation work could occupy the browser main thread and make the app feel heavier than it should.
  • Dashboard ceiling. Views were capped to prevent the browser from freezing under large datasets.
  • Portal report freshness. JSM portal customers are not licensed Jira users and cannot fetch Jira REST data with their own permissions, so portal reports depended on scheduled cache refreshes rather than live computation.
  • Cache fragmentation. Large portal caches had to be split into multiple key-value records because KVS values have size limits, which added cache management overhead.

The old model worked, but its costs had become visible to customers. That is usually the signal that an architecture has done its job and needs to be replaced.

The Forge constraint we chose to respect

The tempting answer would have been to move the hard work into a backend we control. For many reporting platforms, that is the obvious path. But Reports for Jira is intentionally built as a Forge-native app. That matters because many enterprise Atlassian customers increasingly care about where app compute and storage run, how egress is controlled, and whether app data follows Atlassian Cloud data residency expectations.

Atlassian describes the Runs on Atlassian program as a way to help customers identify Forge apps that use Atlassian-hosted compute and storage, support data residency that matches the host Atlassian app, and give customers control over external data egress. That is not just a badge for the listing. It is an architectural commitment.

The hard part was not simply adopting a new storage API. The hard part was designing a reporting pipeline inside Forge boundaries: resolver limits, Jira REST API limits, hosted storage limits, tenant isolation, migration safety, and the trust expectations that come with Runs on Atlassian.

Why Forge SQL changed the design

Forge SQL gave us the missing piece: a relational, queryable storage layer inside Atlassian-hosted Forge infrastructure. It let us stop treating every structured record like an opaque blob and start modeling views, dashboards, migration state, and portal report caches with a schema that matched the way the product actually works.

It is worth being precise about what changed. Forge SQL did not magically turn every chart into a database aggregation. Widget-level chart grouping still happens in application code today. For the live dashboard path, Jira issues are fetched, transformed in memory, and returned as computed chart data. We considered pushing more chart aggregation into SQL, but the current bottleneck is the Jira REST round trip, not the JavaScript grouping step. Adding a write-then-read database round trip before every widget would make many loads slower, not faster.

Forge SQL is doing the job it is best suited for in this architecture: structured storage for views and dashboards, reliable migration state, and a better cache foundation for portal reporting. That is enough to change the product experience without overcomplicating the hot path.

Before and after

The old path made the browser responsible for fetching raw Jira data and doing the heavy transformation locally:

Browser -> Jira REST API -> raw issue JSON -> browser transform -> charts and dashboard

The new path moves the data pipeline into Forge server-side resolvers:

Browser -> Forge resolver -> Jira REST API -> server-side transform/cache -> computed report data -> browser

That shift matters because the browser now receives the reporting result, not the full raw dataset needed to produce it. The UI remains the place where users interact with reports. It is no longer the place where we ask every customer machine to become a reporting backend.

architecture\_updated.png
Screenshot 2026-07-09 at 12.07.28 PM.png

How we designed around platform limits

We did not try to out-clever the resolver ceiling. We designed around it. The new resolver path fetches Jira issues server-side with tuned concurrency and returns computed report data to the client. We validated the approach against Jira Cloud rate limits rather than assuming that more parallelism would always be better.

We also evaluated Forge async events. They are useful for fire-and-forget background work, but the dashboard path needs a synchronous result. A user opens a dashboard and expects the chart data to come back in that request. Queuing work without a way to block and return the final result would not solve that problem.

This is the kind of Forge engineering that matters in production: knowing which platform primitive fits the workload, and just as importantly, knowing which one does not.

The migration had to be invisible

A faster architecture for new installs is easy compared with migrating existing customers. Every existing tenant had views and dashboards stored in KVS. Moving that data to SQL had to happen without a maintenance window, without a manual admin step, and without losing customer work.

We built the migration around Forge app upgrades. When a tenant receives the upgraded app version, a background job copies views and dashboards from KVS into SQL. A per-tenant migration status flag controls reads, so the app never assumes a migration has completed just because it was scheduled.

  • If the tenant is still on KVS, reads stay on KVS.
  • If the tenant is mid-migration, reads still stay on the old path.
  • Only after the copy is verified do reads switch to SQL.
  • If migration fails, retries run safely and the tenant remains on KVS rather than being stranded between two states.

We were deliberate about scope. Views and dashboards moved to SQL because relational modeling helps there. Small global settings and custom widget definitions stayed in KVS because they do not gain much from a schema. Disposable portal cache pages were not migrated because they can be rebuilt. Migrating throwaway data would have added risk without durable value.

What changed for users

The most visible improvement is that large dashboards no longer push the same volume of raw Jira JSON into the browser. In our benchmark, the median load time for a 5,000-issue dashboard dropped from about 7.2 seconds to about 4.8 seconds, while removing the roughly 100 MB client-side JSON transfer and the browser main-thread pressure that came with it.

That is not a claim that every report is instantly fast or that every part of the product is finished. The Jira REST round trip still dominates large live dashboard loads. The single-query ceiling from Jira still shapes the maximum size of some dashboard views. And server-side architecture introduces its own responsibilities: schema migrations, indexes, query monitoring, and careful operational handling of Forge SQL limits.

But the direction is clear. The product now has a better reporting foundation: less raw data in the browser, stronger structured storage, safer tenant migration, and a path to improve portal report freshness and gadget performance without redesigning the app again.

What this opens up next

This migration was never only about moving logic from the browser to the server. It was about making Reports for Jira a better Forge-native reporting foundation.

With Forge SQL in place, features that were awkward with KVS become much more realistic: public report links that do not require Jira authentication, better portal report cache invalidation, AI-assisted chart creation, richer dashboard templates, and more reliable server-side report generation for exports and gadgets.

The important part is not that the storage layer changed. The important part is that the product can now move closer to what customers actually want: reports that are fast enough to use every day, safe enough for admins to approve, and flexible enough to serve both Jira teams and JSM stakeholders.

Choosing the right VIEW26 reporting path

Reports for Jira is the lightweight Forge-native path. It is the right fit for teams that want secure Jira reports, custom dashboards, charts, gadgets, JQL-based filtering, and PDF or Excel exports while staying aligned with the Runs on Atlassian model.

Some service management teams eventually need a deeper reporting layer. Advanced JSM programs often ask for customer portal analytics, branded PDF reporting, SLA and Time-to-SLA dashboards, public links, scheduled stakeholder reports, multi-language support, and larger ITSM reporting workflows. That is where VIEW26 Charts, Portal Reports & Dashboards for JSM fits better.

We see these as two deliberate product paths, not one product replacing the other. Reports for Jira focuses on secure, Forge-native reporting inside Jira Cloud. Charts, Portal Reports & Dashboards for JSM is built for advanced JSM reporting use cases where teams need a broader service-management analytics experience.

The bigger picture

The best infrastructure migrations are not about the technology name. They are about making the product more honest about the work it has to do.

For Reports for Jira, moving the data pipeline from the browser to Forge server-side resolvers backed by Forge SQL lets the app stay true to its trust model while becoming a stronger reporting product. It gives customers a cleaner dashboard experience today and gives us a better foundation for the reporting features we want to build next.

If your team needs secure, lightweight Jira reporting, dashboards, gadgets, and exports, explore Reports for Jira on the Atlassian Marketplace. If your JSM reporting needs go deeper into customer portal analytics, SLA reporting, branded exports, and scheduled stakeholder communication, take a look at VIEW26 Charts, Portal Reports & Dashboards for JSM

gopika-photo

Gopika G · FullStack Engineer

More from the blog.