I recently built an application that is able to have a user input a flight number and return information about scheduled takeoff and landing times, as well as location, airport name, and the flight status.
Backend
The server is where all API calls are to be made. A server is not needed necessarily for this purpose, but allows for a database to be added at a later time with minimal conversion.
This will be a Node server using the Express.js framework to make things a little easier. If you have never used Express before, I made a bit of a starter guide to help out.
This app involves an API to source the flight information, you may choose a variety of different APIs which do similar things, but for this project I'll be using Flight Aware's API which requires a personal key.
Start of with creating a .env
file in the root directory to set your API key.
Once you have your key stored, go over the documentation as there is quite a lot of information to get through to understand what data you should target.
Routes and Actions
The controller action will be named getFlightInfo
, you haven't done so already, use npm install dotenv axios express
to have the necessary packages installed.
flightsController.js
const axios = require("axios");
require("dotenv").config();
const getFlightInfo = (req, res) => {
const ident = req.params.ident.toUpperCase();
axios.get("https://aeroapi.flightaware.com/aeroapi/flights/" + ident, {
headers: {
Accept: "application/json; charset=UTF-8",
"x-apikey": process.env.aeroapiKey,
},
})
.then((resp) => {
res.status(200).send(resp.data["flights"]);
})
.catch(() => {
res.status(400).send({ message: "The flight nubmer is invalid" });
});
};
module.exports = {
getFlightInfo,
};
flightsRoutes.js
const express = require("express")
const router = express.Router()
const { getFlightInfo } = require("../controllers/flightsController")
router.get("/:ident", getFlightInfo)
module.exports = router
Endpoint "/flights/{:ident}"
This action will be able to find out info for a given flight number. The example that's used on the documentation site to test usage for the many endpoints is UAL4
which is the operator code
followed by the flight number
. In this case the ident
being "UAL4" or "UA4", will give data about the flight from London, England to Houston, Texas.
NOTE:
This information will need to be parsed through as this flight flies once a day, EACH day (which is something I learned about flight numbers), in order to find the next or en route flight.
ASIDE
Flight numbers are static to the flight origin and destination, and with what airline is flying the route.
With this all up and configured, test it with a REST client to verify information is coming back successfully.
Frontend
Create a React directory or use the wonderful npx create-react-app <frontend-directory-name>
to get React all ready to go. After the setup completes use npm install axios react-redux @reduxjs/toolkit
to have axios, redux, and reduxjs/toolkit in the package files.
Call the API
Before making a call to the API, the next flight needs to be determined beforehand so that we have relevant data. I've created a features directory which holds a generic functions.js
file, but you may want to place this in a helper directory of choice.
functions.js
const now = new Date();
export const convertISOTimeTDateTime = (time) => {
return new Date(time);
};
export const findNextFlight = (flightsArray) => {
let currentFlight;
for (let i = 0; i < flightsArray.length; i++) {
let flightTime = convertISOTimeToDateTime(flightsArray[i].scheduled_off);
let nextFlight = flightsArray[i + 1];
if (flightTime > now && nextFlight.status !== "Scheduled") {
if (nextFlight.status.includes("En Route")) {
currentFlight = nextFlight;
return currentFlight;
}
currentFlight = flightsArray[i];
} else {
}
}
return currentFlight;
};
Every
date
object returned from the API will be in ISO 8601 format, and for human readability, a conversion will be needed.
flightsService.js
export const getFlightInfo = async (flightNumber) => {
const response = await axios.get(`/api/flights/${flightNumber}`)
return response.data
}
This will call the backend to fetch current flights based on the flight number used.
We'll create a slice function to use this call and load the redux store with all the flights data.
flightsSlice.js
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import flightsService from "./flightsService";
const initialState = {
message: "",
flights: [],
};
export const getFlights = createAsyncThunk(
"flights/:ident",
async (flightNumber, thunkAPI) => {
try {
return await flightsService.getFlightInfo(flightNumber);
} catch (error) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
return thunkAPI.rejectWithValue(message);
}
}
);
export const flightsSlice = createSlice ({
name: "flight",
initialState,
reducers: { reset: () => initialState},
extraReducers: (builder) => {
builder
.addCase(getFlights.fulfilled, (state, action) => {
state.flights = action.payload;
})
.addCase(getFlights.rejected, (state, action) => {
state.message = action.payload;
});
},
});
export default flightsSlice.reducer;
Display flight info
With all functions set and data ready create a home page or some place for the container.
Flights.jsx
import React, { useState } from "react";
import { getFlights } from "../features/flights/flightsSlice.js";
import { useDispatch, useSelector } from "react-redux";
import * as functions from "../features/functions";
import FlightItem from "../components/FlightItem";
function Flights() {
const dispatch = useDispatch();
const { flights, isLoading } = useSelector((state) => state.flightsState);
const [flightNumber, setFlightNumber] = useState("");
const listFlights = () => {
const nextFlight = functions.findNextFlight(flights);
return <FlightItem nextFlight={nextFlight} flightNumber={flightNumber} />;
};
const onChange = (e) => {
setFlightNumber(e.target.value);
};
const onSubmit = (e) => {
e.preventDefault();
dispatch(getFlights(flightNumber));
};
return (
<section id="flights-page">
<form id="flight-search" onSubmit={onSubmit}>
<input
type="text"
name="flightNumber"
autoFocus
value={flightNumber}
onChange={(e) => onChange(e)}
placeholder="Enter flight number"
/>
<input type="submit" value="Find Flight" />
</form>
<div className="info-text">
{flights.length <= 0 ? <h2>Find a Flight</h2> : null}
</div>
{flights.length > 0 ? listFlights() : null}
</section>
);
}
export default Flights;
This, with the algo to find the next flight, will present the FlightItem
component.
FlightItem.jsx
import React from "react";
export default function FlightItem({ nextFlight, flightNumber }) {
const convertDate = (date) => {
if (date) {
const toDateString = new Date(date);
const newDate = toDateString.toUTCString().split("GMT")[0];
return newDate;
}
};
const displayFlightNumber = (flightNumber) => {
return flightNumber.toUpperCase();
};
const placeDate = () => {
let date = new Date();
return convertDate(date);
};
return (
<>
<div className="grid-container flight-item-grid">
<div className="flight-heading">
<h2>
Flight
<br />
{displayFlightNumber(flightNumber)}
</h2>
<h3>{nextFlight.status}</h3>
<h4>{placeDate()} UTC</h4>
</div>
<div className="flight-grid-item">
<h3 className="departure-heading">Departure</h3>
<h3 className="arrival-heading">Arrival</h3>
<table className="departure-table">
<tbody>
<tr>
<td>Scheduled Takeoff</td>
<td>{convertDate(nextFlight.scheduled_off)}</td>
</tr>
<tr>
<td>Actual Takeoff</td>
<td>{convertDate(nextFlight.actual_off)}</td>
</tr>
<tr>
<td>Airport</td>
<td>{nextFlight.origin.code_iata}</td>
</tr>
<tr>
<td>Terminal</td>
<td>{nextFlight.terminal_origin}</td>
</tr>
<tr>
<td>Gate</td>
<td>{nextFlight.gate_origin}</td>
</tr>
</tbody>
</table>
<table className="arrival-table">
<tbody>
{nextFlight.scheduled_on == nextFlight.estimated_on ? (
<tr>
<td>Scheduled Arrival</td>
<td>{convertDate(nextFlight.scheduled_on)}</td>
</tr>
) : (
<tr>
<td>Estimated Arrival</td>
<td>{convertDate(nextFlight.estimated_on)}</td>
</tr>
)}
<tr>
<td>Actual Arrival</td>
<td>{convertDate(nextFlight.actual_on)}</td>
</tr>
<tr>
<td>Airport</td>
<td>{nextFlight.destination.code_iata}</td>
</tr>
<tr>
<td>Terminal</td>
<td>{nextFlight.terminal_destination}</td>
</tr>
<tr>
<td>Gate</td>
<td>{nextFlight.gate_destination}</td>
</tr>
</tbody>
</table>
</div>
</div>
</>
);
}
There is a little bit a refactoring needed for the table of information displayed, but now there will be scheduled times of departure, scheduled (or estimated if the flight is delayed) time of arrival, and the airport information for anyone looking to find what terminal your ride would need to meet you (or any other reverse situation).
Feel free to leave comments or questions in case I have missed something along the way!
Happy flight tracking!
Top comments (1)
Tracking flights with Express and React can be a seamless and efficient way to stay updated on your travel plans. With the power of these technologies, you can create dynamic and responsive flight tracking applications that cater to various user needs. One excellent resource for travelers is AirAdvisor, a platform that provides valuable information on airline refund and compensation policies. For instance, if you're flying with Air France and encounter any issues that warrant compensation or a refund, AirAdvisor can be a valuable ally in navigating the process. They offer insights into your rights as a passenger and help streamline the often complex procedure of claiming compensation.