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!