CityGo allows users to find vacation routes for any city. It is integrated with Google Maps to allow users to navigate the trip on their device.
- The backend uses Node.js and PostgreSQL for storing the trips
- The pg-promise library is used for database connectivity
- To view the frontend of this application, see this repo.
- APIs used:
- Google Places: to retrieve place data using search strings. Includes info such as address, rating, and purpose.
- Unsplashed: for retrieving images of places
-
Search for Cities
- Endpoint:
/cities/search
- Method:
GET
- Parameters:
queryString
(URL parameter) - Description: Searches for cities based on the provided query string.
- Endpoint:
-
Get Most Populous Cities
- Endpoint:
/cities/mostPopulous
- Method:
GET
- Description: Retrieves the most populous cities.
- Endpoint:
-
Get City by ID
- Endpoint:
/cities/getCityById/:cityId
- Method:
GET
- Parameters:
cityId
- Description: Retrieves a city by its ID.
- Endpoint:
-
Get Trip by ID
- Endpoint:
/trips/getTripById/:tripId
- Method:
GET
- Parameters:
tripId
- Description: Retrieves a trip by its ID.
- Endpoint:
-
Get Trips by City ID
- Endpoint:
/trips/getTripsByCity/:cityId
- Method:
GET
- Parameters:
cityId
- Description: Retrieves trips by a city's ID.
- Endpoint:
-
Create a Trip
- Endpoint:
/trips/createTrip
- Method:
POST
- Parameters:
tripDto
(request body) - Description: Creates a trip with the provided trip DTO.
- Endpoint:
-
Get Popular Trips
- Endpoint:
/trips/popularTrips
- Method:
GET
- Description: Retrieves the most popular trips.
- Endpoint:
- First, get a copy of the .env file
- This file contains environment variables, including the Google Maps and Unsplashed API keys
- These variables are accessible from the config.js module for application-wide use
- Install nvm and run the command "nvm install", and then "nvm use"
- Then, install all packages with "npm i"
- Install PostgreSQL with "sudo apt update" and then "sudo apt install postgresql postgresql-contrib"
- Start the server with "sudo service postgresql start"
- Then, enter the psql shell with "sudo -u postgres psql"
- Set a db password: "postgres=# \password"
- Once PostgreSQL is installed, use the following commands to manage the db:
- sudo service postgresql status for checking the status of your database.
- sudo service postgresql start to start running your database.
- sudo service postgresql stop to stop running your databas 9FC7 e.
Initalize the database by running the following script:
- npm run create_database
Finally, start the server by running
- npm run dev
The pg-promise library is used as an interface for the PostgreSQL database. It allows for connecting to the database and creating queries.
The Cities and Trips tables are defined as follows:
CREATE TABLE cities(
id serial primary key,
city_name varchar(50),
state varchar(50),
population integer,
latitude numeric(10, 6),
longitude numeric(10, 6)
);
CREATE TABLE trips(
id serial primary key,
city_id int not null REFERENCES cities(id),
title varchar(255) not null,
destinations jsonb,
description text,
price_range int,
duration int,
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);
The tables are then created using pg-promise:
const pgPromise = require("pg-promise")
...
let dbConfig = { ...config.DATABASE_CONFIG, database: "postgres" }
// Configure DB connection
const pgp = pgPromise({})
let dbClient = pgp(dbConfig)
// Create table cities
await dbClient.none(getQueryFromFile("createTableCities"))
await insertCitiesIntoCitiesTable(dbClient)
// Create table trips
await dbClient.none(getQueryFromFile("createTableTrips"))
A .csv file containing every US city is loaded into the cities table. The file was retrieved from Data.gov.
city_name,state,population,latitude,longitude
Marysville,Washington,63269,48.0517637,-122.1770818
Perris,California,CA,72326,33.7825194,-117.22864779999999
Cleveland,Ohio,OH,390113,41.499320000000004,-81.6943605
...
Unit tests are written using Jest
In Jest, each test suite runs on a separate thread
- A new connection pool is created for every test suite, each containing a max of 3 connections
- Each test case is executed using a db transaction, which is then rolled back to prevent interfering with other test cases
- Also if any query fails, the transaction is rolled back automatically in pg-promise
- For example:
it("should return an error when unsuccessfully inserting a trip", async () => { await dbClient.tx(async (transaction) => { ... const responseAddTrip = await queries.addTrip( cityCreationDto, transaction ) expect(responseAddTrip.success).toBe(false) const newTripId = responseAddTrip.data expect(newTripId).toBe(undefined) await queries.rollbackTransaction(transaction) }) })
The Supertest package is used to simulate requests to back-end endpoints, and receive their responses
- For example:
const request = require("supertest") const app = require("../app") jest.mock("../utils/queries") ... it("should retrieve all trips for a city", async () => { const mockResponse = { success: true, data: [{ title: "Short trip in Manchester" }, { title: "Tour Liverpool" }] } queries.getTripsByCityId.mockResolvedValueOnce(mockResponse) const city_id = 999 const response = await request(app).get(url + "/getTripsByCity/" + city_id) expect(response.body.data[0].title).toEqual("Short trip in Manchester") })