Trying to fetch response from Goodreads API

Hello everyone!

I’m currently working on building a React book finder app that talks with Goodreads’ API. My backend skills are lacking so I wanted to undergo this project to improve on that. But I’m having trouble talking to Goodreads’ API; whenever I try to fetch a response and console.log(...) it, I get a 401 (Unauthorized) Error:

In case its too hard to read, I seem to be getting the error because:

Access to fetch at ‘this URL’ from origin ‘my computer’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. If an opaque response serves your needs, set the request’s mode to ‘no-cors’ to fetch the resource with CORS disabled.

My first question is: How do I set the request’s mode to be ‘no-cors’?

My second question is: Is this recommended? Or is there a better, more sound way of getting past the 401 error?

At the end of the day, I just want to be able to display search results but I need to be able to draw from Goodreads’ API in order to display the data.

Thank you in advance!

Brandon

this could mean there is a problem with your api key have you added your own proper key?

I don’t think so. How do you do that?

FWIW, would resetting the api key help me out?

Look at the API key being passed in the url params in the network tab. It says your API key is api_key, which would suggest you haven’t passed that variable in properly.

So since api_key was declared as a const variable, I fixed url to see what you were talking about.

It looks like the key is being read as undefined.

Since I know that I, in fact, defined api_key, what could this mean?

It’s hard to say without seeing your code, but if a variable is undefined it could just be that it’s out of scope - i.e. the part of the code that calls the variable can’t see it because it is defined in a block it doesn’t have access to.

I see. I did store the key in a separate .env file because I read that it was a secure way of hiding API keys but still being able to use them? Therefore, it would be out of scope.

Ah, yeah this is a tough problem to solve!

If you have a backend api that can do the fetch for you and then feed the data you need to your frontend app, then you’re all set. But any call to the API from your frontend directly will expose your API key.

One solution I use (which sucks, but works) is to create an input field for users to paste their own api key in, and then I read that from local storage.

Example: https://suspicious-liskov-6ab299.netlify.com/

Code: https://github.com/JacksonBates/github-graphql

I don’t know what your familiarity is with React - but the principle is the same in normal javascript, too. Get the key from the user, save it to storage, then load the key from storage.

In my example it basically works like a password field.

So you’re saying I should have a backend api fetch data from the Goodreads’ api? And then move that data to the front-end?

What would be an example of a backend api? Like the one from GitHub?

You have two options.

Option 1 is like my example above. Ask your user for their api key and then save that to local storage. Read the key value from local storage when you need it. Because it’s saved to local storage they only need to do this once (unless their clear their cache).

Option 2 is to make a backend api of your own that communicates with GoodReads directly on your behalf. The journey to get the data would look something like this:

Your front end asks your backend for data.
Your backend asks the GoodReads api for that data.
Your backend sends it to your frontend.

Option 2 is better, but harder to build.

I’ve actually wanted to figure out option 2 properly for ages, so I had a go at implementing a simple backend to act as a request relay.

You can take a look at this to see how I did it.

I used the GoodReads API so it’s more useful to you. The bad thing about GR is that they only serve XML for the search queries, so there’s a little more messing around turning it into JSON for easy use in your front end!

Here’s the repo: https://github.com/JacksonBates/example-goodreads-api-relay

Read through the README.md to get started and let me know if anything is unclear.

1 Like

I can’t seem to create a new .env using the command you enclosed in the README.md file:

cp .env.example .env

When I look inside the repo folder that I cloned to my computer, the .env wasn’t there.

Are you on Windows? I can’t remember the command line commands for that.

You can just make a copy of the file however you normally do. (Like, right click, copy, paste, rename)

Thanks, @camperextraordinaire. My windows knowledge is very rusty!

No, I’m using macOS.

Nope. It wasn’t there when I cloned the repo.

Look for hidden files.

The . hides files on Unix based systems.

cp probably worked, you just have the file hidden most likely.

The file tree in VSCode would show hidden files if you use that.

That did the trick, my friend! I’m using Atom and it was right there in the tree view. Onward! :grinning:

1 Like

@JacksonBates

So I’m now at the part of the README.md where I’m trying to apply the backend to my frontend (the “How do you use this in your front end?” section).

I keep running into this Type Error, though:

I can’t tell whether this error is coming from the backend.js file that has code from the example you provided, or my `app.js’ file:

import  React  from 'react'
import  fetchGoodReads  from './backend'
class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      query: '',
      loading: false,
      itemsFound: []
    }
  }

  render() {

    return (
      <div className="app">
        <header className="app-header">
          <h1>Book Finder App!</h1>
          <h4>What Will Your Next Read Be?</h4>
        </header>
      </div>
    );
  }
}

export default App;

I’ve confirmed the server is running and it passes the tests on Postman, but when I run npm start, I keep getting that TypeError.

@JacksonBates

So I think I found out why I was getting that TypeError: fetchGoodReads is an immediately-invoked function expression (IIFE). I’m guessing that means it can’t be imported in app.js?