Generate Data Dashboards from Natural Language with Querier AI
Generate Data Dashboards from Natural Language with Querier AI Building an analytics dashboard usually means deciding which queries to run, choosing chart types, mapping result columns to datasets, and writing frontend
Generate Data Dashboards from Natural Language with Querier AI
Building an analytics dashboard usually means deciding which queries to run, choosing chart types, mapping result columns to datasets, and writing frontend code to display everything.
What if you could start with a sentence instead?
For example:
Show me a bar chart of monthly revenue, a pie chart of orders by status, and a line chart of new customers per week.
I recently released new AI capabilities in Querier, my open-source Java SQL query builder. The optional querier-ai module can translate a natural-language prompt into a validated dashboard definition, while querier-ui renders that definition as HTML powered by Chart.js.
In this tutorial, we'll integrate AI-powered dashboard generation into a Spring Boot MVC application and embed the rendered dashboard in a Thymeleaf page.
What we're building
The application will:
- Accept a natural-language dashboard description.
- Ask Querier AI to generate a dashboard definition based on the entities we expose.
- Validate the result before rendering it.
- Execute the dashboard's queries through Spring JDBC.
- Render the resulting charts inside an existing HTML page.
Querier handles the dashboard definition and rendering. Your application supplies the database execution layer and decides which data the AI is allowed to see.
1. Add the required dependencies
For AI-generated dashboards, add both querier-ai and querier-ui to your Maven project:
<dependency>
<groupId>com.aytmatech</groupId>
<artifactId>querier-ai</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>com.aytmatech</groupId>
<artifactId>querier-ui</artifactId>
<version>2.0.0</version>
</dependency>
These examples use Querier 2.0.0. Check the release page before starting a new project.
You don't need to add querier-core separately: the AI module declares it as a dependency. The UI module provides the dashboard-building and rendering functionality used in this tutorial.
You'll also need a Spring application with a configured database connection and the entity classes representing your data.
2. Configure the AI client
Querier AI uses a bring-your-own-key configuration and supports OpenAI-compatible endpoints such as OpenAI, Azure OpenAI, Mistral, and Ollama-compatible setups.
Set the configuration through environment variables:
export QUERIER_AI_API_KEY="your-api-key"
export QUERIER_AI_BASE_URL="https://api.openai.com/v1"
export QUERIER_AI_MODEL="gpt-4o-mini"
Adjust these values for your chosen provider. Keep credentials out of source control and never log the API key.
Register the AI components as Spring beans:
import com.aytmatech.querier.ai.AiClient;
import com.aytmatech.querier.ai.QuerierAi;
import com.aytmatech.querier.ai.retry.RetryPolicy;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class QuerierAiConfig {
@Bean
public AiClient aiClient() {
return AiClient.builder()
.apiKey(System.getenv("QUERIER_AI_API_KEY"))
.baseUrl(System.getenv("QUERIER_AI_BASE_URL"))
.model(System.getenv("QUERIER_AI_MODEL"))
.build();
}
@Bean
public QuerierAi querierAi(AiClient aiClient) {
return QuerierAi.builder()
.client(aiClient)
.retryPolicy(RetryPolicy.ofMaxAttempts(2))
.build();
}
}
We'll inject QuerierAi into the dashboard controller in the next step.
3. Define the schema the AI can use
Let's use three entity classes as an example:
private static final Class<?>[] EXPOSED_ENTITIES = {
Order.class,
Customer.class,
Product.class
};
These classes should correctly describe the database tables and columns available to the feature. The first class is treated as the primary entity by the schema inspector, so list your main entity first.
Querier can inspect the exposed classes and their table mappings to create a schema context:
import com.aytmatech.querier.ai.model.AiSchemaContext;
import com.aytmatech.querier.ai.model.AiSchemaInspector;
AiSchemaContext schemaContext =
AiSchemaInspector.inspect(EXPOSED_ENTITIES);
You can also use semantic descriptions and synonyms to help the model map business language to the right columns. For example, a column storing an order's monetary value might be described using terms such as "revenue," "sales value," and "order value."
This is especially useful when your business vocabulary doesn't match your database naming conventions.
4. Generate a dashboard from a prompt
Let's implement a Spring MVC endpoint at POST /ai/dashboard.
The key method is QuerierAi.dashboard(). It returns a DashboardResult that you can check before attempting to render the generated dashboard.
import com.aytmatech.querier.Select;
import com.aytmatech.querier.ai.QuerierAi;
import com.aytmatech.querier.ai.analytics.DashboardResult;
import com.aytmatech.querier.ai.model.AiSchemaContext;
import com.aytmatech.querier.ai.model.AiSchemaInspector;
import com.aytmatech.querier.ai.query.QueryRequest;
import com.aytmatech.querier.dashboard.DashboardRenderer;
import com.aytmatech.querier.dashboard.QueryRunner;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
import java.util.Map;
@Controller
@RequestMapping("/ai")
public class AiDashboardController {
private static final Class<?>[] EXPOSED_ENTITIES = {
Order.class,
Customer.class,
Product.class
};
private final QuerierAi querierAi;
private final NamedParameterJdbcTemplate jdbc;
public AiDashboardController(
QuerierAi querierAi,
NamedParameterJdbcTemplate jdbc) {
this.querierAi = querierAi;
this.jdbc = jdbc;
}
@PostMapping("/dashboard")
public String dashboard(
@RequestParam("prompt") String prompt,
Model model) {
model.addAttribute("dashboardPrompt", prompt);
AiSchemaContext schemaContext =
AiSchemaInspector.inspect(EXPOSED_ENTITIES);
QueryRequest request = QueryRequest.builder()
.prompt(prompt)
.schemaContext(schemaContext)
.temperature(0.2)
.build();
DashboardResult result = querierAi.dashboard(request);
if (!result.isValid()) {
model.addAttribute(
"dashboardError",
String.join("\n", result.validationIssues()));
result.interpretationNote().ifPresent(
note -> model.addAttribute("dashboardNote", note));
return "ai/index";
}
// Bridge Querier queries to the existing database layer.
QueryRunner runner = select -> {
Select.SqlAndParams sqlAndParams = select.toSqlAndParams();
return jdbc.queryForList(
sqlAndParams.sql(),
sqlAndParams.params());
};
String dashboardFragment = DashboardRenderer.builder()
.dashboard(result.dashboard().orElseThrow())
.queryRunner(runner)
.omitChartJsScriptElement(true)
.build()
.renderFragment();
result.interpretationNote().ifPresent(
note -> model.addAttribute("dashboardNote", note));
model.addAttribute("dashboardFragment", dashboardFragment);
return "ai/index";
}
}
The important pieces are:
QueryRequest carries the prompt and the schema context exposed to the AI.
DashboardResult allows you to check whether a valid dashboard was produced. If not, the controller reports the validation issues rather than attempting to render the result.
QueryRunner connects the generated dashboard to your applicationβs database layer. Each Querier Select is converted to SQL and named parameters, then executed through NamedParameterJdbcTemplate.
DashboardRenderer takes the validated dashboard and query runner and produces an HTML fragment that can be embedded in the page.
The temperature value is a request setting, not a guarantee that model output will always be identical.
5. Embed the generated charts in Thymeleaf
Because we're rendering a fragment inside an existing page, load Chart.js in the page layout or template:
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
The controller calls .omitChartJsScriptElement(true) because Chart.js is already loaded by the application. This avoids adding another Chart.js library script element to the generated fragment.
Next, add the dashboard form:
<form method="post" th:action="@{/ai/dashboard}">
<textarea
name="prompt"
placeholder="Show monthly revenue, orders by status, and new customers per week"
required></textarea>
<button type="submit">Generate dashboard</button>
</form>
Display any validation errors:
<div th:if="${dashboardError}" class="alert alert-error">
<strong>Could not generate a valid dashboard:</strong>
<pre th:text="${dashboardError}"></pre>
</div>
Finally, embed the generated fragment:
<div class="dashboard-wrap"
th:if="${dashboardFragment}"
th:utext="${dashboardFragment}">
</div>
th:utext deliberately inserts the HTML without escaping it, which is necessary for rendering the fragment as markup. Use it only for HTML you trust. Don't concatenate raw user input into the fragment, and review the rendering boundary as part of your application's security design.
The generated output is HTML, so you don't need to write custom JavaScript to construct each chart or manually map its dataset columns.
6. What kinds of dashboards can you generate?
You can describe several charts in one prompt. For example:
Create an order analytics dashboard with monthly revenue as a line chart, order counts by status as a doughnut chart, and the top 10 customers by revenue as a bar chart. Use a two-column layout.
Querier UI supports chart types including BAR, HORIZONTAL_BAR, LINE, PIE, DOUGHNUT, RADAR, POLAR_AREA, SCATTER, and BUBBLE.
You can also create dashboards manually with Querier's Java API. AI-generated dashboards provide another way to create the dashboard definition, while the rendering API remains available for both approaches.
7. A few production considerations
Generating charts is only one part of an analytics feature. The application still needs to enforce its own data access rules.
Before deploying this to production, make sure you:
- Expose only the entities and columns the feature should query.
- Enforce authorization and tenant isolation outside the prompt itself.
- Use a database account with appropriately limited permissions.
- Apply execution timeouts and limits to expensive queries and large result sets.
- Keep API credentials in secure configuration, not frontend code or logs.
- Treat rendered HTML as an output boundary and avoid inserting untrusted HTML.
A valid dashboard definition does not mean every underlying query is appropriate for every user. Keep authorization and database access controls in your application.
Get started with Querier
With a small amount of Spring configuration and a QueryRunner adapter, you can turn a natural-language description into a dashboard rendered by your existing Java application.
No custom chart-generation JavaScript is required. You keep control of database execution and can use Querier's dashboard renderer inside your existing web interface.
Explore the source and documentation:
I'm looking forward to seeing how developers use Querier AI to make data exploration more accessible in their Java applications.
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes β full credit and traffic to the original publisher.