How to Connect JavaScript Reports to a PostgreSQL Database with Express
| Quick Start Guide | |
|---|---|
| What You Will Need |
ActiveReportsJS NPM NodeJS Express PostgreSQL |
| Controls Referenced | Report Designer |
| Tutorial Concept | Build an end-to-end, API-driven reporting workflow by connecting the ActiveReportsJS Report Designer to a PostgreSQL database using a Node.js and Express backend. You’ll learn how to securely expose relational data through an Express API and consume it in a browser-based report designer to design, preview, and render live reports in a real-world JavaScript application. |
Reporting remains an important part of many web applications, especially when working with structured or relational data. Whether you’re generating operational reports, financial summaries, or analytical views, a common challenge is connecting your reporting layer to a reliable backend data source in a way that’s secure, scalable, and easy to maintain.
In this article, we’ll walk through how to connect ActiveReportsJS’s Report Designer to a PostgreSQL database using Node.js and Express as a lightweight API layer. The goal is to demonstrate a practical, end-to-end workflow: querying data from Postgres on the server, exposing that data through an Express-based API, and then consuming it in a JavaScript application through the use of the Report Designer’s API calls.
Ready to Check Out the Latest from ActiveReportsJS? Download a Free Trial Today!
Why PostgreSQL and Express
We chose PostgreSQL because it’s one of the most widely adopted relational databases in production environments. Its strong performance, advanced querying capabilities, and compliance make it a great fit for reporting scenarios. Many teams already rely on Postgres as their primary data store, making it a great realistic backend for this example.
For the server layer, Express provides a simple and flexible way to expose database data to front-end applications. Instead of connecting directly to the database - which isn’t secure or scalable - Express serves as an API boundary. This approach reflects how engineers structure real-world applications and allows you to centralize data access, and in the future, validation and business logic.
On the client side, ActiveReportsJS offers a powerful, browser-based reporting solution that allows developers and report authors to design, preview, and render reports directly within the JavaScript application. By connecting ActiveReportJS to an API-backed data source, you gain the flexibility to work with live data while maintaining a clean separation between your reporting UI and backend services.
What This Article Covers
By the end of this article, you’ll have a working example that demonstrates how these technologies fit together. Specifically, we’ll cover:
-
Setting up a PostgreSQL database with sample data for reporting
-
Creating an Express server that connects to Postgres and exposes data through an API
-
Configuring the ActiveReportsJS Report Designer in a JavaScript application
-
Fetching data from the Express API within the Report Designer
We’ll start by setting up a PostgreSQL database that will serve as the data source for our report.
Setting Up a PostgreSQL Database
The first thing we’re going to need to do is set up the PostgreSQL database so we can retrieve data with the Express server we set up in the next step.
For PostgreSQL, you have a couple of options for managing your databases; you can use the command line to log in to your database and run SQL commands there, or you can use one of the GUIs available to manage your databases. I’ll be using pgAdmin, but no matter what you choose, the scripts that we run will be the same.
For our database, we’re going to store information about different vehicles, which can be retrieved and displayed in tables in our report. To get started, we’re going to create a couple of tables in our database, which we’ve named Vehicles:
Ford Table
CREATE TABLE ford (
id BIGSERIAL PRIMARY KEY,
model VARCHAR(50) NOT NULL,
trim VARCHAR(50),
year INTEGER NOT NULL CHECK (year >= 1886),
body_type VARCHAR(30),
vehicle_type VARCHAR(30),
engine_type VARCHAR(50),
fuel_type VARCHAR(20)
);
General Motors Table
CREATE TABLE gm (
id BIGSERIAL PRIMARY KEY,
model VARCHAR(50) NOT NULL,
trim VARCHAR(50),
year INTEGER NOT NULL CHECK (year >= 1886),
body_type VARCHAR(30),
vehicle_type VARCHAR(30),
engine_type VARCHAR(50),
fuel_type VARCHAR(20)
);
Stellantis Table
CREATE TABLE stellantis (
id BIGSERIAL PRIMARY KEY,
model VARCHAR(50) NOT NULL,
trim VARCHAR(50),
year INTEGER NOT NULL CHECK (year >= 1886),
body_type VARCHAR(30),
vehicle_type VARCHAR(30),
engine_type VARCHAR(50),
fuel_type VARCHAR(20)
);
These tables contain 8 fields that will hold information on the different types of Ford, General Motors, and Stellantis vehicles that will be stored here.
Now that the tables are created, we’ll insert some data into each of them that we can retrieve with our client. To do so, we’re going to run some INSERT statements:
Ford Insert
INSERT INTO ford (
model,
trim,
year,
body_type,
vehicle_type,
engine_type,
fuel_type
)
VALUES
('F-150', 'XL', 2022, 'Truck', 'Commercial', 'V6', 'Gasoline'),
('F-150', 'XLT', 2023, 'Truck', 'Commercial', 'V6 Turbo', 'Gasoline'),
('F-150', 'Lightning', 2023, 'Truck', 'Commercial', 'Electric', 'Electric'),
('Mustang', 'EcoBoost', 2021, 'Coupe', 'Passenger', 'I4 Turbo', 'Gasoline'),
('Mustang', 'GT', 2022, 'Coupe', 'Passenger', 'V8', 'Gasoline'),
('Mustang', 'Mach-E', 2023, 'SUV', 'Passenger', 'Electric', 'Electric'),
('Explorer', 'XLT', 2022, 'SUV', 'Passenger', 'V6', 'Gasoline'),
('Explorer', 'Limited', 2023, 'SUV', 'Passenger', 'Hybrid', 'Hybrid'),
('Escape', 'SE', 2021, 'SUV', 'Passenger', 'I4', 'Gasoline'),
('Escape', 'Hybrid', 2022, 'SUV', 'Passenger', 'Hybrid', 'Hybrid'),
('Edge', 'SEL', 2020, 'SUV', 'Passenger', 'V6', 'Gasoline'),
('Bronco', 'Base', 2022, 'SUV', 'Passenger', 'I4 Turbo', 'Gasoline'),
('Bronco', 'Badlands', 2023, 'SUV', 'Passenger', 'V6 Turbo', 'Gasoline'),
('Ranger', 'Lariat', 2021, 'Truck', 'Commercial', 'I4 Turbo', 'Gasoline'),
('Transit', 'Cargo', 2022, 'Van', 'Commercial', 'V6', 'Gasoline'),
('Transit', 'Passenger', 2023, 'Van', 'Commercial', 'V6', 'Gasoline'),
('Expedition', 'XLT', 2022, 'SUV', 'Passenger', 'V6 Turbo', 'Gasoline'),
('Fusion', 'SE', 2020, 'Sedan', 'Passenger', 'I4', 'Gasoline'),
('Fusion', 'Hybrid', 2020, 'Sedan', 'Passenger', 'Hybrid', 'Hybrid');
General Motors Insert
INSERT INTO gm (
model,
trim,
year,
body_type,
vehicle_type,
engine_type,
fuel_type
)
VALUES
('Silverado 1500', 'WT', 2022, 'Truck', 'Commercial', 'V6', 'Gasoline'),
('Silverado 1500', 'LT', 2023, 'Truck', 'Commercial', 'V8', 'Gasoline'),
('Colorado', 'Z71', 2022, 'Truck', 'Commercial', 'V6', 'Gasoline'),
('Tahoe', 'LS', 2022, 'SUV', 'Passenger', 'V8', 'Gasoline'),
('Suburban', 'LT', 2023, 'SUV', 'Passenger', 'V8', 'Gasoline'),
('Equinox', 'LT', 2021, 'SUV', 'Passenger', 'I4 Turbo', 'Gasoline'),
('Traverse', 'RS', 2022, 'SUV', 'Passenger', 'V6', 'Gasoline'),
('Malibu', 'LS', 2020, 'Sedan', 'Passenger', 'I4', 'Gasoline'),
('Camaro', 'SS', 2021, 'Coupe', 'Passenger', 'V8', 'Gasoline'),
('Bolt EV', 'LT', 2022, 'Hatchback', 'Passenger', 'Electric', 'Electric'),
('Sierra 1500', 'SLE', 2023, 'Truck', 'Commercial', 'V8', 'Gasoline'),
('Canyon', 'AT4', 2022, 'Truck', 'Commercial', 'V6', 'Gasoline'),
('Yukon', 'SLT', 2023, 'SUV', 'Passenger', 'V8', 'Gasoline'),
('Escalade', 'Luxury', 2023, 'SUV', 'Passenger', 'V8', 'Gasoline'),
('XT5', 'Premium', 2022, 'SUV', 'Passenger', 'V6', 'Gasoline'),
('CT5', 'Sport', 2021, 'Sedan', 'Passenger', 'V6 Turbo', 'Gasoline'),
('Encore', 'Preferred', 2021, 'SUV', 'Passenger', 'I4 Turbo', 'Gasoline'),
('Envision', 'Essence', 2022, 'SUV', 'Passenger', 'I4 Turbo', 'Gasoline'),
('Enclave', 'Avenir', 2023, 'SUV', 'Passenger', 'V6', 'Gasoline'),
('Blazer EV', 'RS', 2024, 'SUV', 'Passenger', 'Electric', 'Electric');
Stellantis Insert
INSERT INTO stellantis (
model,
trim,
year,
body_type,
vehicle_type,
engine_type,
fuel_type
)
VALUES
('Wrangler', 'Sport', 2022, 'SUV', 'Passenger', 'V6', 'Gasoline'),
('Wrangler', 'Rubicon', 2023, 'SUV', 'Passenger', 'V6', 'Gasoline'),
('Grand Cherokee', 'Laredo', 2022, 'SUV', 'Passenger', 'V6', 'Gasoline'),
('Grand Cherokee', '4xe', 2023, 'SUV', 'Passenger', 'Hybrid', 'Hybrid'),
('Compass', 'Latitude', 2021, 'SUV', 'Passenger', 'I4', 'Gasoline'),
('Renegade', 'Trailhawk', 2022, 'SUV', 'Passenger', 'I4 Turbo', 'Gasoline'),
('1500', 'Tradesman', 2022, 'Truck', 'Commercial', 'V6', 'Gasoline'),
('1500', 'Laramie', 2023, 'Truck', 'Commercial', 'V8', 'Gasoline'),
('2500', 'Big Horn', 2022, 'Truck', 'Commercial', 'V8', 'Diesel'),
('3500', 'Limited', 2023, 'Truck', 'Commercial', 'V8', 'Diesel'),
('Charger', 'SXT', 2021, 'Sedan', 'Passenger', 'V6', 'Gasoline'),
('Charger', 'R/T', 2022, 'Sedan', 'Passenger', 'V8', 'Gasoline'),
('Challenger', 'GT', 2021, 'Coupe', 'Passenger', 'V6', 'Gasoline'),
('Challenger', 'SRT Hellcat',2022, 'Coupe', 'Passenger', 'V8', 'Gasoline'),
('Pacifica', 'Touring', 2022, 'Minivan','Passenger', 'V6', 'Gasoline'),
('Pacifica', 'Hybrid', 2023, 'Minivan','Passenger', 'Hybrid', 'Hybrid'),
('300', 'Touring', 2021, 'Sedan', 'Passenger', 'V6', 'Gasoline'),
('Giulia', 'Ti', 2022, 'Sedan', 'Passenger', 'I4 Turbo', 'Gasoline'),
('Stelvio', 'Sprint', 2023, 'SUV', 'Passenger', 'I4 Turbo', 'Gasoline'),
('Tonale', 'Veloce', 2024, 'SUV', 'Passenger', 'Hybrid', 'Hybrid');
These tables will now each have 20 rows of data for us to work with in the JavaScript Report Designer.
Finally, if you’d like to test and make sure that everything looks correct, you can run any of the following commands to see the data returned:
SELECT * FROM ford;
SELECT * FROM gm;
SELECT * FROM stellantis;
If we run the select statement for the General Motors table, here’s what we get returned:

Now, with our table set up, we can move on to setting up the Express server so that we can get data from our database to our frontend.
Creating an Express Server to Connect to PostgreSQL
To get started, we’re going to be using VisualStudio Code as our IDE of choice. Open the editor, select a folder that you can store your Express server and JavaScript application in, and create a new folder called express-server. Navigate into that folder, and in the VSCode terminal, run the following command:
npm init
This will start you through the process of initializing a package.json file that can be used to set up the server. Feel free to name it what you want and give it any sort of description, but take note of the entry point for the application: index.js. You can change this if you want, but just note that this file will serve as the entry point for our Express server.
Next, we’ll need to install a couple of packages:
npm install express cors pg
-
Express is the package that we’ll use to create our server
-
Cors will be used to allow us to make cross-origin requests
-
Pg is used so that we can query Postgres via our Express server
Once those are installed, we’ll then create two files:
-
index.js - the entry point to our Express server
-
queries.js - will be used to set up database queries that Express can use
The first thing that we’ll do is open up our queries.js file and add the required code there:
const Pool = require('pg').Pool;
const pool = new Pool({
user: USER,
host: 'localhost',
database: 'Vehicles',
password: PASSWORD,
port: 5432
});
module.exports = {
query: (text, params) => pool.query(text, params), pool,
}
What this does is create a Pool object that allows us to manage our database connections and queries. As you can see, we’re setting user, host, database, password, and port properties; your user and password will be different than mine, but if you set everything else up properly and didn’t change the default port, the other settings should work fine for you.
Next, over in the index.js file, we’ll set up our Express server:
const express = require('express');
const cors = require('cors');
const app = express();
const port = 3000;
const db = require('./queries');
app.use(cors());
app.use(express.json());
app.get('/', (req, res) => {
res.json({ info: 'Node.js, Express, Postgres API, and Wijmo FlexGrid' })
});
app.get('/ford', async (req, res) => {
try {
const results = await db.query('SELECT * FROM ford');
res.status(200).json(results.rows);
} catch (err) {
res.status(500).send('Server Error');
}
});
app.get('/gm', async (req, res) => {
try {
const results = await db.query('SELECT * FROM gm');
res.status(200).json(results.rows);
} catch (err) {
res.status(500).send('Server Error');
}
});
app.get('/stellantis', async (req, res) => {
try {
const results = await db.query('SELECT * FROM stellantis');
res.status(200).json(results.rows);
} catch (err) {
res.status(500).send('Server Error');
}
});
app.listen(port, () => {
console.log(`Server listening on port ${port}`);
});
The first thing that we do is import Express and Cors into the file so that we can make use of them. We then use express() to set up the app, define the port, and import the query that we just set up. We’ll also need to make sure the app uses the Cors library that we imported, and also has access to the express.json() functionality for sending our data to the frontend.
Next, we’re going to set up a couple of get() method. The first is just a generic route that will return some JSON to let us know that the server is running.
The next three methods that we set up are going to make the calls to our Postgres database to retrieve our data. All we need to do for these methods is set up a try/catch method, wait for the query to our database to complete, and then return it to the client using express.json(). Each of these methods calls a different table that we set up in the database.
The last thing we do is set up our listen method using the port that we defined at the head of this file. Then, by running node index.js, our server will be up and running, waiting for requests from clients so that it can retrieve data from our database.
Configuring the ActiveReportsJS Report Designer in a JavaScript Application
With our database and server complete, the next thing that we need to do is set up our JavaScript application to use the ActiveReportsJS JavaScript Report Designer.
To get started, we’ll need to import all of the required files to use the report designer. Inside of your HTML file’s <head> tag, add the following code:
<link
rel="stylesheet"
href="https://cdn.mescius.com/activereportsjs/6.latest/styles/ar-js-ui.css"
type="text/css"
/>
<link
rel="stylesheet"
href="https://cdn.mescius.com/activereportsjs/6.latest/styles/ar-js-designer.css"
type="text/css"
/>
<script src="https://cdn.mescius.com/activereportsjs/6.latest/dist/ar-js-core.js"></script>
<script src="https://cdn.mescius.com/activereportsjs/6.latest/dist/ar-js-designer.js"></script>
These imports include the required CSS files (the core ui and designer files), and the required JavaScript files (the core js and designer files).
Next, we’ll add some additional CSS inside of some <style> tags:
<style>
body {
margin: 0;
}
#designer-host {
width: 100%;
height: 100vh;
}
</style>
This removes the margin from our body page, and defines the height and width of the report designer so that it fills the browser window.
Then, we’ll add in a <div> element that we can bind the report designer to:
<div id="designer-host"></div>
Finally, we’ll add in some <script> tags that will be used to initialize the JavaScript Report Designer:
<script>
var designer = new MESCIUS.ActiveReportsJS.ReportDesigner.Designer('#designer-host');
</script>
For running the application, we're using the VSCode plugin Live Server - you can feel free to run your application however you want, but for the purposes of this demo, we're going to be using this plugin. Now, if we run the application, we’ll see the Report Designer loaded into the browser, ready to go:

Fetching Data from the Express API with the Report Designer
With our JavaScript Report Designer ready, we can now go about binding some data to the designer. Open up the Data panel on the right-hand side, and we’re going to add a new data source:

When we add a new data source, it will open up the data source connection wizard, which we’ll use to point to our Express API:

There are a few fields that we have to fill out here. First, we’ll give it a name - in this case, we’ll just call it Vehicles. Then, we select the Source Type as Remote, since the data is coming from a server. Finally, we give it an endpoint. Since this server is just running locally on port 3000, we can just point it towards http://localhost:3000/. We can then hit the Save Changes button and move on to actually pulling data into the report.
Now, we need to click on the + icon next to our data source in order to create a data set:


For the first data set, we’ll point towards the API call that will retrieve data from the Ford table in our database. We set the Uri/path to match the correct path on our server, and then we’ll set the Json Path to $.* - this just signifies that we want to retrieve all of the data instead of filtering anything out.
Before we click the Save Changes button, we’ll need to click the Validate button to make sure that we can actually retrieve the data. If everything is set up correctly, the Database Fields section should now contain items, like in the screenshot above.
There are two more data sets that we need to set up from our Vehicles data source, which you can see below:


With our data source and data sets created, the last step is to load this data into our report to display the data.
Designing and Previewing a Report with the Loaded Data
With our data loaded, we can finally go about building the report to display our data. In this report, we’re going to use some tables. To create the table, we’re going to select each of the elements from our dataset:

And then drag and drop the fields from the data set into the report area:

We’ll do that for each of the other tables, as well as add some table headers and some additional table styles. When we’re done, our report will look like the following:

And just like that, we’ve created a report, linked it to the API calls on the Express server to get data from our Postgres database, and bound those properties to a report.
Conclusion
By following this walkthrough, we’ve demonstrated how PostgreSQL, Express, and ActiveReportsJS can be combined to create a flexible, API-driven reporting workflow in a JavaScript application. Using Express as an intermediary between the database and the reporting layer keeps your data access secure while allowing the Report Designer to consume live data through clearly defined endpoints. ActiveReportsJS makes it possible to design, preview, and render rich reports directly in the browser, all while remaining decoupled from the underlying database implementation. While this example focused on a straightforward reporting scenario, the same approach can be extended to support more complex queries, parameters, filtering, and authentication, giving you a solid foundation for building robust, data-driven reporting solutions.
If you’d like to try out ActiveReportsJS for yourself, you can download the library here.
Happy coding!
Ready to Check Out the Latest from ActiveReportsJS? Download a Free Trial Today!