{"id":21721172,"url":"https://github.com/danielearwicker/flowerbi","last_synced_at":"2025-10-28T18:20:56.597Z","repository":{"id":40283324,"uuid":"277307518","full_name":"danielearwicker/flowerbi","owner":"danielearwicker","description":"Ultra-minimal BI analytics query and UI tools","archived":false,"fork":false,"pushed_at":"2025-03-16T21:24:28.000Z","size":139038,"stargazers_count":9,"open_issues_count":2,"forks_count":3,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-26T15:47:59.246Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/danielearwicker.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-07-05T13:28:18.000Z","updated_at":"2025-03-02T15:24:55.000Z","dependencies_parsed_at":"2024-08-13T19:39:51.631Z","dependency_job_id":null,"html_url":"https://github.com/danielearwicker/flowerbi","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielearwicker%2Fflowerbi","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielearwicker%2Fflowerbi/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielearwicker%2Fflowerbi/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielearwicker%2Fflowerbi/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/danielearwicker","download_url":"https://codeload.github.com/danielearwicker/flowerbi/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248636268,"owners_count":21137411,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-26T02:14:27.413Z","updated_at":"2025-10-28T18:20:51.577Z","avatar_url":"https://github.com/danielearwicker.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# FlowerBI\n\n\u003cimg align=\"left\" width=\"128\" height=\"128\" src=\"logo128.png\"\u003e\n\nFlowerBI supports querying relational databases through a single POST route, so that clients have enough power to do aggregation and joins, but the server/API can can carefully limit what clients are able to do.\n\nIt focuses on supporting very succinct query definitions at the client, and strong-typing via TypeScript inference.\n\nThe [Playground](https://earwicker.com/flowerbi/demo/) lets you interactively build a query, see what the JS/TS code for that query would look like, and also the generated SQL, and the tabulated results.\n\n![Playground animated GIF](playground.gif)\n\n\u003csub\u003eLogo designed by [@shep1987](https://github.com/shep1987)\u003c/sub\u003e\n\n## Use Case\n\nOur users' data is:\n\n-   in SQL Server databases,\n-   arranged in a [star schema](https://en.wikipedia.org/wiki/Star_schema),\n-   served up by our own API (dotnet core/C#),\n-   subject to fine-grained row-level authorisation (that is, only some users are allowed to see some rows).\n\nOur UI is:\n\n-   written in TypeScript/React,\n-   required to show aggregated statistics in nice-looking charts,\n-   driven by rapid evolution and enhancement from user feedback.\n\nWe've tried using a [paid no-code BI product](./PowerBI.md), and while it had some severe drawbacks, we liked the way it worked with data:\n\n-   In one central place, you describe the schema: a _fact_ table that has several foreign keys to _dimension_ tables, all many-to-one relationships.\n-   For each chart you want to render, you just specify columns to group by (from any table) and columns to aggregate over (count, sum, etc.)\n-   Something builds the SQL query for you - this part isn't really that hard, but it's very productive to have it automated.\n\nThere are a lot of free libraries for drawing charts: [chart.js](https://www.chartjs.org/) is really easy to use, and it has [a good React wrapper](https://github.com/jerairrest/react-chartjs-2).\n\nBut it would be a drag to have to write a separate API route that runs a different specific SQL query to get the data for each chart. FlowerBI takes away that problem, and there's honestly not a lot to it.\n\n## Flexible and creative querying at the client\n\nDefine the shape of the data you need in your client code:\n\n```ts\nconst { records } = useQuery(fetch, {\n    select: {\n        customer: Customer.CustomerName,\n        bugCount: Bug.Id.count(),\n    },\n    filters: [Workflow.Resolved.equalTo(true)],\n});\n```\n\nThe `useQuery` function is a handy React hook, defined in the `flowerbi-react` npm package, but the core client code in the `flower-bi` package has no dependency on React, so it's not a prerequisite.\n\nYou supply the `fetch` function to call your API, with your choice of authentication. Inside your API you pass a chunk of JSON to `FlowerBI.Engine` and it performs the SQL query.\n\n## Easy mapping to widely-used visualisation libraries\n\nRender the returned records easily in React, maybe using [chart.js](https://github.com/jerairrest/react-chartjs-2):\n\n```tsx\n\u003cPie\n    data={{\n        labels: records.map((x) =\u003e x.customer),\n        datasets: [\n            {\n                label: \"Bugs\",\n                data: result.records.map((x) =\u003e x.bugCount),\n            },\n        ],\n    }}\n/\u003e\n```\n\nThe record fields `customer` and `bugCount` are strongly typed, inferred from the `select` in the query.\n\n## Lock down the schema\n\nObviously it's not safe to allow clients to send raw SQL to an API and get it executed, so that's not happening here. The query refers to tables/columns (such as `Customer.CustomerName`) that are defined inside the API, declared in Yaml like this:\n\n```yaml\nCustomer:\n    id:\n        Id: [int]\n    columns:\n        CustomerName: [string]\n\nBug:\n    id:\n        Id: [int]\n    columns:\n        CustomerId: [Customer]\n        ReportedDate: [DateReported]\n```\n\n`FlowerBI.Tools` automatically generates TypeScript and C# files that the client can use to get auto-completion and type inference in its queries. So the client code can query the data in a creative and flexible way, but only within the boundaries set by your API's schema definition. Your API can also easily add extra filters to the query, to impose \"row-level security\" on a per-user basis.\n\n## Automatic joins, grouping and aggregation\n\nOur example query casually takes from two different tables:\n\n```ts\nselect: {\n    customer: Customer.CustomerName,\n    bugCount: Bug.Id.count()\n}\n```\n\nThe schema tells us that `Bug` has a foreign key `CustomerId` to the `Customer` table, so we're going to need a `join`. Also the `bugCount` column uses an aggregation function, `count`, which means that we're going to `group by` the customer name to get the number of bugs reported per customer.\n\nYou can specify any number of plain columns (strings, numbers, dates, booleans) to group by, and any number of numeric columns with an aggregation function (`count`, `sum`, `average`).\n\nYou can also optionally supply different filters to each aggregation, so e.g. get the count of all bugs, and the count of resolved bugs, per customer.\n\n```ts\nselect: {\n    customer: Customer.CustomerName,\n    countAllBugs: Bug.Id.count(),\n    countResolvedBugs: Bug.Id.count([Workflow.Resolved.equalTo(true)])\n}\n```\n\nThe result has records like this:\n\n```ts\n{\n    customer: string,\n    countAllBugs: number,\n    countResolvedBugs: number\n}\n```\n\nPerfect for mapping to a multi-bar chart.\n\n## Live Demo\n\nhttps://earwicker.com/flowerbi/demo/\n\nThis runs the whole stack in-browser, using some WASM-based components. This is not part of the real solution; no WASM is needed. It's just a way to run a live demo without having to pay to run real boxes!\n\n-   [sql.js](https://github.com/sql-js/sql.js) representing the RDBMS\n-   The dotnet core engine built in [Blazor](https://dotnet.microsoft.com/apps/aspnet/web-apps/blazor), representing an application server\n-   The UI \"fetches\" data from the Blazor app, which uses `FlowerBI.Engine` to generate SQL queries and runs them against sql.js. It does some ugly hackery to make the queries compatible, as they are currently generated to target Microsoft SQL Server which has a different syntax for many basic things.\n\n## Reference Documentation\n\nGradually appearing:\n\n-   [Yaml Schemas](./docs/markdown/yaml.md)\n-   [Virtual Tables](./docs/markdown/virtual-tables.md)\n-   [Many-to-Many](./docs/markdown/many-to-many.md)\n-   [Conjoint Tables](./docs/markdown/conjoint.md)\n-   UI packages:\n    -   [flowerbi](https://earwicker.com/flowerbi/typedoc/flowerbi)\n    -   [flowerbi-react](https://earwicker.com/flowerbi/typedoc/flowerbi-react)\n    -   [flowerbi-react-chartjs](https://earwicker.com/flowerbi/typedoc/flowerbi-react-chartjs)\n    -   [flowerbi-react-utils](https://earwicker.com/flowerbi/typedoc/flowerbi-react-utils)\n\n## Contributors\n\n-   Maggie Duggan (simpler query generation for version 5)\n-   [@jmulcahy-fiscaltec](https://github.com/jmulcahy-fiscaltec)\n-   [@TWilkin](https://github.com/TWilkin)\n-   [@shep1987](https://github.com/shep1987)\n\n## License (MIT)\n\n_Copyright © 2020-2022 Daniel Earwicker_\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanielearwicker%2Fflowerbi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdanielearwicker%2Fflowerbi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanielearwicker%2Fflowerbi/lists"}