Express.js update/put route does not work - javascript

I have a website where I want to add, edit and delete student contact data. The data is stored in a SQLite database. My post, get and delete routes (Express API) work but my update route does not.
I load the data of an existing contact into a html form and my plan is to edit the data in the form to send the updated data via a button. But I get the error message "Cannot GET /api/update/contacts/1" although I've implemented a POST method request.
I guess the request doesn't hit the API endpoint but I don't know why, especially since all the other routes are working. Can anyone help?
I already tried MethodOverride, but it also did not work.
The code in my html page with the form and the button:
<form method="POST" id="myForm">
<button
onclick="update()"
id="aktualisierenButton"
type="submit"
>Aktualisieren
</button>
<script>
function update() {
window.open("/api/update/contacts/" + localStorage.getItem("paraID"),
"_blank",
"toolbar=no,scrollbars=no,width=800,height=800");
window.close();
}
I already tried MethodOverride, but it also did not help.
The code in my start.js file where I handle the routes:
app.put("api/update/contacts/:studentID", async (request, response) => {
const studentID = parseInt(request.params.studentID);
const existingContact = await contactsManager.getContact(studentID);
if (existingContact) {
const contact = request.body;
await contactsManager.updateContact(studentID, contact);
response.status(200).send();
} else {
response.status(400).send({
error: "The contact with the specified studentID does not exist.",
});
}
});
The code in my ContactsManager.js file where I handle the database requests:
async updateContact(studentID, contact) {
return new Promise((resolve, reject) => {
db.run(
"UPDATE contacts SET vorname = ?, nachname = ?, mail = ? WHERE studentID = ?",
[contact.vorname, contact.nachname, contact.mail, studentID],
(error, row) => {
if (error) {
reject(error);
} else {
resolve(row);
}
}
);
});
}

You are getting the error because window.open will make a GET request to the resource and load it into a window. Instead of creating a new window then destroying it just to update your data, try using the Fetch API instead.
Using the fetch api with a PUT request would look something like below. You will need to adapt this to your specific data, and process or display the result if that's what you are trying to achieve by opening a new window, but this should give you a starting point.
try {
let fetchUrl = "https://mysite.url/api/update/contacts/" + localStorage.getItem("paraID")
let fetchOptions = {
method: "PUT",
headers: {
"Content-Type": "application/json" // Update with your data type
},
body: JSON.stringify({
data: myData // Update with your JSON data to PUT
})
}
let response = await fetch(fetchUrl, fetchOptions)
if (response.ok) {
// Convert the response to json and process
let data = await response.json()
console.log(data)
}
} catch (error) {
// Handle network errors here
console.error(error)
}
Your endpoint also does not implement a POST request, it uses a PUT request. If you wanted a POST request you should use app.post instead of app.put, although using a PUT request is fine for what you are trying to achieve.

Related

Is there a way to send data from a js file to the express server without using a form?

I am new here, Sorry if I miss something from the best practices.
I'm creating a sudoku game, using express, js, html and mongoDb.
Now I'm trying to create a statistics system. I would like to send a data when you wins the sudoku, to the express server (server.js) and after update the mongoDb.
I know I can send a data with the POST method, but I only know using the form html, but in this case I want to send it by the function js.
The js file: game.js
let cells = document.querySelectorAll('.div-cell'); // get the cells of the sudoku board
function checkWin() {
const db = getDb() // get the localStorage db (there is sudoku informationo, like the solution)
curentBoard = ''
for (let i = 0; i < 81; i++) {
if (cells[i].textContent === '') { // check if the cell is blank
curentBoard += '-'
} else {
curentBoard += cells[i].textContent
}
}
if (curentBoard === db.solution) { //check if the board is equal the solution
// Here would be the code to send the data to the server
alert('You won!')
newGame()
}
}
I've tried to use export to send the function to the server.js, to change the information, but I can't because the game.js is linked to the game.html, and for some reason the export doesn't work. Also for import.
I also tried to use ejs files, I rendered the ejs file sending the data statistics, by the server.js (express) but I couldn't change any data in the ejs file.
Then I tried to research other methods to make this, but I didn't find anything
Is there a way to send a data to the server.js by that function?
Here is an example of sending a post request with some data to the backend then sending back a response data from backend to frontend.
//front end
fetch('api/server', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
myData: "test"
})
})
.then((res) => res.json())
.then(data => {
if (data.status == 'success') {
//when data is returned from backend
const response = data.response
console.log(response) //should print dataReceived
}
})
//backend
const handler = async(req, res) => {
//accessing data sent from front end
const body = req.body
//should print test
console.log(body.myData)
//returning data back to frontend
return res.json({
status: 'success',
response: 'dataRecieved'
})
}
Yes, you can use fetch API inside your function to send POST request to your server. You need to send the data in the format that server expects (e.g. JSON)
here is an example of sending post request using fetch()
https://googlechrome.github.io/samples/fetch-api/fetch-post.html
for more details about fetch API see :
https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
or this thread :
Fetch: POST JSON data

How can I delay a POST request from a user form until AFTER a webhook POST from Stripe is received?

I want users to pay a fee before a POST request from a front end form is processed. I have a Stripe webhook that works fine on the backend, but I'm not sure how to delay the front end posting of the form until after the payment confirmation is received.
In the code below, right now, createTour and createTourPay run at the same time. I would like for createTourPay to execute first, and the createTour only triggers after Stripe posts to my application from the webhook. How can I achieve this?
Controller File (webhook):
exports.webhookCheckout = (req, res, next) => {
const signature = req.headers['stripe-signature'];
let event;
try {
event = stripe.webhooks.constructEvent(
req.body,
signature,
process.env.STRIPE_WEBHOOK_SECRET
);
} catch (err) {
return res.status(400).send(`Webhook error: ${err.message}`);
}
if (
event.type === 'checkout.session.completed' &&
event.line_items.name === 'New Job Purchase'
) {
res.status(200).json({ recieved: true });
// Somehow, I want this to trigger the execution of the POST request in my front end JS file.
} else {
if (event.type === 'checkout.session.completed')
createBookingCheckout(event.data.object);
res.status(200).json({ recieved: true });
}
};
Front end JS file:
export const createTourPay = async myForm => {
try {
// 1) Get the checkout session from API response
const session = await axios(`/api/v1/tours/tour-pay`);
const complete = 1;
// console.log(session);
// 2) Create checkout form + charge the credit card
await stripe.redirectToCheckout({
sessionId: session.data.session.id
});
} catch (err) {
// console.log(err);
showAlert('error', err);
}
};
export const createTour = async myForm => {
try {
const startLocation = {
type: 'Point',
coordinates: [-10.185942, 95.774772],
address: '123 Main Street',
description: 'Candy Land'
};
const res = await axios({
method: 'POST',
headers: {
'Content-Type': `multipart/form-data; boundary=${myForm._boundary}`
},
url: '/api/v1/tours',
data: myForm
});
if (res.data.status === 'success') {
showAlert('success', 'NEW TOUR CREATED!');
window.setTimeout(() => {
location.assign('/');
}, 1500);
}
} catch (err) {
showAlert('error', err.response.data.message);
}
};
Broadly: don't do this. Instead, you in fact should create some pending/unpaid version of the "tour" (or any other product/service) in your system, then attach the unique id (eg: tour_123) to the Checkout session when you create it, either using the client_reference_id (doc) or metadata (doc):
const session = await stripe.checkout.sessions.create({
// ... other params
client_reference_id: 'tour_123',
metadata: { tour_id: 'tour_123' },
});
Then you'd use the webhook to inspect those values, and update your own database to indicate the payment has been made and that you can fulfill the order to the customer (ship product, send codes, allow access to service etc).
If you really want to proceed with a more synchronous flow, you can use separate auth and capture to sequence your customer experience and capture the funds later after authorizing and creating your tour entity.
Edit: a note about security
You should never trust client-side logic for restricted operations like creating a "paid" tour. A motivated user could, for example, simply call your /api/v1/tours create endpoint without ever going through your payment flow. Unless you validate a payment and track that state on your server you won't be able to know which of these had actually paid you.

How do I save my data from my POST request, as a variable, to use in my GET request?

I am receiving my data from my POST request. I want to use that data as a variable in a GET request.The data from the POST request is user inputted to make different queries on the 3rd party API> How can I save my data as a variable to do so? You can see my request.body is being saved as const data. And I need it used as ${data} in my get request. I am pretty new to this, so any suggestions on best practice would be appreciated. Thanks for helping out.
Server.js
app.get(`/getmovies`, (req, res) => {
request(`http://www.omdbapi.com/?t=${data}&apikey=${API_KEY}`,
function (error, response, body) {
if (!error && response.statusCode == 200) {
var parsedBody = JSON.parse(body);
res.send(parsedBody)
} else {
console.log("error in the server")
}
}
)
})
app.post('/postmovie', (request, response) => {
console.log("I got a request!")
console.log(request.body);
const data = request.body;
response.json({
status: 'success',
name: data
})
})
///Client
postMovie = async (e) => {
e.preventDefault();
const data = this.state.movie;
const options = {
method: 'Post',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data)
};
const response = await fetch('/postmovie', options);
const json = await response.json();
console.log(json);
};
getMovies = (e) => {
e.preventDefault();
const { movie } = this.state;
axios.get(`/getmovies`)
.then(response => this.setState({ movies: response.data }))
// .then(response => this.setState({ movies: response.data }))
.catch(err => console.error(err))
}
render() {
const { movie } = this.state;
return (
<div>
<section className="form-container">
<div className="movie-form-div">
< form>
<input className="form-input" type="text" name="name"
value={movie.name}
onChange={e => this.setState({ movie: { ...movie, name: e.target.value } })} />
<button onClick={this.getMovies}>Search</button>
<button onClick={this.postMovie}>Post</button>
</form >
</div>
</section>
As I have mentioned in the comments, your question is worded such that you make it sound like you absolutely need two requests.
Per the comments, OP has clarified that they don't necessarily need two requests so on that note here are a few things that you can try.
1) Set up an application.conf
Configuration files allow us to parameterize and configure variables that our applications need to function. You can vary the config files between environments (dev/stage/prod etc.) and deployments.
This will assume your API_KEY doesn't change frequently. Think of it as hard coding your api key like
var apiKey = "...."
but a better and more secure way of doing so.
In your case this won't work but it is good to be aware of it. You can use this package in the future
2) Get request query parameters.
This is your best bet
Query parameters, as the name suggest, allow us to pass in variables with the get requests to filter the results in the server side. This can be something like a user-id, page number etc.
Your get url from the client side would look like something like this
mydomain.com/getmovies?apikey={YourKeyGoesHere}
I am sure you have seen that ? in many of the urls you visit.
If you are using express your end point will look something like this.
app.get(`/getmovies`, (req, res) => {
var apiKey = req.query.apikey
request(`http://www.omdbapi.com/?t=${data}&apikey=${apikey}`,
function (error, response, body) {
if (!error && response.statusCode == 200) {
var parsedBody = JSON.parse(body);
res.send(parsedBody)
} else {
console.log("error in the server")
}
}
)
})
See How to get GET (query string) variables in Express.js on Node.js?
This will allow you to pass it from the client side.
If you are not using express please see https://nodejs.org/api/querystring.html
3) If you absolutely need two endpoints...
Use a cookie. Once a cookie is set, it is transported back and forth with every request, until it expires.
See this How to set cookie in node js using express framework? or this
Once you receive the post request you would set a cookie
app.post('/postmovie', (request, response) => {
response.cookie("apikey", data, maxAge: 900000, httpOnly: true });
response.json({
status: 'success'
})
})
Once this cookie is set, it will be transported to the client and back with all the requests including the get.
So in your get request you can do:
app.get(`/getmovies`, (req, res) => {
var apikey = req.cookies.apikey;
request(`http://www.omdbapi.com/?t=${data}&apikey=${apikey}`,
function (error, response, body) {
if (!error && response.statusCode == 200) {
var parsedBody = JSON.parse(body);
res.send(parsedBody)
} else {
console.log("error in the server")
}
}
)
})
So what's the difference between #2 and #3?
The query parameters that are encoded in the url are public and can be intercepted. This is fine for things like page numbers but if the API key is a paid one, this isn't very safe. Also once you submit the query, it is gone (unless you save the api key in the client side to the memory or local storage) so the user will have to potentially re-enter the api key everything.
Cookies on the other hand are private. The http-only flag we set in the code, prevents even the browser from reading it. Only the issuing server can see the contents. Plus cookies get stored in the browser until the expiration date so they can be reused with multiple requests.
Pick the one that works for you.
P.S. I didn't test the code above, you may need to tweak it a little bit. The answer is to give you a general idea on how these things are usually handled

Nuxt axios cannot handle the server sesponse

I am new to Nuxt.js and I am faced with a strange kind of issue. I have an endpoint in my backend API, allowing the end user to send a token and a new password and reset the user password.
While the request is sent correctly, and the server responding with the correct data:
In the Nuxt.js side, I have an issue, with the response data.
So, to handle all the HTTP requests using the axios, I have a class like that:
class WebAPI {
// $axios is the instance used in the Nuxt.js context
constructor($axios) {
this.$http = $axios;
}
async call(config) {
try {
///////////////////////////////////////////////////////
// HERE IS THE PROBLEM. THE FOLLOWING CONSOLE.LOG
// IT RETURNS undefined WHILE THE NETWORK RESPONSE
// RETURNS WITH DATA
///////////////////////////////////////////////////////
const result = await this.$http(config);
console.log(result);
// ...
} catch( e) {
// ...
}
}
}
And I use this class like:
const data = {
token,
new_password
};
const options = {
method: 'POST',
url : '/reset-password',
data
};
return this.webApi.call(options);
But as you probably see, in the WebAPI service, the response of the axios is undefined.
Also, it worths to mention, that the exact same WebAPI class working perfectly with other API Requests I do throughout the application.
Could you help with that issue? Do you see anything wrong?
I think you are using axios wrong. Try use $request method, like that:
async call(config) {
try {
const result = await this.$http.$request(config);
console.log(result);
// ...
} catch( e) {
// ...
}
}

Azure Mobile Services - Getting more user information

I inherited a Windows 8 application that is written with XAML. So in C# when I make this call
user = await MobileServices.MobileService
.LoginAsync(MobileServiceAuthenticationProvider.MicrosoftAccount);
(This is for Azure Mobile Services)
The user object is ONLY giving me the Token and the MicrosoftAccount:..............
In order to get to authenticate people, I need to be able to see WHO is requesting access...
I looking at articles like below, but I seem to be missing something? Is this javascript in the article something I would have to write in Node.js?
Example article:
http://blogs.msdn.com/b/carlosfigueira/archive/2013/12/12/expanded-login-scopes-in-azure-mobile-services.aspx
Currently to be able to get more information about the logged in user, you need to make a second call to the service to retrieve the user info. You don't really need to ask for additional login scopes (the topic of the post you mentioned) to retrieve the user name, since that is given by default for all the providers.
This post should have the code you need to write in the server side (node.js) to get more information about the logged in user. The TL;DR version is given below:
On the server side: add this custom API (I'll call it "userInfo"; set the permission of GET to "user", and all others to admin):
exports.get = function(request, response) {
var user = request.user;
user.getIdentities({
success: function(identities) {
var accessToken = identities.microsoft.accessToken;
var url = 'https://apis.live.net/v5.0/me/?method=GET&access_token=' + accessToken;
var requestCallback = function (err, resp, body) {
if (err || resp.statusCode !== 200) {
console.error('Error sending data to the provider: ', err);
response.send(statusCodes.INTERNAL_SERVER_ERROR, body);
} else {
try {
var userData = JSON.parse(body);
response.send(200, userData);
} catch (ex) {
console.error('Error parsing response from the provider API: ', ex);
response.send(statusCodes.INTERNAL_SERVER_ERROR, ex);
}
}
}
var req = require('request');
var reqOptions = {
uri: url,
headers: { Accept: "application/json" }
};
req(reqOptions, requestCallback);
}
});
}
On the client side, after a successful login, call that API:
user = await MobileServices.MobileService
.LoginAsync(MobileServiceAuthenticationProvider.MicrosoftAccount);
var userInfo = await MobileServices.MobileService.InvokeApiAsync(
"userInfo", HttpMethod.Get, null);
userInfo will contain a JObject with the user information. There is an open feature request to make this better at http://feedback.azure.com/forums/216254-mobile-services/suggestions/5211616-ability-to-intercept-the-login-response.

Categories