Engineering case study
GlobalVisaMap
From visa records to an interactive map: server-side filtering, typed results, and a browser-based geographic interface.

The project
GlobalVisaMap is a personal software project for exploring visa options by country and personal circumstances. It connects a questionnaire and a structured visa catalogue to a map, so results can be explored geographically as well as in a list.
These notes describe the implementation in the project source. They are not a claim about current immigration rules, production uptime, or measured user outcomes.
Server-side matching
A Next.js server action accepts questionnaire data and calls a matching module backed by MongoDB and Mongoose. Database access stays on the server; the client receives serializable result objects rather than database documents.
The matching module applies structured filters and assigns scores before sorting the results. It also maps differently named visa types into shared categories. This is rule-based matching, not a machine-learning prediction or a guarantee of eligibility.
A shared result model
The TypeScript VisaOption shape carries a country, name, description, requirements, category, and optional match score. Keeping those fields together lets the results list and map work from the same matched records.
Category normalization is a practical part of that boundary. Labels such as Work, Work Visa, and Work / Employment need a consistent representation before filtering and display can agree.
Mapping the results
The map uses Leaflet through React Leaflet, with GeoJSON country boundaries. Matched records are grouped by country, and a set of country names determines which boundaries are highlighted. Country popups show the associated visa names and categories.
The map is dynamically loaded with server-side rendering disabled because Leaflet depends on browser APIs. A client-only wrapper keeps that dependency separate from the server-side matching workflow.
Tradeoffs and remaining work
Rule-based filtering makes the matching logic inspectable, but its usefulness depends on the underlying records being complete and current. A score is a sorting aid, not a probability of approval.
The current map joins records to GeoJSON using normalized country names. Stable country identifiers would make that boundary less sensitive to naming differences.
The server action returns an empty array when a database connection or matching operation fails. That keeps the interface from crashing, but it can make a service failure look like a genuine no-match result. A distinct error state would make the outcome clearer.
Freshness checks, official-source references, and explicit failure states matter more here than presenting a longer list of matches. Visa decisions still require checking the relevant government authority.