Skip to content
All projects

Case study · 01

Status: Ongoing

Fishing Forecast

A fishing planner for Koszalin and the surrounding area, running on my own server. For a chosen spot and species it scores conditions from 0 to 100, explains every factor, finds the best time windows of the day and gives a 7-day outlook. Sea spots get an extra mode with waves and a safety label.

Fishing Forecast dashboard with the score, factor breakdown and 3-day trend

01 · Problem

What needed solving

Weather apps show numbers, not an answer. The real question is simpler: is it worth going today, when exactly, and why. And the answer has to be the same on the phone and on the laptop.

02 · Solution

How it works

A Fastify API with PostgreSQL stores accounts, spots and preferences, and is the only part that talks to Open-Meteo, caching each spot's forecast for 15 minutes. A React PWA shows the result of a deterministic, documented algorithm: eight weighted factors, species rules capped at ±12 points, and time windows cut to at most four hours.

03 · Key features

What it does

  • Accounts with bcrypt-hashed passwords and httpOnly session cookies; tokens stored as HMACs
  • Spots and settings saved per account and synced across devices
  • Score from 8 weighted factors, plus waves for sea spots when marine data exists
  • Best time windows for today and a 7-day outlook with expandable details
  • Installable PWA with an offline UI cache and a read-only mode from a local copy

04 · Tech stack

Built with

  • TypeScript
  • Fastify
  • PostgreSQL
  • React
  • Vitest
  • Docker
Status
Ongoing
Code
github.com/KacperBlok1/fishing-forecast-koszalin
Demo
Not hosted yet · run locally

05 · Challenges

What was tricky

  1. When Open-Meteo is down

    Forecasts are cached in PostgreSQL: fresh for 15 minutes, kept as a fallback for 24 hours. If Open-Meteo is down, the server returns the last good response and the app shows how old it is. Missing marine data is reported, never invented.

  2. Time zones

    Open-Meteo returns times in the spot's local time without a zone. The app works on those strings instead of parsing them with the browser clock, so dawn lands on the right hour even when the phone is set to another time zone.

  3. Scoring a whole day

    Almost every day has one good hour at dawn, so the best hour alone made all days look the same. The daily score blends the best window (65%) with the daytime average (35%).

06 · Screenshots

How it looks

Fishing Forecast dashboard: a 95/100 score for pike, factor breakdown and 3-day trend
The “Now” tab: score, factor breakdown and 3-day trend (UI in Polish)Open full-size image ↗

Try it

Run it locally

No hosted demo yet. The source and README are on GitHub, and these commands start it locally from the repo root.

terminal
$ cp .env.example .env
# set POSTGRES_PASSWORD and SESSION_SECRET
$ docker compose up -d --build
$ curl http://localhost:8090/api/health