Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
196 changes: 124 additions & 72 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,126 +1,157 @@
# **T28 - Project 2: Search for Countries**
# T28 - Project 2: Search for Countries

Repository for team IT2810-H26-T28, Project 2.

## **About the Project**
## About the Project

The application allows users to search, filter, and sort through a catalogue of countries around the world, as well as explore detailed information about each country (e.g. capital, continent, languages, population, area, and flag). Users should also be able to add their own data related to countries (whether they have visited a country and notes), which will be stored persistently on the server.

The project is being developed in stages. The frontend is built with React and TypeScript, and communicates with a GraphQL backend backed by PostgreSQL.

## **Status**
The dataset also includes more than 34,000 cities linked to their respective countries, allowing the application to be tested with a larger dataset.

## Status

The project is under active development. The frontend uses Apollo Client to fetch country data from the GraphQL backend, which retrieves data from PostgreSQL. The backend is hosted on the group's NTNU VM and managed using systemd.

Additional functionality, including user-generated country status and notes, is planned for later development.

## **Implemented so far:**
## Implemented so far:

- Country list using data fetched from the GraphQL backend.
- Country detail page using data fetched from the GraphQL backend.
- Search functionality for countries, with case-insensitive searching performed in the PostgreSQL database.
- Debounced search input (300 ms) to reduce unnecessary GraphQL requests.
- Loading, error, and empty-result states for country searches.
- Apollo Client integration for GraphQL queries and client-side caching.
- GraphQL resolvers for countries, individual countries, cities, and individual cities.
- Apollo Server with resolvers that retrieve data from PostgreSQL.
- Backend support for country search, continent filtering, and pagination.
- Search bar and country list/detail UI components.

Further functionality is planned, including connecting search and filtering controls to the backend, sorting, user-generated country status and notes, and additional accessibility and performance improvements.

## **Planned Functionality**
- PostgreSQL database hosted on the group's NTNU VM, populated with country and city data.
- Backend deployed on the NTNU VM as a systemd service, allowing it to continue running after SSH logout and restart automatically if the process fails.

- **Search**: free-text search by country name, case-insensitive, with debouncing to avoid unnecessary API calls.
## Planned Functionality

- **Result list**: presentation of search results with support for large datasets (pagination or dynamic loading while scrolling).
- **Result list**: Support for large datasets through pagination or dynamic loading. The current frontend displays up to 20 search results.

- **Detailed view**: more information about each country when clicking on a result.
- **Sorting and filtering**: Sorting by name/population and filtering by continent/language and Visited/Want to visit. These operations will be performed on the entire dataset in the backend, rather than only on the data loaded into the client.

- **Sorting and filtering**: sorting by name/population and filtering by continent/language and Visited/Want to visit. These operations will be performed on the entire dataset in the backend, rather than only on the data loaded into the client.

- **User-generated data**: the ability to mark countries as Visited/Want to visit and add notes, stored persistently in the database.
- **User-generated data**: The ability to mark countries as Visited/Want to visit and add notes, stored persistently in the database.

- **Accessibility and sustainable web development**: Semantic HTML, ARIA where necessary, and keyboard navigation. Conscious choices regarding data usage, caching, and performance to reduce unnecessary resource consumption. See our [Accessibility and Sustainability Plan](docs/planAccessibilityAndSustainability.md).

- **Responsive design**: adapted for both mobile and desktop.
- **Responsive design**: Continued improvements for both mobile and desktop.

- See our [design mockups](docs/design.md).

## **Technology**
## Technology

- **Frontend**: React 19 + TypeScript, set up with Vite

- **State management**: GraphQL client: Apollo Client.

- **Backend**: GraphQL (Node.js/TypeScript)
- **Database**: PostgreSQL, installed directly on the group's VM

- **Data**: countries and cities from open datasets (see [Data](#data))

- **Testing**: Vitest (coming)
- **Linting/formatting**: ESLint and Prettier

## **Getting Started**
## Architecture and Scalability

The application follows a client-server architecture consisting of three main components:

- **Frontend**: React and TypeScript with Apollo Client for sending GraphQL queries and caching responses.
- **Backend**: Node.js, TypeScript, and Apollo Server, exposing a GraphQL API and handling requests from the frontend.
- **Database**: PostgreSQL, storing structured country and city data.

The backend and database are hosted on the group's NTNU VM. The backend runs as a systemd service to remain available independently of active SSH sessions.

To support larger datasets, searching, filtering, and pagination are handled by the backend and database rather than requiring the frontend to load all records. The GraphQL API allows the frontend to request only the data needed for each view.

The dataset currently contains approximately 250 countries and 34,155 cities. Including city data provides a larger dataset for evaluating database queries and scalability.

For deployment details, see [Backend Deployment](docs/backendDeployment.md).

## Getting Started

### Prerequisites

- Node.js and npm
- Access to the project's PostgreSQL database
- SSH access to the group's VM if the database is accessed through an SSH tunnel
- Node.js 24+ and npm
- Access to the NTNU network or VPN to connect to the deployed backend

### 1. Configure the Database Connection
To run the frontend using the deployed backend, no direct database access or SSH access to the VM is required.

Follow the instructions in [Database Setup](docs/databaseSetup.md) to configure `backend/.env` and establish an SSH tunnel when necessary.
To run the backend locally, access to the PostgreSQL database and SSH access to the group's VM may be required. See [Database Setup](docs/databaseSetup.md) for instructions.

### 2. Start the Backend
### 1. Start the Frontend

Open a terminal and run:
Open a terminal in the repository root and install the dependencies:

```bash
cd backend
npm install
npm run db:check
```

Connect to the NTNU network or VPN and create a `.env.local` file in the repository root with the following content:

```env
VITE_GRAPHQL_URL=http://it2810-28.idi.ntnu.no:3001/
```

Start the frontend:

```bash
npm run dev
```

Keep this terminal open while developing. The GraphQL server runs at [http://localhost:3001/](http://localhost:3001/).
Open [http://localhost:5173/](http://localhost:5173/) in your browser.

The frontend communicates with the GraphQL backend hosted on the group's NTNU VM. You do not need to start the backend locally.

### 2. Run the Backend Locally (Optional)

### 3. Start the Frontend
If you need to develop or test the backend locally, first follow the instructions in [Database Setup](docs/databaseSetup.md) to configure `backend/.env` and establish an SSH tunnel if necessary.

Open a second terminal in the repository root and run:
Open a terminal in the repository root and run:

```bash
cd backend
npm install
npm run db:check
npm run dev
```

Open [http://localhost:5173/](http://localhost:5173/) in your browser.
The GraphQL server runs at [http://localhost:3001/](http://localhost:3001/).

The frontend uses Apollo Client to query the GraphQL backend. By default, the GraphQL endpoint is `http://localhost:3001/graphql`. When connecting to the backend hosted on the NTNU VM, the endpoint is `http://it2810-28.idi.ntnu.no:3001/`. The endpoint can be overridden with the `VITE_GRAPHQL_URL` environment variable.
To connect the frontend to your local backend instead of the deployed backend, update `.env.local` in the repository root:

Both the backend and the database connection must be available for country data to load.
```env
VITE_GRAPHQL_URL=http://localhost:3001/
```

Keep the backend running in a separate terminal while developing.

### Backend Deployment (NTNU VM)

The GraphQL backend and PostgreSQL database are hosted on the group's NTNU VM (`it2810-28.idi.ntnu.no`).

The backend runs as a systemd service (`t28-backend`), allowing it to continue running after SSH logout and start automatically when the VM boots.

For information about the backend configuration, frontend communication, and starting, restarting, or monitoring the backend service, see [Backend Deployment](docs/backendDeployment.md).
For instructions on configuring, restarting, and monitoring the backend service, see [Backend Deployment](docs/backendDeployment.md).

### **Other Commands**
### Other Commands

| Command | Description |
| ---------------------- | ---------------------------------------------------------------- |
| `npm run build` | Type-check and build the production version |
| `npm run lint` | Run ESLint |
| `npm run format` | Format code with Prettier |
| `npm run format:check` | Check formatting without changing files |
| `npm run preview` | Preview the production build locally |
| `npm run fetch-data` | Fetch and process countries and cities into `data/` |
| `npm run db:check` | Check the database connection (in `backend/`) |
| `npm run db:seed` | Populate the database with country and city data (in `backend/`) |
Unless otherwise specified, run these commands from the repository root.

<!-- > `npm run test` (Vitest) is not set up yet — it will be added and documented here once testing is introduced in the project. -->
| Command | Description |
| ---------------------- | ---------------------------------------------------------------------- |
| `npm run build` | Type-check and build the frontend for production |
| `npm run lint` | Run ESLint |
| `npm run format` | Format code with Prettier |
| `npm run format:check` | Check formatting without changing files |
| `npm run preview` | Preview the production build locally |
| `npm run fetch-data` | Fetch and process country and city data into `data/` |
| `npm run db:check` | Check the database connection (run from `backend/`) |
| `npm run db:seed` | Populate the database with country and city data (run from `backend/`) |

## **Data**
## Data

The dataset is generated by `scripts/fetch-data.ts` and saved to `data/`:

Expand All @@ -133,9 +164,9 @@ To fetch the data again, run:
npm run fetch-data
```

Data was last fetched on **8 October 2026**.
The initial dataset was fetched on 8 October 2026. Country and city data are stored locally in JSON files and imported into PostgreSQL using the database seed script.

### **Sources and licences**
### Sources and licences

| Data | Source | Licence |
| -------------------------------------------------- | -------------------------------------------------------------- | --------------------------------------------------------- |
Expand All @@ -144,19 +175,29 @@ Data was last fetched on **8 October 2026**.
| Cities | [GeoNames](https://www.geonames.org/) (`cities15000`) | [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) |
| Flags | [flagcdn](https://flagcdn.com/) | |

### **Notes on the data**
### Notes on the data

- **Population** uses the most recent year available from the World Bank for each country, so the year can differ between countries.
- **34 countries and territories have no population data** at the World Bank (e.g. Taiwan, Vatican City and several small territories). These are shown as _Unknown_.
- **Kosovo** has different codes in the two sources (`UNK` in mledoze, `XKX` in the World Bank). This is handled in the script.
- Some territories have **no capital** (e.g. Antarctica). These are shown as _None_.
- **Svalbard and Jan Mayen** has no area in the source data. This is shown as _Unknown_.

## **Use of AI**
## Use of AI

We have used AI tools, including Claude Code and ChatGPT, throughout the development of this project, primarily as support for learning, debugging, and solving technical challenges.

(Describe here how and for what purposes AI has been used during development, e.g. for setup, code generation, debugging, or quality assurance of code and documentation.)
Our typical workflow has been to write and implement code ourselves, using AI primarily as a learning tool to explain unfamiliar concepts, identify errors, and suggest improvements rather than generating complete solutions directly.

## **Project Structure**
Examples include working with React and TypeScript, understanding GraphQL queries and resolvers, connecting the frontend to the PostgreSQL database through the backend, and implementing country search with debouncing and appropriate loading and error states.

AI has also been used to help troubleshoot backend deployment on the NTNU VM, configure the systemd service, resolve Git-related issues, and improve technical documentation, including the README.

Using AI to explain concepts and provide feedback on our own code has helped us better understand the technologies used in the project. It has also made debugging and troubleshooting more efficient, particularly when working with unfamiliar tools and configurations.

All AI-generated suggestions have been reviewed and evaluated before being applied, and the group remains responsible for the final implementation.

## Project Structure

```text
backend/
Expand All @@ -171,7 +212,11 @@ data/
├── cities.json
└── countries.json
deploy/
└── t28-backend.service # systemd service configuration
docs/
├── backendDeployment.md # Backend deployment on NTNU VM
├── databaseSetup.md
├── design.md
└── planAccessibilityAndSustainability.md
Expand All @@ -180,38 +225,45 @@ scripts/
└── fetch-data.ts
src/
├── components/ # Reusable UI components
├── graphql/ # GraphQL queries
├── lib/ # Apollo Client configuration
├── pages/ # Page components
├── types/ # TypeScript types
├── components/ # Reusable UI components
├── graphql/ # GraphQL queries
├── hooks/ # Custom React hooks
├── lib/ # Apollo Client configuration
├── pages/ # Page components
├── types/ # TypeScript types
├── App.tsx
└── main.tsx
```

## Choice of database: PostgreSQL

Our data is structured and relational: city → country, and city/country → user status
(visited, want to visit, note). A relational database fits this well, with foreign keys and joins.
We chose PostgreSQL because the project's data is structured and relational. Each city belongs to a country, and the database also supports storing user-generated information such as visited countries, countries users want to visit, and personal notes.

The project requires scalability, with search, filtering, sorting and pagination performed on
the server over the whole dataset. PostgreSQL supports this with indexes, `LIMIT/OFFSET`,
`ILIKE` for case-insensitive search and trigram indexes (`pg_trgm`) for fast text search over
tens of thousands of cities. It is also mature, well documented and has good Node libraries (`pg`).
A relational database is suitable for this structure because it supports foreign keys, relationships between tables, and efficient queries.

Setup: see [database setup](docs/databaseSetup.md).
PostgreSQL also supports the project's scalability requirements. Searching, filtering, sorting, and pagination can be performed in the database rather than requiring the frontend to load the entire dataset.

The backend currently supports case-insensitive country searches using `ILIKE`, as well as filtering and pagination through SQL queries. PostgreSQL also supports indexes and additional optimizations that can improve performance as the dataset grows.

For database configuration instructions, see [Database Setup](docs/databaseSetup.md).

## Seeding the database

To populate the database with countries and cities from the provided JSON files, follow the instructions in [Database setup](docs/databaseSetup.md).
The database is populated using the country and city data stored in `data/countries.json` and `data/cities.json`.

The seed script imports this data into PostgreSQL, making it available to the GraphQL backend.

Run the seed script from the `backend` directory:
Before running the script, configure the database connection by following the instructions in [Database Setup](docs/databaseSetup.md).

Run the following commands from the repository root:

```bash
cd backend
npm run db:seed
```

## **Contributors**
The seed script can be run multiple times without creating duplicate records.

## Contributors

Team IT2810-H26-T28