is hapi's server.inject production safe? - hapi.js

I have encountered server.inject in test examples, but not in production code example.
I have a route that needs to execute something that is handled by another route, so I need to make an internal call.
handler: (req, res) => {
const OPTIONS = {
method: 'POST',
path: '/path'
payload: {PAYLOAD_HERE}
}
server.inject(OPTIONS, (err, response) => {
res(response)
});
}
is this okay?

Yes. I've done exactly that in a large scale production system.

Related

Next.js 500 error and Axios error when calling API that uses environment variables

I'm building a wine pairing app in Next.js—where when a user clicks on a type of wine (i.e. chardonnay), it calls an API to return the suggested food pairings. This works with no issue when I use NEXT_PUBLIC for my environment variables, but I don't want to expose my private API key.
This is my first time using server-side environment variables with Next.js I understand that this needs to happen within the pages/api folder. API routing with Next.js is still something that I'm learning, so I've been following the docs, and I also found this tutorial which I followed that resulted in the 500 (Internal Server Error). I'm also getting an Axios error. This is a screenshot of both errors—please let me know if anything should be expanded, and I'll post another screenshot.
I also understand that I can use getStaticProps(), but this call is coming from a component rather than a page, and I understand from the docs that getStaticProps() must be called from a page.
This is what my .env.local looks like:
API_KEY=<my api key>
BASE_URL=https://api.spoonacular.com/
This is what my API call looks like (pages/api/wineWithFood.js)
import axios from 'axios';
export default async function wineWithFood(req, res) {
const {
query: { wine },
} = req;
const url = `${process.env.BASE_URL}food/wine/dishes?wine=${wine}&apiKey=${process.env.API_KEY}`;
const response = await axios.get(url);
res.status(200).json({
data: response.data,
});
}
This is the relevant code for what that call looks like in my component which is properly imported into the page where it belongs:
const getPairing = async () => {
axios.get(`/api/wineWithFood?wine=${wine}`, {
headers: {
Accept: 'application/json',
'Access-Control-Allow-Origin': '*',
},
})
.then((response) => response)
.then((response) => {
setData(response.data.pairings)
})
.catch((err) => console.log(err))
}
const handleChange = (e) => {
e.preventDefault();
setWine(e.target.value);
getPairing();
};
console.log(wine)
I see that the request isn't capturing the wine type, but when I console.log the wine, it's showing up in the browser console as expected. When I console.log the response.data from the API call, I get a status code of 400 with a message stating that the wine must not be empty.
Now, if I change my code to the following—I get the same errors and console.logs as I mentioned... but only on the first try! On the second try (clicking the same exact wine), the wine shows correctly in the browser console since I'm console.loging it, but I get that same 500 error in my console, however, now I can see all of the correct data in my terminal! That leads me to believe I'm doing something wrong on the frontend. Here's the tweaked code that results in this:
const getPairing = async (wine) => {
axios
.get(`/api/wineWithFood?wine=${wine}`, {
headers: {
Accept: 'application/json',
'Access-Control-Allow-Origin': '*',
},
})
.then((response) => response)
.then((response) => {
setData(response.data.pairings);
console.log(response.data.pairings);
})
.catch((err) => console.log(err));
};
const handleChange = (e) => {
e.preventDefault();
setWine(e.target.value);
getPairing(wine);
};
I'm happy to check out any other resources to help me out if that's a better answer to this question.
I finally figured this out and wanted to share the answer—which was staring me in the face.
I left my API call in pages/api/wineWithFood.js the same. I was right. The error was on the frontend. I got rid of the getPairing() function and put everything in the handleChange function. When I console logged my response on the frontend, I realized that the info I needed was res.data.data.pairings. I also changed the axios call by using e.target.value as the search query. I removed wine and setWine since it wasn't necessary. Here's the final code:
const handleChange = (e) => {
e.preventDefault();
axios
.get(`/api/wineWithFood?wine=${e.target.value}`, {
headers: {
Accept: 'application/json',
'Access-Control-Allow-Origin': '*',
},
})
.then((res) => {
setData(res.data.data.pairings);
});
};
I hope this can help someone out—also I'm open to feedback if there's a better way.

How to test `verify` of an express middleware in Jest

I have a function which returns a middleware as such:
const jsonParser = () => {
return express.json({
limit: '5mb',
verify: (req, res, buf) => {
// If the incoming request is a stripe event,
if (req.headers['some-header']) {
httpContext.set('raw-body', buf.toString());
}
},
});
};
I would like to test that the httpContext.setis indeed called when the some-header header is present.
My test:
describe('jsonParser middleware', () => {
it('sets the http context', async () => {
const req = {
headers: {
'some-header': 'some-sig',
'content-type': 'application/json',
},
body: JSON.stringify({
some: 'thing',
}),
};
const res = {};
const middleware = jsonParser();
middleware(req, res, () => {});
expect(httpContext.set).toHaveBeenCalled();
});
});
I have no idea how to make the test run the function passed to verify. Express docs state that the content type should be json, but nothing more. Anyone that can point me in the right direction is highly appreciated.
Thank you.
as mentioned in the comments i want to give you an example of an integration test which tests the header and jsonwebtoken. i am also using the express framework but i wrote my code in JS.
this is a test for creating a forumpost in a forum i built. a middleware is checking for the token of the user so this case could be similiar to yours.
const request = require('supertest');
test('create authorized 201', async () => {
const forumCountBefore = await ForumPost.countDocuments();
const response = await request(app)
.post('/api/forumPosts')
.set({
Authorization: `Bearer ${forumUserOne.tokens[0].token}`,
userData: {
userId: forumUserOneId,
email: 'forum#controller.com',
username: 'forum',
},
})
.send(forumPost)
.expect(201);
expect(response.body.message).toBe('created forumPost');
const forumCountAfter = await ForumPost.countDocuments();
expect(forumCountBefore + 1).toBe(forumCountAfter);
});
i am using mongoDB thats why i use ForumPost.countDocuments to count the amount of entries in the DB.
as you can see in the test i use supertest (imported as request) to send an http call. in the set block i set the authorization token. this causes the middleware to be executed in the integration test.
the test can only pass when the code of the middleware gets executed correctly so it should cover the code of your middleware.

globalize axios as API wrapper in vue project

I have almost 13 Axios requests in my Vue application. which are almost the same
axios({
method: 'post',
url: `${this.$root.api_url}/v2/cameras/${this.selected.exid}/nvr/snapshots/extract`,
data: {
start_date: moment(this.fromDateTime).format(),
end_date: moment(this.toDateTime).format(),
schedule: this.schedule,
interval: this.interval,
create_mp4: this.create_mp4,
inject_to_cr: this.inject_to_cr,
jpegs_to_dropbox: this.jpegs_to_dropbox,
requester: this.$root.user.email,
api_key: this.selected.api_key,
api_id: this.selected.api_id
}
}).then(response => {
if (response.status == 201) {
this.showSuccessMsg({
title: "Success",
message: "Snapshot Extractor has been added (Local)!"
});
this.$events.fire('se-added', {})
this.clearForm()
} else {
this.showErrorMsg({
title: "Error",
message: "Something went wrong!"
})
}
})
I pass the method, URL and data.. and do a few things in response and in case of error.
How can I reduce that so much code? I have this idea to make an API file for this where, the method will accept, API.get(method, URL, data) and I will have {message, statusCode} in return. and then on the basis of that, I can do other stu7ff.
I tried to follow some documentation online but it didn't work. Is there any suitable way to reduce this code.
Is it even possible to give success and error message as well in API.get or post or delete that it would be very minimal when you send the API request?
EDIT: so i guess you need something like a class here:
class API {
static get(url, callback) {
axios({
method: "get",
url: url,
data: data
}).then(response => {
callback(response);
});
}
static post(url, data, callback) {
axios({
method: "post",
url: url,
data: data
}).then(response => {
callback(response);
});
}
}
API.post("url", data, response => {
console.log(response);
});
API.get("url", response => {
console.log(response);
});
I use yamlful
You make a .yml file which includes
events:
- method: get
get: /events/:id
then API calls become
const response = await this.$api.events.get(2)
Furthermore, I inject methods into my context
// api.js
async function populateEvents (app, id) {
const response = await app.$api.events.get(id)
return response
}
export default ({ app, store }, inject) => {
inject('populateEvents', id => populateEvents(app, id))
}
// any_file.vue
this.populateEvents(12)
and in api.js you can generalize your api calls, so if any 2 api calls do the same stuff, you can refactor that repeated code into a separate method

handle network request failed in react native

I'm facing an issue while using react native fetch api. many times request got failure . I have a high speed connection. but many times it got failed.
that issue is happening In android,ios both.
const shoppingApi = 'myserverlink';
async function Sendshoppinapi(data) {
try {
let response = await fetch(shoppingApi, {
method: 'POST',
headers: {
'Accept': 'application/json',
'content-type':'multipart/form-data'
},
body: data
});
let responseJson = await response.json();
return responseJson;
}
catch (error) {
Alert.alert(error.toString())
}
}
export {Sendshoppinapi};
data that I sending server as post request
add_to_wishlist = (item,index) => {
{
let data = new FormData();
data.append('methodName', 'add_to_wishlist');
data.append('user_id', global.userid)
data.append('item_id', this.props.navigation.state.params.itemid.toString())
Sendshoppinapi(data).then((responseJson)=>{
console.warn(responseJson);
if(responseJson.responseCode == '200'){
this.setState({fav:false})
Alert.alert('SHOPPING','Item added to wishlist successfully.',[{text: 'OK',},],{ cancelable: false })
}
else{
this.setState({fav:false})
Alert.alert('SHOPPING','Item already .',[{text: 'OK',},],{ cancelable: false })
}
})}
}
Error that when request got failed
I've quoted an answer I used for another post - however I have added await.
You can check the status of the call, to determine perhaps why the network call failed. Try using fetch's ok to check whether the response was valid, for example:
.then(function(response) {
if (!response.ok) {
//throw error
} else {
//valid response
}
})
Using await:
let response = await fetch(url)
if (response.ok) return await response.json()
You can also access the response's status like:
response.status;
or also, statusText such as:
response.statusText;
checkout the below:
https://developer.mozilla.org/en-US/docs/Web/API/Response/statusText
https://developer.mozilla.org/en-US/docs/Web/API/Response/status
https://www.tjvantoll.com/2015/09/13/fetch-and-errors/
Use then() function with promises. (Requested code snippet)
fetch(shoppingApi, {
method: 'POST',
headers: {
'Accept': 'application/json',
'content-type':'multipart/form-data'
},
body: data
})
.then((resp) => {
return resp.json()
})
.then((resp) => {
//resp contains your json data
});
You also can make your function returns a Promise, and use it with then():
function sendShoppingApi(data) {
return new Promise((resolve, reject) => {
fetch(shoppingApi, {
method: 'POST',
headers: {
'Accept': 'application/json',
'content-type':'multipart/form-data'
},
body: data
})
.then((resp) => {
return resp.json();
})
.then((resp) => {
resolve(resp);
/*
you should also check if data is valid, if something went wrong
you can reject the promise:
if(!dataOK)
reject("error message");
*/
});
});
}
So now you can do something like this:
sendShoppingApi(data)
.then((resp) => {
//do stuff with your data
})
.catch((err) => {
//handle error
});
UPDATE
could be a duplicate of this: React Native fetch() Network Request Failed
For the case when you are running the app on the android device, the API is on a computer and both of them are on the same network I have added some possible things to check. I haven't detailed specific solutions since there are many answers on each topic.
Do a quick check with ngrok https://ngrok.com/ on the free plan to see if that works. If yes:
Make sure the API is accessible by trying to access it on the device browser (most important is to check if you allow the port at inbound rules, firewall).
If you are using HTTPS, you might get an error if your react native env is not properly configured to accept not trusted certificates, assuming you are using a non trusted one. Do a check without HTTPS, only with HTTP, to see if it's the case. https://github.com/facebook/react-native/issues/20488

Calling server.inject() POST request not calling handler in Hapi.js

I have a Jasmine test spec test_spec.js like this:
describe('my tests', () => {
it('POST should return 201 created', () => {
var req = {
method: 'POST',
url: '/api/v1.0/message',
payload: JSON.stringify({name: 'Ethan'})
};
server.inject(req, res => {
expect(res.statusCode).to.equal(201);
});
});
});
The route for the API call looks like this:
var routes = [{
path: '/api/v1.0/message',
method: 'POST',
handler: function(request, reply) {
reply('Success').created();
}
}];
exports.register = function(server, options, next) {
server.route(routes);
next();
}
When I run the tests, though, this particular test's expect() function doesn't get called because the server.inject() method doesn't call the response callback. In fact, not even the route handler method gets called (I checked with console.log statements). However, when I change the request method and the route from POST to GET, it works and the test calls the expect() method as expected. The test just doesn't work with POST requests. Am I doing it wrong?
Turns out that the problem was in the test call describe() snippet posted in my question. I neglected to call the done() function inside the server.inject() call. Once I added that, the POST test started getting called:
describe('my tests', () => {
it('POST should return 201 created', (done) => {
var req = {
method: 'POST',
url: '/api/v1.0/message',
payload: JSON.stringify({name: 'Ethan'})
};
server.inject(req, res => {
expect(res.statusCode).toEqual(201);
done();
});
});
});
The need to call the done() callback wasn't obvious to me from the Jasmine documentation. The call is necessary in order to postpone the spec completion until done() is called (meaning payload is posted).