EDIT: I've carefully reviewed and tried solutions from the following SO posts, which were very informative, but unfortunately haven't been able to manage to make the new info work in my context.
- What is JSONP all about?
- jsonp with jquery
- Basic example of using .ajax() with JSONP?
- Make cross-domain ajax JSONP request with jQuery
Hi! I'm trying for the first time to use data from an external API (the Google Books API - https://developers.google.com/books/docs/v1/using) in an Node.js & Express/MEAN stack app. I'm using the google-books-search npm package (https://www.npmjs.com/package/google-books-search) - not sure whether that's making things easier or harder for me at this juncture.
I've spent the weekend researching and trying to fix my problems. I've read a lot of cool stuff about JSONP, AJAX, and more but fear I'm investigating material that's beyond my current ability to understand it. I hope to get the basics working before I go too much further down the rabbit hole.
Here are the issues:
- Half of my routes work in the browser and/or in cURL but others don't.
- I'm able to access JSON query results from the external API in the console, but haven't been able to manipulate that data to show up in my HTML view. I've read a lot about having to use JSONP but haven't been able to make it work.
Here's an explanation of what I want the routes to do:
- app.get('/') - root - WORKS 
- app.get('/authorsearch/:author') - retrieve query results from Google Books API - CAN PULL FROM API AND DISPLAY QUERY RESULT DATA IN CONSOLE 
- app.get('/titlesearch/:title') - retrieve query results from Google Books API - CAN PULL FROM API AND DISPLAY QUERY RESULT DATA IN CONSOLE 
- app.post('/books') - save book to database - NOT THERE YET (since I haven't been able to grab the query results from the API) 
- app.get('/books/:id') - retrieve specific book from database - WORKS (currently displays empty array) 
- app.post('/user') - create a new user and save to database - DOESN'T WORK 
- app.post('/login') - existing user log in - DOESN'T WORK
- app.get('/user/:id') - retrieve user details from database - DOESN'T WORK
- app.put('/user/:id') - update user details in database - WORKS
- app.delete('/user/:id') - delete user account from database - WORKS
My goal is for users to be able to search for books by title or author. Registered/logged-in users will be able to save selected books as favorites and access their list of books during later sessions.
SERVER.JS
// DEPENDENCIES
var express      = require('express'),
    mongoose     = require('mongoose'),
    bodyParser   = require('body-parser'),
    md5          = require('md5'),
    cookieParser = require('cookie-parser'),
    morgan       = require('morgan'),
    books          = require('google-books-search');
var port = process.env.PORT || 3000;
var app = express();
// MIDDLEWARE
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(express.static('public'));
app.use(cookieParser());
// DATABASE
mongoose.connect('mongodb://localhost/google_books_app_db');
// MODELS
var User = require('./models/user');
var Book = require('./models/book');
// LISTENER
app.listen(port);
console.log("server working on port " + port);
// =======
// =======
// ROUTES
// =======
// =======
// =====
// Root 
// =====
// root - WORKS 
app.get('/', function(req, res) {
    // res.render("index");
    console.log("root working");
});
// ===============================================
// Search routes - from Google Books API's server
// ===============================================
// GET books by search of author name - WORKS
app.get('/authorsearch/:name', function(req, browserResponse) {
    //browserResponse.send("bannana"); return;
    //console.log( browserResponse.send );
    var author = req.params.name;
    books.search("inauthor=" + author, function(err, bookSearchResponse) {
        if ( ! err ) {
            browserResponse.send( bookSearchResponse );
            //console.log(res);
            // $('body').append(res);
            // res.json(req.body);
        } else {
            console.log(err);
        }; // end if/else
    }); // end search
}); // end get books by author
// GET books by search of title - WORKS
app.get('/titlesearch/:title', function(req, browserResponse) {
    var title = req.params.title;
    books.search("intitle=" + title, function(err, bookSearchResponse) {
        if ( ! err ) {
            browserResponse.send( bookSearchResponse );
            // $('#results-container').append(res);
            // res.json(req.body);
        } else {
            console.log(err);
        }; // end if/else
    }); // end search
}); // end get books by title
// ============================
// Book routes - from local db
// ============================
// GET books - WORKS (shows empty array)
app.get('/books/:id', function(req, res) {
    Book.find({ 'user': req.params.id }).exec(function(err, books) {
        console.log("getting books");
        res.send(books);
    });
}); 
// ============
// User routes
// ============
// CREATE new user sign-up
app.post('/user', function(req, res) {
    var password_hash = md5(req.body.password);
    var user = new User({
        username: req.body.username,
        password_hash: password_hash
    });
    user.save(function(err) {
        if (err) {
            console.log(err);
            res.statusCode = 503;
        } else {
            console.log(user.username + " created server side");
            res.cookie("loggedInUserId", user.id)
            res.send(user);
        }; //end if/else
    }); // end save
}); // end new sign-up
// POST user log-in
app.post('/login', function(req, res) {
    var req_username = req.body.username;
    var req_password = req.body.password;
    req_password_hash = md5(req_password);
    User.findOne({ 'username' : req_username }).exec(function(err, user) {
        if (user != null && req_password_hash == user.password_hash) {
            res.cookie("loggedInUserId", user.id);
            res.send(user);
        } else {
            console.log("Error, try again");
        }; // end if/else
    }); // end findOne
}); // end log-in
// GET user by ID
app.get('/user/:id', function(req, res) {
    console.log("Find one user");
    User.findById(req.params.id).then(function(user) {
        res.send(user);
    }); // end findById
}); // end get user
// UPDATE edit user account - WORKS 
app.put('/user/:id', function(req, res) {
    console.log("User edit request");
    User.findOneAndUpdate( { _id: req.params.id }, req.body, function(err, user) {
        console.log("User updated");
        res.send(user);
    }); // end findOneAndUpdate
}); // end edit user
// DELETE user account - WORKS
app.delete('/user/:id', function(req, res) {
    console.log("User delete request");
    User.findOneAndRemove( { _id: req.params.id }, function(err) {
        res.clearCookie("loggedInUserId");
        console.log("User cookie cleared");
        res.send("User deleted");
    }); // end findOneAndRemove
}); // end delete user
// END OF USER ROUTES
// END OF ALL ROUTES
APP.JS
console.log("loaded");
$(document).ready(function() {
    console.log("document onload");
// =============
// CLICK EVENTS
// =============
    // search by author button 
    $('#author-searchbtn').on('click', function() {
        // console.log("debugger hello");
        var searchAuthor = $('#search-field').val();
        searchByAuthor(searchAuthor);
    }); // end onclick
    // search by title button 
    $('#title-searchbtn').on('click', function() {
        var searchTitle = $('#search-field').val();
        searchByTitle(searchTitle);
    }); // end onclick
    // new user sign-up button
    $('#register-btn').click(function() {
       console.log("Clicked register");
       newUser();
     });
    // user log-in button
    $('#login-btn').click(function() {
        console.log("Clicked login");
        loginPost();
    })
    // user log-out button
    $('#logout-btn').click(function() {
        Cookies.remove('loggedInUserId');
        console.log("User cookie deleted; user logged out");
    }); 
// =================
// SEARCH FUNCTIONS
// =================
    // WORKS
    var searchByAuthor = function(searchAuthor) {
        console.log("about to make ajax request");
        $.ajax({
          url: '/authorsearch/' + searchAuthor
          // url: 'https://www.googleapis.com/books/v1/volumes?inauthor=' + searchAuthor
       }).done(function(res) {
            console.log("ajax author response recieved");
                console.log(res);
          console.log("===========SEARCHED BY AUTHOR===========");
       }) // end Ajax call
       console.log( "ajax author request made");
       //handleResponse(res);
    }; // end searchByAuthor
    // WORKS
    var searchByTitle = function(searchTitle) {
        console.log("about to make ajax request");
        $.ajax({
            url: '/titlesearch/' + searchTitle
        }).done(function(data) {
             console.log("ajax title response recieved");
             console.log(data);
             // $('#results-container').html(data);
             console.log("===========SEARCHED BY TITLE===========");
             for (var i = 0; i < data.items.length; i++) {
                var item = data.items[i];
                document.getElementById("results-container").innerHTML += "<br>" + item.volumeInfo.authors;
                console.log(item.volumeInfo.authors);
            };
        console.log(data.items[3]);
        }); // end Ajax call
        console.log( "ajax title request made");
        // handleResponse(data);
    }; // end searchByTitle
  // HAVEN'T GOTTEN HERE YET
    var handleResponse = function(res) {
        for (var i=0; i < res.items.length; i++) {
            var item = resp.items[i];
            document.getElementById('#results-container').innerHTML;
        }; // end for loop
    }; // end handleResponse
  // HAVEN'T GOTTEN HERE YET - NOT SURE IF IT WILL BE NEEDED
    var displayBooks = function(res) {
        // displayBooks();
    }; // end displayBooks
// ===============
// USER FUNCTIONS
// ===============
    var setUp = function() {
        console.log("setting up");
        if (Cookies.get("loggedInUserId") != undefined) {
            console.log("already logged in");
        };
    };
    var newUser = function() {
        user = {
            username: $('#username').val(),
            password: $('#password').val(),
        };
        console.log(user);
        $.ajax({
            url: '/user',
            method: 'POST',
            dataType: 'json',
            data: user
        }).done(function(data) {
            user = Cookies.get('loggedInUserId');
        }); // end ajax request
    }; // end newUser
    var loginPost = function() {
        user = {
            username: $('#username').val(),
            password: $('#password').val(),
        };
        $.ajax({
            url: '/login',
            method: 'POST',
            dataType:'json',
            data: user
        }).done(function(data) {
            console.log("submitted login info");
        }).fail(function() {
            console.log("login failed");
        }); // end ajax request
    }; //end userLogin
}); // end document ready
INDEX.HTML - Please note that the templating isn't up and running yet so I just have a very basic view (search field and buttons, user registration form, user log-in form, log-out button).
<html>
<head>
    <script type="text/javascript" src="https://code.jquery.com/jquery-2.1.4.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/js-cookie/2.0.4/js.cookie.js"></script>
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.4/handlebars.js"></script>
    <link rel="stylesheet" type="text/css" href="css/styles.css">
    <title>Google Books App</title>
</head>
<body>
    <h1>Discover Literature</h1>
    <div id="search-container">
        <p>Find something you'll love.</p>
        <input type="text" id="search-field">
        <button id="author-searchbtn">Search by Author</button>
        <button id="title-searchbtn">Search by Title</button>
    </div>
    <div id="results-container">
    </div> 
    <br><br>
    <!-- TEMP SIGN-UP -->
    <div id="signup-container" data-id="{{_id}}">
        <b>Sign up</b>
        <br>
        <label for="username">Username</label><br/>
        <input type="text" id="username" placeholder="username"/><br>
        <label for="password">Password</label><br/>
        <input type="text" id="password" placeholder="password"/><br>
        <button id="register-btn" data-id="{{_id}}">Register!</button>
    </div>
    <br><br>
    <!--TEMP LOG-IN -->
    <div id="login-container" data-id="{{_id}}">
        <b>Log in</b>
        <br>
        <label for="username">Username</label><br/>
        <input type="text" id="username" placeholder="username"/><br>
        <label for="password">Password</label><br/>
        <input type="text" id="password" placeholder="password"/><br>
        <button id="login-btn" data-id="{{_id}}">Login!</button>
    </div> 
    <br><br>
    <!-- TEMP LOG-OUT BUTTON -->
    <b>Log out</b> 
    <br>
    <button id="logout-btn">Log out</button>
<!-- ============== TEMPLATES ============== -->
<!-- add sign-up and log-in templates -->
<!-- edit user account -->
    <template id="edit-user-template">
        <div id="edit-user-container" data-id="{{_id}}">
            <button id="account-delete-button">Delete account</button><br>
        </div>
    </template>
<!-- delete user account -->
    <template id="delete-user-template">
        <div id="delete-user-container" data-id="{{_id}}">
            Please confirm.
            <button id="delete-user-confirm-button" data-id="{{_id}}">Delete my account</button>
        </div>
    </template>
<!-- ============== END TEMPLATES ============== -->
    <script src="js/app.js"></script>
</body>
</html>
Thank you for any insights!
 
    