Home > Software engineering >  How to access search params with node.js(express) API call and served on react front-end
How to access search params with node.js(express) API call and served on react front-end

Time:10-28

Tech stack - node.js(express) server, react front-end

components folder structure

|__src
|  |__components
|     |__App
|     |   |__App.js
|     |   |__App.css
|     |__Business
|     |   |__Business.js
|     |   |__Business.css
|     |__BusinessList
|     |   |__BusinessList.js
|     |   |__BusinessList.css
|     |__SearchBar
|         |__img
|         |__SearchBar.js
|        |__SearchBar.css
|__util
   |__yelp.js

What i want to happen
When a user enters input into "Search Businesses" and enters input into "Where?" and clicks "Let's go button" it should retrieve the category typed in, EX: "Pizza" and location typed in, EX: "Denver"

In other words, a user should be able to "Search Businesses" and enter a location "Where" and filter the results

How this is working
With a click of the Let's Go button, it calls the searchYelp function which is being imported from yelp.js into App.js inside of yelp.js searchYelp function it fetches the endpoint '/api/hello' from the express server that's calling the API.

What's happening currently
So currently no filtering is happening and when you click the "Let's Go" button it returns a list of random businesses
I have the filtering code setup I just don't know how to implement it to get it working...

Heres the code

SearchBar.js

import React from 'react';
import './SearchBar.css';

class SearchBar extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            term: '',
            location: '',
            sortBy: 'best_match'
        }

        this.handleTermChange = this.handleTermChange.bind(this)
        this.handleLocationChange = this.handleLocationChange.bind(this)
        this.handleSearch = this.handleSearch.bind(this)

        this.sortByOptions = {
            'Best Match': 'best_match',
            'Highest Rated': 'rating',
            'Most Reviewed': 'review_count'
        };
    }

    getSortByClass(sortByOption) {
        if (this.state.sortBy === sortByOption) {
            return 'active'
        }
        return ''
    }

    handleSortByChange(sortByOption) {
        this.setState({
            sortBy: sortByOption
        })
    }

    handleTermChange(event) {
        this.setState({
            term: event.target.value
        })
    }

    handleLocationChange(event) {
        this.setState({
            location: event.target.value
        })
    }

    handleSearch(event) {
        this.props.searchYelp(this.state.term, this.state.location, this.state.sortBy)
        event.preventDefault()
    }

    renderSortByOptions() {
        return Object.keys(this.sortByOptions).map(sortByOption => {
            let sortByOptionValue = this.sortByOptions[sortByOption]
            return <li onClick={this.handleSortByChange.bind(this, sortByOptionValue)} className={this.getSortByClass(sortByOptionValue)} key={sortByOptionValue}>{sortByOption}</li>;
        })
    }

    render() {
        return (
            <div className="SearchBar">
                {this.searchYelp}
                <div className="SearchBar-sort-options">
                    <ul>
                        {this.renderSortByOptions()}
                    </ul>
                </div>
                <div className="SearchBar-fields">
                    <input onChange={this.handleTermChange} placeholder="Search Businesses" />
                    <input onChange={this.handleLocationChange} placeholder="Where?" />
                    <button className="SearchBar-submit" onClick={this.handleSearch}>Let's Go</button>
                </div>
            </div>
        )
    }
};

export default SearchBar;

App.js

import React from 'react';
import BusinessList from '../BusinessList/BusinessList';
import SearchBar from '../SearchBar/SearchBar';
import Yelp from '../../util/yelp';
import './App.css';

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      businesses: [],
    };

    this.searchYelp = this.searchYelp.bind(this);
  }

  searchYelp(term) {
    Yelp.searchYelp(term).then((businesses) => {
      this.setState({ businesses: businesses })
    })
  }
  render() {
    return (
      <div className="App">
        <SearchBar searchYelp={this.searchYelp} />
        <BusinessList businesses={this.state.businesses} />
      </div>
    )
  }
}

export default App;

Yelp.js

const { default: SearchBar } = require("../components/SearchBar/SearchBar");

const Yelp = {
    searchYelp(term) {
    return fetch('/api/hello/:term')
        .then((response) => {
            console.log(response)
            return response.json()
        }).then((jsonResponse) => {
            console.log(jsonResponse)
            if (jsonResponse.businesses) {
                return jsonResponse.businesses.map((business) => {
                    return {
                        id: business.id,
                        imageSrc: business.image_url,
                        name: business.name,
                        address: business.location.address1,
                        city: business.location.city,
                        state: business.location.state,
                        zipCode: business.location.zip_code,
                        category: business.categories.title,
                        rating: business.rating,
                        reviewCount: business.review_count,
                    }
                })
            }
        })
    }
}

export default Yelp

And the server
I was trying to filter the term only to test but was unsuccessful, If I could get the term and location working I would be fine with that I think, and leave out sortBy... Server.js

const express = require('express');
const axios = require('axios');
require('dotenv').config();
const cors = require('cors');
const bodyParser = require('body-parser');

const app = express();
app.use(cors());
const port = process.env.PORT || 5000;

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

const apiKey = process.env.APIKEY

app.get('/api/hello/:term', (req, res) => {
    const term = req.params.term
    const config = {
        method: 'get',
        url: 'https://api.yelp.com/v3/businesses/search?term=${term}&location=${location}',
        headers: {
            'Authorization': `Bearer ${apiKey}`
        }
    };

    axios(config)
        .then(function (response) {
            // res.send(JSON.stringify(response.data, null, 2));
            // res.send(term)
            return JSON.stringify(response.data, null, 2)
        }) 
        .then(function (jsonResponse) {
            res.send(jsonResponse)
        }) 
        .catch(function (error) {
            console.log(error);
        });
});

app.listen(port, () => console.log(`Listening on port ${port}`));

Heres a screenshot of the web app
enter image description here

CodePudding user response:

if you want to use the built-in capability of the yelp location filter, you better express it in your express app (pun intended!):

app.get('/api/hello/', (req, res) => {
    // use query instead of params,
    // and destruct location as well
    const {term, location} = req.query
    const config = {
        method: 'get',
        url: 'https://api.yelp.com/v3/businesses/search?term=${term}&location=${location}',
        headers: {
            'Authorization': `Bearer ${apiKey}`
        }
    };
}

... and pass query parameters in the front end:

const Yelp = {
    // take care to update any existing calls
    // to this function with the location parameter
    searchYelp(term, location) {
        return fetch(`/api/hello?term=${term}&location=${location}`)
        // etc. etc. ...

if you just want to filter it in the front end (not recommended), and the location input will always hold a city name (and you can enforce it) - you're nearly there, all that's left to do is to declare the new function arguments, and filter the result:

function searchYelp(term, location, sortBy) {
  // ...
  this.setState({ businesses: businesses.filter(business => business.city === location) })
  // ...
}

if you still don't want to use yelp API's location, and want a more fuzzy search (i.e. to be able to feed a general term, like "brooklyn", and output exact info like city, state, zip code etc.), you would have to implement some sort of autocomplete mechanism on the location input, which will involve further calls to some backend service.

  • Related