Full-Stack

WhereTo - Climate Travel App

Completion Date: March 2022

WhereTo - Climate Travel App
WhereTo Home Page

WhereTo is a travel app that suggests locations with weather similar to your input destination. This was the final project for the Northcoders bootcamp, and was built as a team using agile methodology. It uses a non-relational database to store climate and city data, and external APIs to fetch coordinates, photos and weather data.


Technology

  • Svelte
  • Svelte Kit
  • MongoDB
  • TypeScript
  • Hosted with Vercel

I wanted to use technology that was highly rated by developers, so we built it with Svelte, SvelteKit, MongoDB, and wrote the application using TypeScript. It was a steep learning curve to take on all this new technology and learn it in a short timeframe, but while undertaking the project we came to love the tech, and really enjoyed using Svelte.


A few years ago I saw a fantastic map on Reddit that showed locations that had similar climates. At the time I was working selling wine, and found it so interesting that places like the Hunter Valley near Sydney, Australia had the same climate as Buenos Aires, Argentina, a region on the other side of the world. For bootcamp project pitches I immediately thought of this and knew this was a great idea that we could create.

As this was a group project, we divided the work using a Trello board and had daily stand ups to discuss our progress and deal with blocking issues.

My main contributions were turning the user input into co-ordinates to query the database, fetching information for each city, and creating the location page to display further information for each place.

Since completing the course I have continued to work on the app, re-introducing the seeding feature for people to seed their own database when running locally, and fixing bugs to allow for hosting with Vercel.

Wireframe plan for project
Initial home page wireframe during the project planning phase

Key Features

  • Utilizes non-relational database and external APIs
  • Type checked with TypeScript
  • Uses MongoDB Geospatial Queries to handle coordinate information

components/search.svelte
// User input string is used to query the Google Autocomplete API to get a place id
const placeIdFetch = async (location: string) => {
	const res = await fetch(`/api/destination/${location}`);
	if (res.status === 200) {
		const resObject = await res.json();
// Place predictions are shown to the user in the autocomplete drop down list
		predictions = resObject.predictions;
		placeId = resObject.place_id;
	} else {
		predictions = TEMPPREDICTION;
		placeId = 'TEMPPLACEID';
		}
};

// Fetch call to Google Geocoding API get lat/long co-ordinates from the place id
const coordinateFetch = async (place_id: string): Promise<Coordinates> => {
	const res = await fetch(`/api/latlng/${place_id}`);
	const resObject = await res.json();
// PlaceName and country appended to URL for use on the searchresults page
	placeName = [resObject.placeName, resObject.country];
	return resObject.coordinates;
};

When the user enters their location, the string is passed into Google Autocomplete API, which returns a place ID. This place ID is used to query the Google Places API to get co-ordinates for the user's input destination.

The co-ordinates are then used to query the MongoDB database to return the array of cities with a similar climate.

searchresults/index.ts
// Query the database with the co-ordinates to get an array of similar cities
try {
	cities = await getCitiesSimilarToLocation([lng, lat], { limit: 9 });
} catch (error) {
	console.log(error);
}

// For each city, fetch the weather and a photo
const combinedData = await Promise.all(
	cities.map(async (city) => {
		const cityWeather = await getWeather(city.city_ascii);
		const { photo_reference } = await getPhotoRef(city.city_ascii);
		const src = `https://maps.googleapis.com/maps/api/place/photo?maxwidth=400&photoreference=${photo_reference}&key=${process.env['VITE_API_KEY']}`;

		return { weather: cityWeather, details: city, src } as City;
	})
);

Inside the Search Results page endpoint, current weather and photos are fetched for each city. These cards are iterated and rendered onto the page.

City Card
City card with a photo, temperature, and humidity fetched from external APIs

Problems I faced

Using a meta-framework like SvelteKit offered a lot of new possibilities compared to Create React App projects like I had previously made. I found Svelte to be great and intuitive to use, but due to the lack of documentation, and my first time using a meta-framework, I had to re-write some features as I figured out better ways to approach the problem. Originally, I had made fetch requests in the component file. Then, I found out about API endpoints, and moved the fetch calls to there. Finally, I learnt about page endpoints and this was the best solution for fetching data. This was a great learning experience and helped me understand how to structure a project like this in the future.

On the home page there were a few steps required to convert the user's search location into coordinates.

I had to first use the Google Autocomplete API to turn the user's input, generally a city name, into a full address. This query returned a Google place id, which was then subsequently used to call the Google Places API, returning the coordinates needed to query the MongoDB database, giving us the cities with a similar climate. Google Maps has two APIs for different purposes – one for client side, and one for server side. I spent an evening getting one to work, only for me to realize I had implemented the wrong one and needed the server side API. This really helped me to learn to read the documentation and understand what I need before I start writing code.

We used TypeScript on this project because it is highly rated by so many developers. TypeScript can be overwhelming at first, causing a lot of errors (but importantly, before production!). To be able to use it properly I had to spend some extra time in completing the Codecademy TypeScript course to understand how to utilize it's features correctly, especially interfaces and object typing. This was a massive help to me and I will definitely use TypeScript moving forward.

When stitching the final product together, we had some problems linking all the pages together to create a smooth user journey. This was our first project as a group, so merging the branches together had a learning curve to overcome. It definitely improved my Git skills.


What I learnt

  • The usefulness of Meta Frameworks

In future projects I will take advantage of features like page endpoints to render content server-side before page load. Svelte Kit's file-based routing makes it so easy to create routes.

  • Improved skill working with APIs

I spent a lot of time working with external APIs like Google Places. This has increased my confidence using these tools in future projects.

  • How to use TypeScript

I learnt how to use TypeScript, and how useful it is. This will serve me well in future projects now I have overcome the initial learning curve.

  • Concise syntax of Svelte

I enjoyed using Svelte, particularly how simple it is to manage state.

components/search.svelte
// Svelte concise syntax for form submission event handler
<form on:submit|preventDefault={onConfirmSubmit}>
...
</form>

For example, handling a form submission can be done in such a little amount of code, whereas in React this is a more verbose process.

  • Improved Git skills

Working in a team on different branches and working with pull requests massively improved my skills with Git. This will be extremely helpful in future projects and working on development teams.


What I would improve

With more time on this project I would create additional features such as flight information, distance to destination, currency exchange rates. The 'My Places' and 'My Searches' pages are both placeholders so I would finish their functionality.

Instead of fetching data in multiple places, I would use Svelte Stores to fetch the data once and allow access to it from different places in the app.

There was a geolocation feature which we didn't have time to fully implement, I would like to finish this functionality by using the co-ordinates to autofill the user location through reverse geocoding.

Ben Weston

Built with Next.js, Tailwind and MDX