Reviewed-on: #198
Fuel Wizard
This is the source code mono-repository for the Fuel Wizard front end and server/API applications.
What is Fuel Wizard?
Fuel Wizard (https://fuelwizard.au) is a free, open source web application, which provides simple, useful fuel price analytics to everyone. We currently plan to integrate with all of Australia's state government-provided fuel price data sources. Currently, we integrate with NSW, but more will be added in the future. If you are interested in working on an integration for a particular state, please reach out!
Why do this?
Fuel Wizard was a response to limitations with state-government provided fuel price applications. Whilst these have been a positive step for empowering consumers with open data, these apps have a long way to go in terms of functionality. Some third-parties have created fuel price applications, but we hope to provide an application which is modern, fast, simple to use, and contains intelligent price analytics, which can help our users to find better deals on fuel.
Features
- Interactive map of fuel prices, with user-selectable map styles (rendered from self-hosted OpenStreetMap vector tiles)
- Fuel price history charts and trends
- Station search and listings
- Route directions between locations
Architecture
Front End
The front end application is a Next.js 16, TypeScript web app, which uses the Mantine UI library and utilities to display the application data. react-map-gl with MapLibre GL is used for the map views, rendering self-hosted OpenStreetMap vector tiles served from tile.fuelwizard.au (styles are loaded from the server's styles.json and are user-selectable, with "Basic" as the default — see frontend/utils/mapStyles.ts). Hey API (@hey-api/openapi-ts) automatically generates types, an SDK, and TanStack Query helpers for requests to the API, and TanStack Query manages front end data fetching. OSRM (served at osrm.fuelwizard.au) provides the routing API, with osrm-text-instructions generating turn-by-turn directions.
Back End
The back end/API server is an ASP.NET Core (.NET 10), C# web app, which serves a REST API for consumption by the front end, and by third-parties. OpenAPI is used to automatically create API definitions, with a Scalar web interface for viewing and trying out the spec (served at /api-docs in development). Hangfire is used to schedule recurring jobs within the application code, such as fetching the latest NSW fuel prices every 20 minutes. EF Core (with the Npgsql provider) handles data access.
Database
A TimescaleDB instance (PostgreSQL with the PostGIS extension) is used for persistence, and more complex queries of time-series data. The schema is model-first: EF Core migrations are applied automatically on application startup, and the price table is stored as a TimescaleDB hypertable.
Local Development
Environment
cp .env.sample .env
The back end auto-loads the root .env when running in Development — it carries the (public, sandbox) NSW credentials, the optional Victoria consumer ID, and the Western Australia feed switches. Fill in VIC_FUEL_API_CONSUMER_ID= if you have a key (VIC jobs otherwise log a warning and skip). The front end needs no env setup for local development.
Database
A TimescaleDB instance is required to run a local development instance of the application. This can be set up quickly through Docker, using the compose config found at backend/Database/Deployment/compose.yaml:
docker compose up -d
Run the command from the backend/Database/Deployment directory. The container listens on host port 5432 with the default credentials postgres/password, which match the development configuration, so no further setup is needed. You can reference the Timescale docs for more on running the database with Docker here: https://docs.timescale.com/self-hosted/latest/install/installation-docker/ .
Back End
You will need the .NET SDK 10 installed on your machine: https://dotnet.microsoft.com/download/dotnet/10.0 .
Build and run the API from the repository root:
dotnet run --project backend/
The API is served at http://localhost:5161. From there you can visit:
- http://localhost:5161/api-docs — the Scalar interface for browsing the API spec and trying out requests
- http://localhost:5161/hangfire — the Hangfire dashboard, for manually running scheduled jobs (fetching new prices, seeding reference data, etc.)
Both endpoints are development-only.
Front End
Whilst you should be able to get away with npm or yarn, we strongly recommend using pnpm. We have found this to be the best performing, and most reliable package manager for React projects, and so we tailor these instructions to it.
Install pnpm, as they recommend: https://pnpm.io/installation .
At the root of the front end project (fuel-wizard/frontend), run the install command:
pnpm i
This will download and install all of the required packages, which could take a while, so sit tight until that finishes.
After this has completed, run:
pnpm dev
This starts the Next.js development server, with hot-reload, etc. Navigate to http://localhost:3000 to see the web application.
Other command aliases which will be useful for you are:
| Command | Description |
|---|---|
pnpm build |
Creates a production-optimised build of the Next.js app in the .next directory |
pnpm start |
Starts the production Next.js server |
pnpm lint / pnpm lint:fix |
Runs oxlint on the front end codebase / fixes lint errors |
pnpm format / pnpm format:check |
Formats the codebase with oxfmt / checks formatting |
pnpm test / pnpm test:watch / pnpm test:coverage |
Runs the Vitest test suite (in watch mode / with coverage) |
pnpm typecheck |
Runs the TypeScript compiler to check for type errors |
pnpm generate |
Regenerates the API client in the generated directory (requires the API server running on port 5161) |
Tests
The back end test suite (xUnit) lives in backend/Tests/, and can be run from the repository root:
dotnet test backend/Tests/FuelWizardApi.Tests.csproj
The integration tests spin up a TimescaleDB container via Testcontainers, so they require Docker.
Production Deployment
The stack runs via the root compose.yaml (back end + TimescaleDB + front end on a pinned Docker network):
cp .env.sample .env # fill in real NSW credentials, DB_PASSWORD, domain URLs, VIC consumer ID
docker compose up -d --build
Deploy with stop/start (EF Core migrations run at startup against the new schema). The app refuses to start without the NSW credential variables.
Attribution
Thank you to these fantastic projects, and their respective communities, without which this application would be much worse!
Back End
- .NET (as the basis of our back end): https://github.com/dotnet
- ASP.NET Core (as the basis of our web API): https://github.com/dotnet/aspnetcore
- Hangfire (for background and scheduled tasks): https://www.hangfire.io
- EF Core (as our .NET ORM): https://github.com/dotnet/efcore
- Npgsql (as our PostgreSQL driver): https://www.npgsql.org
- PostgreSQL (as our database): https://www.postgresql.org
- TimescaleDB (as our time-series database): https://github.com/timescale/timescaledb
- Scalar (for our API documentation interface): https://scalar.com
- OpenAPI (for automatic API definitions): https://www.openapis.org
- xUnit (as our back end test framework): https://xunit.net
- Moq (for mocking in our back end tests): https://github.com/devlooped/moq
- Testcontainers (for running Docker containers in our integration tests): https://testcontainers.com
- coverlet (for test coverage): https://github.com/coverlet-coverage/coverlet
- Microsoft.NET.Test.Sdk (as our back end test runner): https://github.com/microsoft/vstest
Front End
- Next.js (as the basis of our SSR front end): https://nextjs.org
- React.js (as our JS framework): https://react.dev
- Mantine (as our UI/component library): https://mantine.dev
- MapLibre GL (as our interactive map rendering engine): https://maplibre.org
- react-map-gl (as our React wrapper for MapLibre GL): https://visgl.github.io/react-map-gl
- TanStack Query (as our front end query management library): https://tanstack.com/query/latest
- Hey API (for automatic generation of front end API interfaces): https://heyapi.dev
- Recharts (for charts in Mantine): https://recharts.org
- React Icons (for our icons!): https://react-icons.github.io/react-icons
- Sharp (for image processing): https://sharp.pixelplumbing.com
- osrm-text-instructions (for turn-by-turn directions): https://github.com/Project-OSRM/osrm-text-instructions
- oxlint (for code linting): https://oxc.rs
- oxfmt (for code formatting): https://oxc.rs/docs/guide/usage/formatter
- Vitest (as our front end test framework): https://vitest.dev
- Testing Library (for front end testing utilities): https://testing-library.com
- jsdom (for simulating a browser in our front end tests): https://github.com/jsdom/jsdom
- PostCSS (for CSS transformation in Mantine): https://postcss.org
- postcss-preset-mantine & postcss-simple-vars (for Mantine's PostCSS setup): https://github.com/mantinedev/postcss-preset-mantine
- TypeScript (as our front end language): https://www.typescriptlang.org
- Node.js (as our JS runtime): https://nodejs.org
- pnpm (as our front end package manager): https://pnpm.io
- DefinitelyTyped (for their fantastic TypeScript type definitions): https://github.com/DefinitelyTyped/DefinitelyTyped
Data & Services
- OpenStreetMap (as the source of our map data): https://www.openstreetmap.org/copyright
- NSW Fuel Check (as the source of our fuel price data): https://www.fuelcheck.nsw.gov.au
- OSRM (as the routing API): https://project-osrm.org
- Planetiler (for building our vector tile maps): https://github.com/onthegomap/planetiler
- TileServer GL (for serving our vector tiles): https://github.com/maptiler/tileserver-gl
Roadmap (in rough order of priority)
- Email notifications (WIP)
- Generalise pagination across back end code (pass in simple Pagination objects to methods, rather than the large signature we have currently)
- SA, QLD fuel price data (tough to get access to)
- Trend predictions (we have historical trends; predictions need more data to work with before we start)
- Trip logging and cost vs. distance tracking (routing is done; saving and analysing trips is not)
- Request validation via FluentValidation (currently a single hand-rolled page-size validator)
- Sorting and filtering on the paginated API endpoints (client-side sorting only applies to the loaded page)
- And more...
License
This program is free software: you can redistribute it and/or modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program. If not, see https://www.gnu.org/licenses/.