I am trying to add a dynamic route to the api folder for GET request.
In this scenario it works fine
api/[product]
const baseUrl ='https://myUrl'
const { product } = req.query
const url = `${baseUrl}/${product}`
And then testing http://localhost:3000/api/phone in Postman returns the correct result. So far it is fine.
Then, instead of fetching the api by product, I want my dynamic property to be filter values. Filter syntax provided by particular api looks like this ?$filter=name eq 'Milk'.
Filtering on frontend works fine when I test it in postman or in browser.
https://myUrl/phone?$filter=name eq 'iphone'
Now I try to do exactly the same as above, now in api folder. But it returns 404 This page could not be found..
api/[productType]
const baseUrl ='https://myUrl/phone?$filter'
const { productType } = req.query
const url = `${baseUrl}=${productType}`
In Postman I test it so:
`http://localhost:3000/api/phone=name-eq-'iphone'`
And get 404 response.
How to fix this issue? Any help will be appreciated.
I have created a demo at https://github.com/vercel-support/so-71663454-nextjs-route-poc with
// /api/[productType]
export default function handler (req, res) {
const baseUrl ='https://myUrl/phone?$filter'
const { productType } = req.query
const url = `${baseUrl}=${productType}`
res.json({ productType, query: req.query, url })
}
If you visit the link using https://so-71663454-nextjs-route-poc.vercel-support.app/api/phone=name-eq-'iphone'
You should be able to see the following result:
{
"productType": "phone=name-eq-'iphone'",
"query": {
"productType": "phone=name-eq-'iphone'"
},
"url": "https://myUrl/phone?$filter=phone=name-eq-'iphone'"
}
Related
I am trying to make the following call in React using axios:
axios.get(`http://localhost:5000/daily_batches/num_tweets_by_tag_and_date/`, {
params: {
tag: "Green/Sustainable Energy",
date: "2021-05-07"
}
})
.then(res => {
console.log(res)
})
Should I use req.params, req.body or req.query in my express route for the axios call to be successful? If I use req.params, like so:
router.route('/num_tweets_by_tag_and_date/').get(async (req, res) => {
try {
const tag = req.params.tag;
const date = req.params.date;
const tag_num_tweets_field = "tags." + tag + ".num_tweets"
const num_tweets_data = await DailyBatch.findOne({"date": date}, {[tag_num_tweets_field]: 1, "_id": 0});
res.json(num_tweets_data)
} catch (err) {
console.log(err)
res.status(400).json({
errors: {
global: "An error occurred."
}
})
}
});
I get a "data:null" as shown in this screenshot. And indeed by doing console.log(req.params.tag) and console.log(req.params.date) in the route I get "undefined"
If I use req.body.tag and req.body.date instead of req.params.tag and req.params.date, I still get "undefined" when I console.log(date) and console.log(tag). And the api response is still "data:null". However, in Insomnia the api call works just fine and returns the expected data, as shown in the pic below:
If I use req.query.date and req.query.tag, I successfully get the data in the axios api call I make in React. However, the call does not work in Insomnia, where I get "null".
I cannot seem to find a way to make the get call in React using axios and passing a dictionary with the call parameters, while at the same time being able to make calls to the same endpoint in Insomnia. The only way I found is to use req.param("tag") and req.param("date") but apparently it's deprecated and so I would not want to use that.
I have read multiple posts about req.params, req.body and req.query but the more I read about this the more I am getting confused. Any help would be appreciated!
Probably req.body.
req.params is used if you set up your route using variables, such as
router.get('/users/:id', (req, res)=>{})
you can console.log(req.body) to check.
req.params is used when you have dynamic route .
Example= /:num_tweets_by_tag_and_delete
check here: Node.js: req.params vs req.body
After searching for a product inside a search box, my app executes a get request on
"localhost/api/products?searchword".
Now i want to build an express api, that searches for the searchword inside my db.
How do i get access to the searchword?
I tried with req.query.name but it only works with a link like
"localhost/api/products?name=searchword".
How do i get access with my specific link
I have an express API endpoint that does exactly that.
I call it using this URL structure: /blog/search?q=some+search+string
export const blogSearch: RequestHandler = async (req, res) => {
const queryString = (req.query.q || "");
// ...
}
In order to use req.query, you should use the ?key1=value1,key2=value2 structure.
http://expressjs.com/en/api.html#req.query
If you want to manually parse the queryString, you can use req.url and you'll get the /api/products?searchword, so you can split it on the ? the read your searchWord.
Maybe someone will encounter the same problem, so i want to post my code that worked
router.get('/',
expressAsyncHandler( async (req, res)=> {
const name = req.url.split('?')[1] || "";
console.log(name);
const nameFilter= name ? { title: {$regex: name, $options: 'i'} } : {};
const products = await Product.find({
...nameFilter,
}).populate();
res.send(products);
})
);
I was expecting the delete call
const userDeleteHandler = (id: number) => {
axios.delete('/admin/deleteUser/', {params: {UserID: id}})
.then(response => {
console.log(response);
})
.catch(error => {
console.log(error);
})
};
to work, but got a consistent 404 error. After some digging here I tried
axios.delete('/admin/deleteUser/'+ id)
which worked. Why did my params attempt not work?
Here is my server.ts settings
import adminRoutes from './routes/admin';
const app = express();
app.options('*', cors()) // include before other routes
app.use(cors( {origin: true}));
app.use('/admin', adminRoutes);
and the routes in my routes/admin.ts
router.get('/users', getAllUsers);
router.post('/newUser', postNewUser);
router.patch('/editUser', editUser);
router.delete('/deleteUser/:UserID', deleteUser);
Axios places the params like a query string with question marks (?). Therefore by using params, you are sending a request to /admin/deleteUser/?{id} which is not found
Because api is not found => 404
router.delete('/deleteUser/:UserID', deleteUser);
(you getting value as a param but you sending value as a query. if you remove /:UserID then it will work)
You should call like
axios.delete(/admin/deleteUser/${id})
(if you call API like above then it will work try this it will work definitely )
the reason not working is, You calling it only endpoint you should call API with-param (UserId is the param, not a query, just learn how to sent param value and query value this will make you understand )
This get request - http://localhost:7000/api/search/gender/Female results in
{
"id": 32,
"email": "no"
},
Backend code:
const app = require('express').Router(),
db = require('../../../config/db'),
User = require('../../../config/User')
app.get('/search/:type/:value', async (req, res) => {
const { type, value } = req.params
module.exports = app
What you want is to set up your back end to read query string params and use those params to do your db filtering so you can make a single request to get whatever filtered data you need
Then your url would look something like:
/api/search?email=true&gender=female&city=athens
or whatever combination of params you need at the time
You should be able to pass objects to axios and it will serialze the front end params for you or use the URL API and specifically it's searchParams to construct url's
I'm trying to use Google Cloud Print(GCP) API, but I can't make it works.
Maybe I've understood bad the workflow because is the first time I'm using the google api, please help me to understand how to make it works.
Initial considerations:
I'm trying to implement it in reactJS, but It is indifferent because the logic to make GCP works is independent of the technology. Then you also can help me understand the workflow.
What exactly I want:
To make my first test, I am looking to get all information about my printer.
What I did:
I created a project in: https://console.developers.google.com
Inside the project created, I created a credential:
create credentials -> OAuth client ID
And I chose Application type: Web, and also configure the restrictions to source and redirection to my localhost.
Manually in https://www.google.com/cloudprint, I added my printer, I made a test printing a PDF and was OK.
I created a project in reactJS to get the information of my printer I've added.
Component:
Explanation:
I'm using a component react-google-login to obtain easily the user accessToken: https://github.com/anthonyjgrove/react-google-login
This component only obtains the access token and save it in localStorage, in a variable called googleToken and it draws a button to call a function to obtain the information about the printer.
code:
import React, { Component } from 'react'
import GoogleLogin from 'react-google-login';
import { connect } from 'react-redux'
import { getPrinters } from '../actions/settings'
class Setting extends Component {
responseGoogle(response) {
const accessToken = response.accessToken
localStorage.setItem('googleToken', accessToken)
}
render() {
return (
<div>
<GoogleLogin
clientId="CLIENT_ID_REMOVED_INTENTIONALLY.apps.googleusercontent.com"
buttonText="Login"
onSuccess={this.responseGoogle}
onFailure={this.responseGoogle}
/>
<button
onClick = {() => {
this.props.getPrinters()
}}
>test printer</button>
</div>
)
}
}
const mapStateToProps = state => {
return {
state: state
}
}
const mapDispatchToProps = dispatch => {
return {
getPrinters() {
dispatch(getPrinters())
}
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(Setting)
Action or Function to get information printer:
Explanation:
I'm passing the parameter printerid to get information about that printer.
In authorization, I'm using OAuth ... because in the documentation says that(second paragraph).: https://developers.google.com/cloud-print/docs/appInterfaces
The next two headers I wrote it because I tried solutions as:
Google Cloud Print API: User credentials required
Google Cloud Print User credentials required
code:
import axios from 'axios'
axios.defaults.headers.common['Authorization'] = 'OAuth ' + localStorage.getItem('googleToken')
axios.defaults.headers.common['scope'] = 'https://www.googleapis.com/auth/cloudprint'
axios.defaults.headers.common['X-CloudPrint-Proxy'] = 'printingTest'
const getPrinters = () => {
return () => {
return axios.get('https://www.google.com/cloudprint/printer'
, {
params: {
printeid: 'PRINTER_ID_REMOVED_INTENTIONALLY'
}
}
)
.then(response => {
console.log('response of google cloud print')
console.log(response)
})
}
}
export { getPrinters }
Error:
After all explained before, I got the next error:
User credentials required
Error 403
Note:
I'm using CORS plugin by recommendation of:
Chrome extensions for silent print?
because initially, I had cors error.
Any suggestion or recommendation would be very useful, thanks.
I've resolved my problem, my main problem about User Credential required were because I was using the incorrect access token and It was because I was getting the access token incorrectly.
I'm going to explain my whole solution because there are few examples of codes with this API.
Solutions:
The steps described were Ok until the fourth step where I used the external component react-google-login to trying to get the access token, instead I used googleapis module: Link Github googleapis
Also to avoid CORS problem(and not use CORS chrome plugin) I wrote the requests to Google API in server side.(NODEJS)
I had also a problem in the frontend when I tried to generate a popup to give permission for printer(problems about CORS), my solution was to use this very simple module for authentication: Link Github oauth-open
General scheme:
Explanation:
Knowing I have all data described in my question post(until the third step).
Authentication:
The next step in getting a URL and use it to the user can authenticate.
As I said before I used the module oauth-open in the frontend to generate the popup and only this module need the URL. To get the URL in the backend I used the endpoint /googleurl, where here I used the method generateAuthUrl of the module googleapis to generate the URL.
After that In the frontend, I got the authentication_code(that returned the module oauth-open), I send It to my endpoint /googletoken and here I process the authentication_code to generate access token, refresh token and expiration date with the method getToken of the module googleapis. Finally, these data are stored in the database.
Print:
For print, since the frontend, I send what data I need send to the printer. I used my endpoint /print
In the backend endpoint, my logic was the next:
Recover tokens and expiration date from database, with the expiration date check if the token has expired, and if It has already expired then gets another token and replace the old access token with the new one, replacing also with the new expiration date, to obtain this new data only is necessary call to method refreshAccessToken of module googleapis.Note: the refresh token never expires.
After having the access token updated, use it to send data to the printer with Google route(.../submit)
Code:
All the next codes are in only 1 file
Some data as validation, static variables, error handler, etc, has been removed to better understanding.
Route get URL authentication.
const express = require('express');
const google = require('googleapis');
const router = express.Router();
var OAuth2 = google.auth.OAuth2;
const redirect_url = 'http://localhost:3001/setting'; //Your redirect URL
var oauth2Client = new OAuth2(
'CLIENT ID', //Replace it with your client id
'CLIEND SECRET', //Replace it with your client secret
redirect_url
);
var url = oauth2Client.generateAuthUrl({
access_type: 'offline',
scope: 'https://www.googleapis.com/auth/cloudprint'
});
router.get('/googleurl', (req, res) => {
return res.status(200).send({
result: { googleURLToken: url }
});
});
To get tokens using the authentication code and save these in the database.
const Setting = require('../models/setting'); // My model(Mongoose)
router.post('/googletoken', (req, res) => {
oauth2Client.getToken(req.body.code, function (err, tokens) {
oauth2Client.credentials = tokens;
// If refresh token exits save it
// because the refresh token it returned only 1 time! IMPORTANT
if (tokens.hasOwnProperty('refresh_token')) {
let setting = new Setting();
setting.refreshTokenGoogle = tokens.refresh_token;
setting.expirationTokenGoogle = tokens.expiry_date;
setting.tokenGoogle = tokens.access_token;
setting.save()
.then((settingCreated) => {
return res.status(200).send({
message: 'OK'
});
})
}
});
});
To print
const axios = require('axios');
const moment = require('moment');
router.post('/print',async (req, res) => {
const tickeProperties = {
'version': '1.0',
'print': {
'vendor_ticket_item': [],
'color': { 'type': 'STANDARD_MONOCHROME' },
'copies': { 'copies': 1 }
}
};
const accessToken = await getTokenGoogleUpdated();
axios.get(
'https://www.google.com/cloudprint/submit',
{
params: {
printerid : printerID, // Replace by your printer ID
title: 'title printer',
ticket: tickeProperties,
content : 'print this text of example!!!',
contentType: 'text/plain'
},
headers: {
'Authorization': 'Bearer ' + accessToken
}
}
)
.then(response => {
return res.status(200).send({
result: response.data
});
})
}
);
async function getTokenGoogleUpdated() {
return await Setting.find({})
.then(async setting => {
const refreshTokenGoogle = setting[0].refreshTokenGoogle;
const expirationTokenGoogle = setting[0].expirationTokenGoogle;
const tokenGoogle = setting[0].tokenGoogle;
const dateToday = new Date();
// 1 minute forward to avoid exact time
const dateTodayPlus1Minute = moment(dateToday).add(1, 'm').toDate();
const dateExpiration = new Date(expirationTokenGoogle);
// Case date expiration, get new token
if (dateExpiration < dateTodayPlus1Minute) {
console.log('Updating access token');
oauth2Client.credentials['refresh_token'] = refreshTokenGoogle;
return await oauth2Client.refreshAccessToken( async function(err, tokens) {
// Save new token and new expiration
setting[0].expirationTokenGoogle = tokens.expiry_date;
setting[0].tokenGoogle = tokens.access_token;
await setting[0].save();
return tokens.access_token;
});
} else {
console.log('Using old access token');
return tokenGoogle;
}
})
.catch(err => {
console.log(err);
});
}
I hope It helps you if you want to use Google Cloud Print to not waste a lot of time as I did.
The important part there is a scope https://www.googleapis.com/auth/cloudprint which is not obvious and took one day for me to figure out.