Trying to use some url parameters in an API call. Eventually my plan is to allow the user to put in a Zip Code and I will transform the zip code into latitude and longitude. This is intended to be a digital signage solution where the user can set up a URL in their signage system and include their location as part of the url.
Here's what I have for my created function in Vue. Admittedly, I am both a novice at Vue and still trying to fully understand async await. I thought that I did, but this is inconsistently not waiting for the latitude and longitude params from the query before sending the API call.
async created() {
const lat = await this.$route.query.lat;
const lon = await this.$route.query.lon;
try {
const response = await axios.get(
'https://api.openweathermap.org/data/2.5/onecall?lat=' + lat + '&lon=' + lon + '&units=imperial&exclude=minutely,alerts&appid=' + process.env.VUE_APP_APIKEY,
{ headers: {"Content-Type": "application/json"} }
)
this.weather = response.data
} catch (err) {
console.log(err.toJSON());
}
}
Current Updated code using setup
setup() {
const route = useRoute();
let lat = ref(0);
let lon = ref(0);
let weather = ref(null);
watchEffect(() => {
lat.value = route.query.lat;
console.log(lat.value);
lon.value = route.query.lon;
console.log(lon.value);
axios.get(
'https://api.openweathermap.org/data/2.5/onecall?lat=' + lat.value + '&lon=' + lon.value + '&units=imperial&exclude=minutely,alerts&appid=' + process.env.VUE_APP_APIKEY,
{ headers: {"Content-Type": "application/json"} }
).then( response => {
weather.value = response.data;
console.log(weather);
}).catch(e => {
console.log(e);
});
});
return {
lat, lon, weather
}
},
I would recommend doing this in either the watch function (both in the options and the composition API) or a watchEffect (only in the composition API), the latter is less complicated. It just listens to changes on reactive properties.
<script setup>
import { useRoute } from 'vue-router';
import { ref, watchEffect } from 'vue';
const route = useRoute();
const lat = ref(0);
const long = ref(0);
watchEffect(() => {
lat.value = route.query.lat;
lng.value = route.query.lng;
});
</script>
You will need to better safeguard the assignments!
Related
I have followed through the following post on how to set up Twilio video API with javascript: https://www.twilio.com/docs/video/tutorials/get-started-with-twilio-video-node-express-server
This works fine but I am now running into problems trying to convert this code to work with VueJS. Everything seems to run and I see the HTML appended for the audio and video, however no video element appears on the page. How do I fix this
This is the code I have written/adapted
<script setup>
import {ref} from "vue";
const isFormVisible = ref(true);
const roomName = ref("");
const participants = ref([]);
import Twilio, { connect, createLocalTracks, createLocalVideoTrack } from 'twilio-video'
const startRoom = async (event) => {
console.log('started');
isFormVisible.value = false;
const response = await fetch("http://localhost:3000/join-room", {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify({ roomName: roomName.value }),
});
const { token } = await response.json();
console.log(token);
// join the video room with the token
const room = await joinVideoRoom(roomName.value, token);
// render the local and remote participants' video and audio tracks
handleConnectedParticipant(room.localParticipant);
room.participants.forEach(handleConnectedParticipant);
room.on("participantConnected", handleConnectedParticipant);
room.on("participantDisconnected", handleDisconnectedParticipant);
window.addEventListener("pagehide", () => room.disconnect());
window.addEventListener("beforeunload", () => room.disconnect());
}
const handleDisconnectedParticipant = (participant) => {
// stop listening for this participant
participant.removeAllListeners();
// remove this participant's div from the page
//const participantDiv = document.getElementById(participant.identity);
//participantDiv.remove();
};
const handleConnectedParticipant = (participant) => {
// create a div for this participant's tracks
participants.value.push(participant);
// iterate through the participant's published tracks and
// call `handleTrackPublication` on them
participant.tracks.forEach((trackPublication) => {
handleTrackPublication(trackPublication, participant);
});
// listen for any new track publications
participant.on("trackPublished", handleTrackPublication);
};
const handleTrackPublication = (trackPublication, participant) => {
function displayTrack(track) {
// track.attach creates an HTMLVideoElement or HTMLAudioElement
// (depending on the type of track) and adds the video or audio stream
if(!participant.track) {
participant.track = track.attach().outerHTML;
}
else {
participant.track += track.attach().outerHTML;
}
console.log(track.attach().outerHTML);
console.log(track.attach());
}
// check if the trackPublication contains a `track` attribute. If it does,
// we are subscribed to this track. If not, we are not subscribed.
if (trackPublication.track) {
displayTrack(trackPublication.track);
}
// listen for any new subscriptions to this track publication
trackPublication.on("subscribed", displayTrack);
};
const joinVideoRoom = async (roomName, token) => {
// join the video room with the Access Token and the given room name
let connectOptions = {
name: roomName,
// logLevel: 'debug',
audio: true,
video: { width: 400 }
};
const room = await Twilio.connect(token, connectOptions);
return room;
};
</script>
ive got a page which i call an api through getserversideprops to basically get all the data from a table. it used to work fine, but suddenly when i runned it today an error occured which i dont know the cause is. this is the response im getting when i console log the response
what i tried on my own and noticed to "help" was reducing the amount of columns that the API was selecting. For the api i used knex and express. this was the original code that used to work but now does not.
try{
let data = "";
await db.transaction(async (t)=>{
data = await db('sales').transacting(t)
.join('users','users.id','sales.cashier_id')
.join('customer','customer.customer_id','sales.customer_id')
.select('customer.name as customer_name','sales.sales_id','sales.date','sales.payment_type','sales.payment_status','sales.customer_id','sales.transaction_total','sales.total_paid','users.name','sales.shipment_fee','sales.days','sales.sale_type')
})
return data
}catch(e){
console.log(e);
throw e;
}
what i tried was reducing the amount of select columns to i think 5 or 6, but definitely if the amount of columsn i use is under a limit it works. like
try{
let data = "";
await db.transaction(async (t)=>{
data = await db('sales').transacting(t)
.join('users','users.id','sales.cashier_id')
.join('customer','customer.customer_id','sales.customer_id')
.select('customer.name as customer_name','sales.sales_id','sales.transaction_total','sales.date','sales.payment_type')
})
return data
}catch(e){
console.log(e);
throw e;
}
these are the attributes of my table
this is the server.js file of my expressjs
const compression = require('compression');
const express = require("express");
const app = express();
const fs = require('fs');
const http = require('http')
const https = require('https')
const PORT = process.env.PORT || 8000;
var cors = require('cors')
const mainRoutes = require('./api/v1/routes/index')
const cookieParser = require("cookie-parser");
app.use(compression());
app.use(cors( {origin: 'http://localhost:3000',credentials: true}))
// app.use(cors());
app.use(express.json());
// app.use(express.urlencoded({ extended: true }));
app.use(cookieParser());
// Main Routes
app.use("/api/v1", mainRoutes,(req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
next();
});
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}.`);
});
this is how im calling the api from next
export async function getServerSideProps(context) {
try{
const res = await axios.get("/sales/get-all");
const data = await res.data
return { props: { data } }
}catch(err){
const data = "error"
return { props: { data} }
}
}
where i declared default url of the axios at the app.js file of next
import '../styles/globals.css'
import axios from 'axios';
axios.defaults.baseURL = "http://localhost:8000/api/v1/"
function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}
export default MyApp
im not quite sure what the problem is so pardon if the title of the question is not according to the problem i have.
EDIT:
i moved the api to a useeffect call, its now working. but why isnt it working in getserversideprops?
I've this code to get nearby places and nearby beaches from a point, with Google maps. This is called from a Next.js component, via the useSWR hook.
All the data is returned correctly, but before first Axios call (const fetchNearbyPlaces = async (urlWithToken = null) => {...), I'm receiving this error in the console:
API resolved without sending a response for /api/google/places/33.807501/-78.70039, this may result in stalled requests.
I can't figure out what the error is, although there may be several because I'm a novice. I appreciate any suggestion.
const axios = require("axios");
const GetNearbyPlaces = async (req, res) => {
const {
latitude,
longitude,
} = req.query;
const radius = 50000;
const types = [
"airport",
"tourist_attraction",
"amusement_park",
"aquarium",
"art_gallery",
"bar",
"museum",
"night_club",
"cafe",
"restaurant",
"shopping_mall",
"store",
"spa",
];
function checkFunc(arr, val) {
return arr.some(arrVal => val === arrVal);
}
const url = `https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=${latitude}%2C${longitude}&radius=${radius}&key=${process.env.CW_GOOGLE_MAPS_API_KEY}`;
const beachesUrl = `https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=${latitude}%2C${longitude}&radius=${radius}&type=natural_feature&key=${process.env.CW_GOOGLE_MAPS_API_KEY}`;
try {
let results = [];
let beaches = [];
const fetchNearbyBeaches = async (urlWithToken = null) => {
await axios.get(urlWithToken ? urlWithToken : beachesUrl).then(data => {
beaches = [...beaches, ...data.data.results];
if (data?.data?.next_page_token) {
const newUrl = `https://maps.googleapis.com/maps/api/place/nearbysearch/json?key=${process.env.CW_GOOGLE_MAPS_API_KEY}&pagetoken=${data.data.next_page_token}`;
setTimeout(() => {
fetchNearbyBeaches(newUrl);
}, 2000);
} else {
beaches.length > 5 && beaches.splice(5);
results.length > 5 && results.splice(5);
const finalResults = [...beaches, ...results];
finalResults.length > 10 && finalResults.splice(10);
return res.status(200).json({
data: {
results: finalResults,
},
success: true,
});
}
});
};
const fetchNearbyPlaces = async (urlWithToken = null) => {
await axios.get(urlWithToken ? urlWithToken : url).then(data => {
results = [...results, ...data.data.results];
if (data?.data?.next_page_token) {
const newUrl = `https://maps.googleapis.com/maps/api/place/nearbysearch/json?key=${process.env.CW_GOOGLE_MAPS_API_KEY}&pagetoken=${data.data.next_page_token}`;
setTimeout(() => {
fetchNearbyPlaces(newUrl);
}, 2000);
} else {
const dirtyResultsWithDuplicates = [];
results.map(result => {
return types.map(type => {
if (checkFunc(result.types, type) && !result.types.includes("lodging")) {
dirtyResultsWithDuplicates.push(result);
}
});
});
const set = new Set(dirtyResultsWithDuplicates);
const filtered = Array.from(set);
results = filtered.length > 10 ? filtered.splice(10) : filtered;
return fetchNearbyBeaches();
}
});
};
fetchNearbyPlaces();
} catch (err) {
res.status(500).json({
message: err.message,
statusCode: 500,
});
}
};
export default GetNearbyPlaces;
The problem is with the backend application not the frontend component.
Nextjs expects a response to have been sent when the api handler function exits. If for example you have a databaseCall.then(sendResponse) in your api handler function what happens is that the handler function exits before the database returns.
Now this is not a problem if the database does return after that and sends the response, but it is if for example the database has an error. Because the handler function exits without a response already being sent Nextjs can't be sure that at that point there isn't a stalled request.
One way to fix this is by await-ing the db call(or whatever other async function you call) thereby preventing the handler function from exiting before some kind of response has been send.
The solution was added this object to mi API code.
export const config = {
api: {
externalResolver: true,
},
};
Documentation: https://nextjs.org/docs/api-routes/request-helpers
I'm using a static array to scaffold a user table, prior to refactoring with actual postgres db and some fetch()-ing code. At present, the tests work, but obviously they are working synchronously. Here's the placeholder API code:
// UserAPI.js
let findUserById = (credentials = {}) => {
const { userId } = credentials
if (userId) {
const foundUser = users.find(user => user.id === userId)
if (foundUser !== undefined) {
const { password: storedpassword, ...user } = foundUser
return user
}
}
return null
}
exports.byId = findUserById
And an example test as follows:
// excerpt from TokenAuth.test.js
const UserAPI = require('../lib/UserAPI')
describe('With TokenAuth middleware', () => {
beforeEach(() => {
setStatus(0)
})
it('should add user to req on authorised requests', () => {
const token = createToken(fakeUser)
const authReq = { headers: { authorization: 'Bearer ' + token } }
const myMiddleware = TokenAuth(UserAPI.byId)
myMiddleware(authReq, fakeRes, fakeNext)
// expect(authReq.user).toStrictEqual({ id: 1, username: 'smith#example.com' });
expect(authReq.user.username).toStrictEqual('smith#example.com')
expect(authReq.user.id).toStrictEqual(1)
})
})
This runs fine, and along with other tests gives me the coverage I want. However, I now want to check that the tests will deal with the async/await nature of the fetch() code I'm going to use for the proper UserAPI.js file. So I re-write the placeholder code as:
// UserAPI.js with added async/await pauses ;-)
let findUserById = async (credentials = {}) => {
const { userId } = credentials
// simulate url resolution
await new Promise(resolve => setTimeout(() => resolve(), 100)) // avoid jest open handle error
if (userId) {
const foundUser = users.find(user => user.id === userId)
if (foundUser !== undefined) {
const { password: storedpassword, ...user } = foundUser
return user
}
}
return null
}
exports.byId = findUserById
... at which point I start getting some lovely failures, due I think it's returning unresolved promises.
My problem is two-fold:
How should I alter the UserAPI.test.js tests to deal with the new async nature of findUserByCredentials() ?
Am I ok in my assumption that ExpressJS is happy with async functions as request handlers? Specifically, due to the async nature ofUserAPI.findUserByCredentials is this ok?
Main App.js uses curried UserAPI.byId() for the findUserById.
// App.js (massively simplified)
const express = require('express')
const TokenAuth = require('./middleware/TokenAuth')
const RequireAuth = require('./middleware/RequireAuth')
const UserAPI = require('./lib/UserAPI')
let router = express.Router()
const app = express()
app.use(TokenAuth(UserAPI.byId))
app.use(RequireAuth)
app.use('/users', UserRouter)
module.exports = app
My TokenAuth middleware would now run along these lines:
// TokenAuth.js (simplified)
const jwt = require('jsonwebtoken')
require('dotenv').config()
const signature = process.env.SIGNATURE
let TokenAuth = findUserById => async (req, res, next) => {
let header = req.headers.authorization || ''
let [type, token] = header.split(' ')
if (type === 'Bearer') {
let payload
try {
payload = jwt.verify(token, signature)
} catch (err) {
res.sendStatus(401)
return
}
let user = await findUserById(payload)
if (user) {
req.user = user
} else {
res.sendStatus(401)
return
}
}
next()
}
module.exports = TokenAuth
A partial answer us simply to add an async/await on the middleware call:
it('should add user to req on authorised requests', async () => {
const token = createToken(fakeUser)
const authReq = { headers: { authorization: 'Bearer ' + token } }
const myMiddleware = TokenAuth(UserAPI.byId)
await myMiddleware(authReq, fakeRes, fakeNext)
// expect(authReq.user).toStrictEqual({ id: 1, username: 'smith#example.com' });
expect(authReq.user.username).toStrictEqual('smith#example.com')
expect(authReq.user.id).toStrictEqual(1)
})
I'm developing an app for Shopify. Currently under development stage. Until now, I have successfully managed to authorise the app and then redirect it back to admin page using the Embedded App SDK. However, when I return to the admin page, it gives me an error saying Request origin cannot be verified.
The console shows Failed to load resource: the server responded with a status of 403 (Forbidden)
The URL in the console is something like this https://myshop.myshopify.com/admin/apps/dfdjf4343343434343434bfdf/shopify/shopify/callback?code=ffdfdffd&hmac=fdfdfdfdfdfdfdfdfddfdfdfdfdf&shop=myshop.myshopify.com&state=151193864548800×tamp=1511938648
The fdfdfdfdfdfdfdfdfddfdfdfdfdf are just random characters that I've replaced instead of a hash. FYI - I've removed the app name and user profile name and avatar from the image.
This is happening because, you are unable to match state, that is set in cookie, while responding with redirect url
const ShopifyToken = require('shopify-token')
const forwardingAddress = process.env.HOST
const shopifyToken = new ShopifyToken({
sharedSecret: process.env.SHOPIFY_API_SECRET,
redirectUri: forwardingAddress + '/shopify/callback',
apiKey: process.env.SHOPIFY_API_KEY
})
const shopify = {
// use this for authentication
auth: (req, res, next) => {
const shop = req.query.shop
if (!shop) {
return res.status(400).send('Missing shop parameter. Please add ?shop=your-development-shop.myshopify.com to your request')
}
const shopRegex = /^([\w-]+)\.myshopify\.com/i
const shopName = shopRegex.exec(shop)[1]
const state = shopifyToken.generateNonce()
const url = shopifyToken.generateAuthUrl(shopName, scopes, state)
res.cookie('state', state)
res.redirect(url)
},
// use this as your callback function
authCallback: async (req, res) => {
const { shop, hmac, code, state } = req.query
const stateCookie = cookie.parse(req.headers.cookie).state
if (state !== stateCookie) {
// you are unable to set proper state ("nonce") in this case, thus you are getting this error
return res.status(403).send('Request origin cannot be verified')
}
if (!shop || !hmac || !code) {
res.status(400).send('Required parameters missing')
}
let hmacVerified = shopifyToken.verifyHmac(req.query)
console.log(`verifying -> ${hmacVerified}`)
// DONE: Validate request is from Shopify
if (!hmacVerified) {
return res.status(400).send('HMAC validation failed')
}
const accessToken = await shopifyToken.getAccessToken(shop, code)
const shopRequestUrl = 'https://' + shop + '/admin/shop.json'
const shopRequestHeaders = {
'X-Shopify-Access-Token': accessToken
}
try {
const shopResponse = await request.get(shopRequestUrl, { headers: shopRequestHeaders })
res.status(200).end(shopResponse)
} catch (error) {
res.status(error.statusCode).send(error.error.error_description)
}
}
}
Simple as this is, also make sure that the protocol matches from what you typed in to start the app install.
If you accidentally use http for http://you.ngrok.io/ but your callback redirects to https (i.e. https://you.ngrok.io/auth/callback), the OAuth handshake will fail.
const express = require('express');
const router = express.Router();
const dotenv = require('dotenv').config();
const cookie = require('cookie');
const requestPromise = require('request-promise');
const ShopifyToken = require('shopify-token');
const scopes = "write_products";
const forwardingAddress = process.env.HOST;
var shopifyToken = new ShopifyToken({
sharedSecret: process.env.SHOPIFY_API_SECRET,
redirectUri: forwardingAddress + '/shopify/callback',
apiKey: process.env.SHOPIFY_API_KEY
})
router.get('/shopify', (req, res) => {
const shop = req.query.shop;
if (!shop) {
return res.status(400).send('Missing shop parameter. Please add ?shop=your-development-shop.myshopify.com to your request')
}
const shopRegex = /^([\w-]+)\.myshopify\.com/i
const shopName = shopRegex.exec(shop)[1]
const state = shopifyToken.generateNonce();
const url = shopifyToken.generateAuthUrl(shopName, scopes, state);
res.cookie('state', state);
res.redirect(url);
});
router.get('/shopify/callback', (req, res) => {
const { shop, hmac, code, state } = req.query;
const stateCookie = cookie.parse(req.headers.cookie).state;
if (state !== stateCookie) {
// you are unable to set proper state ("nonce") in this case, thus you are getting this error
return res.status(403).send('Request origin cannot be verified')
}
if (!shop || !hmac || !code) {
res.status(400).send('Required parameters missing')
}
let hmacVerified = shopifyToken.verifyHmac(req.query)
console.log(`verifying -> ${hmacVerified}`)
// DONE: Validate request is from Shopify
if (!hmacVerified) {
return res.status(400).send('HMAC validation failed')
}
const accessToken = shopifyToken.getAccessToken(shop, code);
const shopRequestUrl = 'https://' + shop + '/admin/products.json'
const shopRequestHeaders = {
'X-Shopify-Access-Token': accessToken
}
try {
const shopResponse = requestPromise.get(shopRequestUrl, { headers: shopRequestHeaders })
res.status(200).send(shopResponse)
} catch (error) {
res.status(error.statusCode).send(error.error.error_description)
}
});
module.exports = router;