Bookmyflight : A web app that helps you find cheap flights and book them based on your budget.
Tags: javascript, react, node, express
Published: 2024-05-23 20:59:47

Currently working on a full fledged Flight booking website using Amadeus API. 
Progress so far :-
Done with user register , login and logout part. The backend is written in nodejs and express. I have used mongodb for the database. 
All the toast messages you see are coming from the server. I have also used JWT , generated access token and refresh token using it, and saved it in user browser cookie. But I don't know how to use this access token or refresh token , like now after login I don't have to call backend again , so how i will get to know when the access token expires??
Will figure it out , after moving deep in the project ( first time using jwt). 
On logout , user cookies get deleted.
I have also added Flight search component , I don't know this full white bg looks good or not , will soon improve the bg. First tried gray , but it looked ugly.
I have never used such a large and complicated API before. Every request requires headers and parameters, and I have to generate a new access token whenever the old one expires. It's been a real challenge!

I want to share some of the problems I faced and how I solved them:

1. The API doesn’t accept city names directly, instead it takes the city airport code, known as the IATA code. For example, for Delhi, it is DEL. So, when a user types a city name like Ranchi, behind the scenes I had to convert it to the IATA code, IXR. To achieve this, I used another Amadeus API endpoint that fetches this data and stores it in a state variable, which is then passed in the params.

```
  const handleFromInputSearch = async (value)=>{
    setFrom(value)
    const headers = {'Authorization' :`Bearer ${accessToken}`}
    const params = {'subType':'CITY,AIRPORT' , 'keyword':`${value}`}
    try {
      const airportData = await axios.get('https://test.api.amadeus.com/v1/reference-data/locations', {headers , params})
      setAirportData(airportData?.data?.data)
      setDepartureAirport(airportData?.data?.data[0]?.iataCode)
      console.log(airportData?.data?.data[0]?.iataCode)
    } catch (error) {
      console.log('Error fetching airport data' , error)
    } 
  }
```

2. The date format required by the API is year-month-day, but the component from which the user selects the date is in month-date-year format. Therefore, I reformatted the date to match the API expectations.

```
  const formatDate = (date) => {
    const year = date?.year
    const month = date?.month < 10 ? `0${date?.month}` : date?.month
    const day = date?.day < 10 ? `0${date?.day}` : date?.day
  
    return `${year}-${month}-${day}`
  }
```


3. When flight data is fetched from the API, the format of the total travel duration is different, like PT20H55M. This means that the total travel duration is 20 hours and 55 minutes. As you can see, this is not a human readable format. So, I wrote a function to convert this data into a human readable format. I took help from Stackoverflow and Chatgpt because this was a problem I faced for the first time.

```
  const formatTotalTravelDuration = (duration) => {
    // Match the duration string against the regular expression pattern
    const match = duration.match(/PT(\d+H)?(\d+M)?/)
  
    // Extract hours and minutes from the matched groups
    const hours = match[1] ? match[1].replace('H', '') : '0'
    const minutes = match[2] ? match[2].replace('M', '') : '0'
  
    // Construct the human-readable format by combining hours and minutes
    return `${hours}h ${minutes}m`.trim()
  }
```

4. There is a similar problem with the flight timing, which is in this format: 2024-05-30T22:15:00. A bit uncomfortable to read, right? I wrote a function to transform this into a human-readable format.

```
  const formatTiming = (dateTime) => {
    // Extract the time part from the datetime string
    const timePart = dateTime.split('T')[1]
  
    // Match the time part against the regular expression pattern
    const match = timePart.match(/(\d+):(\d+):(\d+)/)
  
    // Extract hours and minutes from the matched groups
    const hours = match && match[1] ? match[1] : '0'
    const minutes = match && match[2] ? match[2] : '0'
  
    // Construct the human-readable format by combining hours and minutes
    return `${hours}:${minutes}`.trim()
  }
```

5. The airline name you see on the card component is not the way it was sent from the API. I received an airline code from the API, like for Air India, which is AI. So, how will the user know which airline it is? To resolve this issue, I used another Amadeus API endpoint that provides the airline name according to the airline code sent in the parameter.

```
  useEffect(() => {
    const fetchData = async () => {
      try {
        const headers = { 'Authorization': `Bearer ${accessToken}` };
        const params = { 'airlineCodes': `${airLine}` };
        const response = await axios.get('https://test.api.amadeus.com/v1/reference-data/airlines?airlineCodes', { params, headers });
        setAirlineName(response?.data?.data[0]?.businessName);
      } catch (error) {
        console.log("error fetching airlines name ", error);
      }
    };

    fetchData();
  }, [airLine]);

```

So many more features to add, optimization is needed, improvement in UI, and many more things. My main goal in choosing this project was to get good hands on experience with a large and complex API, and luckily, there is not a single tutorial, YouTube video, or blog for this API. So, everything you see I have done by reading docs and using the hit and trial method. You wouldn't believe how many many many times I tried to generate an access token perfectly.

Another challenging thing I can think of now is the payment for booking, like how the payment will be executed and confirmed.

Github repo : https://github.com/nitintwt/bookmyflight

Let's see how it goes.