Skip to main content

Why Dara? Not Just Another App Framework

· 10 min read
Krzysztof Bielikowicz

Introduction

Pearl Investment App

Dara is our open-source application framework designed for anyone who needs to turn their data and workflows into interactive, easy-to-use applications - especially data scientists, developers and decision makers. It’s designed to help people create engaging visuals and comprehensive applications without hassle.

The framework was born out of necessity; both our internal teams and our customers needed a seamless way to turn intricate work into interactive and user-friendly applications. Its development was driven by limitations found in existing solutions, where users grapple with restrictive extensibility, escalating complexity, compromised performance and challenging integrations. The current landscape made the transition from static data representation to dynamic, actionable insights a real challenge, especially for those wanting to leverage Causal AI-powered workflows.

We designed Dara to answer these problems by providing simple reusable components, a flexible and modular approach to managing application state and a smooth learning curve from your first component to a multi page application packed with interaction points.

With Dara, you can quickly build aesthetically pleasing and functionally rich applications using just Python. It also offers the flexibility for customization, allowing users to integrate custom components using JavaScript and CSS to meet their various needs.

In essence, Dara is a practical solution for those who want to present complex workflows or develop in-depth applications to support decision-making. It’s a versatile tool that helps users make sense of and act on their insights in the complex world of data-driven decision-making.

Why do we need app frameworks?

Professionals in fields such as data science or analytics frequently use Jupyter notebooks to present and share their work, models and results. These tools excel at rapid prototyping and model building phases of the workflow. However, when it comes to presenting these findings, Jupyter notebooks are unintuitive, confusing, and static for wider business stakeholders.

To bridge this gap, there exist frameworks designed to facilitate the creation of interactive web applications, enabling professionals to convey their insights more effectively. These frameworks are intended to be accessible, offering simple APIs in languages Data Scientists are familiar with, such as Python, enabling those with no experience building web applications to create intuitive and visually compelling interfaces.

Pitfalls of existing solutions

There are several popular dashboarding solutions in the ecosystem. However, when reviewing the alternatives at causaLens, we have found a few persistent issues in the design of these frameworks that stopped us from embracing what was already out there.

  • Limited Extensibility: Many frameworks offer a fixed set of components, and we needed to be able to extend the existing behaviors with our innovations.
  • Increased Complexity: Several frameworks became challenging to navigate as code complexity increased, especially when evolving from simple single-page dashboards to multi-page applications with intricate interactions.
  • Compromised Performance: The performance of some frameworks was severely hampered by their dependency to call back to the Python code for even the simplest update or interaction, often requiring full page refreshes.
  • Challenging Integration: Some dashboarding frameworks posed integration challenges, making it cumbersome to add REST endpoints for automated processes to access the same functionality as the dashboard without implementing multiple solutions.

Our solution

As a result, we set out to build our own application framework to solve these challenges. We needed something that worked for us and our customers. These were our guiding principles:

Native Causality Support

We believe that Causal AI tech is a foundation to empower informed decision making through explainable causal models. In order to interact and explain Causal Models we have built a set of components that let you visualize, tweak, and explore them. They allow decision makers to interact with complex technology in a simple and user-friendly way.

Discover the capabilities of our graph viewer component. We’ve also open-sourced a complementary casual graph library to assist in defining and working with causal structures. We invite you to experience and explore these tools, developed to support the visualization and exploration of intricate causal relationships.

Graph Viewer Gallery App

Graph Viewer showcase gallery app

Smooth learning curve & scalability

Dara provides a gentle learning curve, allowing users to progress from creating basic dashboards with no user interaction to developing intricate, multi-page applications featuring numerous interaction points, all while maintaining clean and manageable code.

When you’re just starting with Dara, creating your first app is quite straightforward. Here’s how you can create a simple ‘Hello World’ in Dara:

main.py
from dara.core import ConfigurationBuilder

# Initialize app config, using builder pattern
# to build up the app piece by piece
config = ConfigurationBuilder()

# Add a single page at /hello, with the text 'World' on the page
config.add_page(title='Hello', content='World')

Hello World App

Simple ‘Hello World’ in Dara

On the flipside, if you’re a seasoned developer, know that Dara can handle the heavy lifting - the framework has been thoroughly tested by our internal team, running multiple multi-page complex web apps in production.

Extendability

In Dara, components are the heart of your application. Think of them as reusable pieces of UI that act as building blocks. You can arrange, modify, and integrate these blocks wherever needed within your app, making application design both efficient and structured.

Now, the foundation of Dara lies in its core – the dara-core. This core is responsible for the fundamental architecture of the framework. It provides the scaffolding and essential utilities that enable all other functionalities. While dara-core lays the foundation with essential abstractions and interactivity primitives for application development, it doesn't come bundled with a comprehensive set of user interface elements.

That's where libraries like dara-components step in, offering a rich set of ready-to-use components that are built on top of the core's abstractions. This module is built upon the sturdy abstractions provided by dara-core and offers a vast library of pre-designed components. It's like the toolkit that equips you with everything you need to construct a full-fledged application. To showcase its potential, let's revisit our previous example and add a Text component:

main.py
from dara.core import ConfigurationBuilder
from dara.components import Text

config = ConfigurationBuilder()

# Add a single page at /hello, with the text 'World' on the page
config.add_page(title='Hello', content=Text(text='World'))

The best part is - you can create a multitude of components by leveraging the existing building blocks Dara provides, all without needing any knowledge of JavaScript. Function-based (or class based, depending on your preference) components in Dara allow you to encapsulate and reuse logic, making your applications more modular and maintainable. You can piece together these components like building blocks, orchestrating them to create intricate, fully-fledged applications.

Here's a brief example of how you can compose a component using a function:

main.py
from dara.core import ConfigurationBuilder, SideEffect
from dara.components import Button, Stack, Text

def GreetingPanel(name: str):
greeting_text = Text(text=f"Hello, {name}!")
greet_button = Button("Greet", onclick=SideEffect(lambda: print(f"Greeted {name}")))

return Stack(greeting_text, greet_button)

config = ConfigurationBuilder()
config.add_page(title="Greet", content=GreetingPanel("John"))

Example of creating a reusable GreetingPanel component in Dara

In this example, GreetingPanel is a custom component created by combining Text and Button components, demonstrating how easily existing components can be composed to create new ones. Using this approach, you can create comprehensive and interactive applications, mastering the art of component composition before deciding to delve deeper into JavaScript for more advanced customization.

Remember, writing custom components in JavaScript is entirely optional in Dara. Most users find that the extensive set of built-in components, combined with the power of function-based composition, fulfills their needs, making the transition to creating applications smooth and enjoyable.

Once you understand how to incorporate components into your applications, you might be curious about how components are constructed in Dara. Components are essentially ComponentInstance subclasses, mapping its class properties to a React component.

Python Definition (dara.components/text.py)
from dara.core.definitions import ComponentInstance

class Text(ComponentInstance):
js_module = '@darajs/components'
text: str
TypeScript Implementation (@darajs/core/index.tsx)
interface TextProps {
text: string;
}

export function Text(props: TextProps) {
return <p>{props.text}</p>;
}

Simplified implementation of a Text component

This simple but powerful abstraction is the foundation upon which the dara.components library is built upon, and how you can also choose to write your own components.

After understanding how to integrate and construct components, it’s essential to explore how Dara supports further customization. The ConfigurationBuilder instance is your gateway to a plethora of customization options, allowing you to tailor your projects according to your evolving needs. From integrating authentication configurations, registering custom components, and themes to additional HTTP endpoints, Dara ensures your projects can grow in complexity without compromising structure or maintainability.

This unified interface enables the straightforward distribution of reusable plugins, even beyond the core framework. Here’s a snippet showcasing the integration of a custom plugin:

Definition
from dara.core import ConfigurationBuilder

def custom_plugin(config: ConfigurationBuilder):
config.set_theme(SomeCustomTheme)
config.add_endpoint(some_custom_endpoint)
# ...
Usage
from dara.core import ConfigurationBuilder
from some_package import custom_plugin

config = ConfigurationBuilder()
custom_plugin(config)

Example of a custom Dara plugin

With Dara, it’s not just about starting simple; it’s about scaling and extending your projects effortlessly, no matter how complex your needs become!

Plot Interactivity App

Plot Interactivity Dara gallery app

Close to native web-app performance

To ensure scalability, our architecture is designed with reducing the client-server back-and-forth in mind. Therefore, state updates happen mostly in the browser. Your application will only call into the Python side when absolutely necessary.

For instance, if you’re just connecting an Input component to a Text component, there’s no need to involve your server; the data never leaves your browser.

Variable data flow

Data flow with a simple Input->Text synchronization; update happens without server interaction

In situations where you need to run calculations based on user input, the framework is smart—it only pings the server for that specific calculation while the rest of the rendering stays client-side.

Derived Variable data flow

Data flow involving a derived computation result; update happens in the browser, a single request is made to the server for just the computation result

We’re just scratching the surface with these diagrams. They offer a glimpse into the architecture which powers Dara.

Support hybrid access modes

As we touched upon earlier, our framework streamlines the process of defining and adding REST endpoints through a simplified API. Here’s how:

main.py
from dara.core import ConfigurationBuilder
from dara.core.http import get

# route definition, can be anywhere in your app or an external package
@get('/hello/{name}')
async def hello_route(name: str):
return f'Hello, {name}!'


config = ConfigurationBuilder()

# register the route in your app
config.add_endpoint(hello_route)

Example of registering a custom route in a Dara app

This makes it simple for automated processes to consume some of the business logic presented in your dashboard. The routes added are covered by the authentication system (which you can opt out of), making your app secure by default.

And for those familiar with FastAPI, you’ll find the API quite intuitive—under the hood, it’s all FastAPI, making the learning curve even smoother.

Conclusion

Dara was created with everyone in mind: from data scientists to developers and decision-makers. We recognized the challenges faced when trying to turn complex data into easy-to-use applications, especially when AI comes into the mix. Dara is our answer to those challenges. It focuses on practicality, aiming to provide a smoother and more intuitive experience in implementing AI to facilitate informed decision-making processes.

With Dara, our goal is to offer a clear, accessible, and adaptable framework. We've shared a glimpse of what Dara can do, and we are eager to delve deeper and share more about its capabilities and how it stands out in our upcoming posts.