Next JS How can i set cookies in an api without errors? - cookies

Next JS. I am trying to set some cookies in my /api/tokencheck endpoint. Here is a very simplified version of the code:
import { serialize } from 'cookie';
export default (req, res) => {
/* I change this manually to simulate if a cookie is already set */
let cookieexists = 'no';
async function getToken() {
const response = await fetch('https://getthetokenurl');
const data = await response.json();
return data.token;
}
if (cookieexists === 'no') {
getToken().then((token) => {
res.setHeader('Set-Cookie', serialize('token', token, { path: '/' }));
});
return res.status(200).end();
} else {
return res.status(200).end();
}
};
I have tried a ton of variations as to where to put my return.res.status... code, and tried many different ways to return a success code, but depending on where I put the code I variously end up with either of the following errors:
"API resolved without sending a response for /api/checkguestytoken, this may result in stalled requests."
or
"unhandledRejection: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client"
I seem to have some gap in my knowledge about how the API works in Next JS because I cannot figure out how to just run the async function, get a result, set a couple of cookies and then exit with a 200. Could someone please tell me what I'm doing wrong?

Related

Unable to set cookies on server side with Sveltekit

the title speaks for itself: From one moment to another, I am unable to set cookies from the server page of my Sveltekit project.
In the +page.server.js of my page I have an action in which I am setting a cookie with the set function. After that, when reloading the page, I am trying to retrieve the cookie with the get function inside the load function, but the cookie is undefined. I am quite convinced that it is not set at all, but I don't know why.
export const load = async ({ cookies }) => {
const myCookie = cookies.get('myCookie');
}
export const actions = {
default: async ({ cookies }) => {
//stuff
cookies.set('myCookie', true);
throw redirect(303, '/');
}
}
I specify that this is a procedure I have already done in other projects and it has always worked. What could be the problem? Thank you for your help.
UPDATE:
I don't know if it helps, but I want to specify that I am using Pocketbase and that a cookie is set in the hook.server.js of the project, which I can see in the browser and works correctly:
export async function handle({ event, resolve }) {
event.locals.pb = new PocketBase('http://127.0.0.1:8090');
event.locals.pb.authStore.loadFromCookie(event.request.headers.get('cookie') || '');
if (event.locals.pb.authStore.isValid) {
event.locals.user = serializeNonPOJOs(event.locals.pb.authStore.model);
} else {
event.locals.user = undefined;
}
const response = await resolve(event, {
transformPageChunk: ({ html }) => minify(html, minification_options)
})
response.headers.set('set-cookie', event.locals.pb.authStore.exportToCookie({ secure: false }));
return response
}

PayloadTooLargeError for Expo in React Native

What is causing the PayloadTooLargeError error? I get it sometimes and also when the payload is a few KB (as far as I can figure out).
PayloadTooLargeError: request entity too large
at readStream (/usr/local/lib/node_modules/expo-cli/node_modules/#expo/dev-server/node_modules/raw-body/index.js:155:17)
at getRawBody (/usr/local/lib/node_modules/expo-cli/node_modules/#expo/dev-server/node_modules/raw-body/index.js:108:12)
at read (/usr/local/lib/node_modules/expo-cli/node_modules/#expo/dev-server/node_modules/body-parser/lib/read.js:77:3)
at jsonParser (/usr/local/lib/node_modules/expo-cli/node_modules/#expo/dev-server/node_modules/body-parser/lib/types/json.js:135:5)
at call (/usr/local/lib/node_modules/expo-cli/node_modules/connect/index.js:239:7)
at next (/usr/local/lib/node_modules/expo-cli/node_modules/connect/index.js:183:5)
at serveStatic (/usr/local/lib/node_modules/expo-cli/node_modules/serve-static/index.js:75:16)
at call (/usr/local/lib/node_modules/expo-cli/node_modules/connect/index.js:239:7)
at next (/usr/local/lib/node_modules/expo-cli/node_modules/connect/index.js:183:5)
I found some solutions that you can set the limit to a higher value, but that's not specifically for Expo.io
There is no console.log used in the app
The error you are seeing could be caused by one of the packages you are using which uses body parser.
In body parser there is an option to limit to request body size:
limit
Controls the maximum request body size. If this is a number, then the value specifies the number of bytes; if it is a string, the value is passed to the bytes library for parsing. Defaults to '100kb'.
Taken from here.
You can see a related SO questions here and here.
I also saw this GitHub issue for Expo-Cli
I had the same problem, after a lot of trials, I figure it out. The issue is related to the way you fetch the data, you are continuously fetching the data from database, which cause this error.
The solution is to fetch the data only once,
useEffect(() => {
}, []);
Here is code example to
useEffect(() => {
const fetchUser = async () => {
try {
let user = await AsyncStorage.getItem('user_id');
let parsed = JSON.parse(user);
setUserId(parsed);
//Service to get the data from the server to render
fetch('http://myIpAddress/insaf/mobileConnection/ClientDashboard/PrivateReplyConsulationTab.php?ID=' + parsed)
//Sending the currect offset with get request
.then((response) => response.json())
.then((responseJson) => {
//Successful response from the API Call
setOffset(offset + 1);
const pri = Object.values(responseJson[0].reply_list);
setPrivateReplies(pri);
setLoading(false);
})
.catch((error) => {
console.error(error);
});
}
catch (error) {
alert(error + "Unkbown user name")
}
}
fetchUser();
}, []);
I think the storage is merging the old requests, so can you reset the async storage
AsyncStorage.clear()

Postman & Newman - cookieJar.getAll() requires a callback function

I am trying to call a graphql and get the data from cookies, it runs well in postman app. However when I trying to run this postman collection on the command line with Newman
In terminal:
newman run postman_collection.json -e environment.json
then it gave me the error
[UnhandledPromiseRejection: This error originated either by throwing inside of an async function without a catch block,
or by rejecting a promise which was not handled with .catch().
The promise rejected with the reason "TypeError: CookieJar.getAll() requires a callback function".]
{
code: 'ERR_UNHANDLED_REJECTION'
}
And the Test script code is like this
pm.test("Get a test data", function () {
const jsonData = pm.response.json();
pm.expect(jsonData.data.createTest.success).to.eql(true);
});
pm.test("Test data cookies set", async function () {
const cookieJar = pm.cookies.jar();
const url = pm.environment.get("service-url");
const cookies = await cookieJar.getAll(url);
const cookieNames = cookies.map(cookie => cookie.name);
pm.expect(cookieNames).to.include("test-token");
pm.expect(cookieNames).to.include("legacy-test-token");
});
So I assume the error is because getAll() requires a callback function, Do you know what I'm doing wrong? How can I improve it, Can you help me solve this? Many thanks
'it runs well in postman app' --> I doubt it. I tried and it always passed.
I added a callback, also changed a setting Whitelist Domain in Postman GUI.
pm.test("Test data cookies set", function () {
const cookieJar = pm.cookies.jar();
const url = pm.environment.get("service-url");
cookieJar.getAll(url, (error, cookies)=> {
if(error) console.log(error);
const cookieNames = cookies.map(cookie => cookie.name);
pm.expect(cookieNames).to.include("test-token");
pm.expect(cookieNames).to.include("legacy-test-token");
});
});

Cypress setCookie not working as expected

I have added a command getCSRFToken that is used by other commands to get the CSRF token for making requests to my app:
Cypress.Commands.add("getCSRFToken", () => {
cy.getCookie('XSRF-TOKEN').then((cookie) => {
if (!cookie) {
return cy.request('HEAD', '/')
.its('headers')
.then((headers) => {
const token = headers['x-xsrf-token'];
if (!token) {
throw new Error('XSRF token not found');
}
return cy.setCookie('XSRF-TOKEN', token)
.then(() => token);
});
}
return cookie.value;
});
});
The portion that makes a HEAD request is for usage of this function when no pages have yet been visited in the test, for example when making POST requests to create test data.
AFAICT this looks like it should work to me, however it seems subsequent calls to getCookie doesn't actually retrieve anything:
I thought returning the setCookie promise and getCookie promise might make a difference but it does not seem like that is the case.
By default, Cypress clears up all cookies before every test is run. They have an api to keep a cookie for the next test execution which is Cypress.Cookies.preserveOnce
Back to your use case, you can call Cypress.Cookies.preserveOnce('XSRF-TOKEN') in the suite-level beforeEach in every suite where you want to get the token. If you don't want to repeat the call, you can move it inside your getCSRFToken command.
Cypress.Commands.add("getCSRFToken", () => {
Cypress.Cookies.preserveOnce('XSRF-TOKEN')
cy.getCookie('XSRF-TOKEN').then((cookie) => {
.....
});
});

Stop subsequent queries in apollo after 401 has been returned?

I am using apollo client to make a query in my Component. It is composed with 2 queries. How do i stop it from sending another query to it after it has given me a 401 error. I am using a onError Apollo Link Error to listen for errors. However it dispatches both queries and i cannot stop the next one.
Apollo Link Error allows you to intercept and handle query or network errors. It doesn't however provide an opportunity to manage subsequent requests. For this you will need to create your own link.
I've used something like the following in the past. The example below specifically handles bearer auth with refresh tokens but the same principle could be used to handle any auth failure.
import { ApolloLink, Observable } from 'apollo-link';
const isAuthError = (statusCode: number) => [401, 403].includes(statusCode);
const authLink = new ApolloLink((operation, forward) => {
// Set outgoing Authorization headers
const setHeaders = () =>
operation.setContext(({ store, headers, ...rest }) => {
// get the authentication token from local storage if it exists
const token = localStorage.getItem('token');
// return the headers to the context so httpLink can read them
return {
...rest,
store,
headers: {
...headers,
authorization: `Bearer ${token}`
}
};
});
setHeaders();
return new Observable(obs => {
const subscriber = {
next: obs.next.bind(obs),
// Handle auth errors. Only network or runtime errors appear here.
error: error => {
if (isAuthError(error.statusCode)) {
// Trigger an auth refresh.
refreshTokenOrLogin()
.then(setHeaders)
.then(() => forward(operation).subscribe(subscriber));
}
}
});
} else {
obs.error(error);
}
},
complete: obs.complete.bind(obs)
};
forward(operation).subscribe(subscriber);
});
});
The first portion sets the auth context as documented by Apollo. You should replace this with whichever auth mechanism you are using.
operation.setContext(({ store, headers, ...rest }) => {
// get the authentication token from local storage if it exists
const token = localStorage.getItem('token');
// return the headers to the context so httpLink can read them
return {
...rest,
store,
headers: {
...headers,
authorization: `Bearer ${token}`
}
};
});
Non terminating links like this must return an observable. This allows us to catch any network errors just as Apollo Link Error does except we can now handle what happens subsequently. In this case we create and return a new observable with an error handler that will trigger an auth token refresh and then retry the request. The next and completion handlers are passed through to the next link untouched.
new Observable(obs => {
const subscriber = {
next: obs.next.bind(obs),
// Handle auth errors. Only network or runtime errors appear here.
error: error => {
if (isAuthError(error.statusCode)) {
// Trigger an auth refresh.
refreshTokenOrLogin()
.then(setHeaders)
.then(() =>
// We can now retry the request following a successful token refresh.
forward(operation).subscribe(subscriber)
);
}
}
});
} else {
obs.error(error);
}
},
complete: obs.complete.bind(obs)
};
forward(operation).subscribe(subscriber);
});
It might be easier to think of this as 2 links. One that sets the outgoing auth context and the other that captures the response and handles the auth errors.