Javascript 404 error when trying to access API call - javascript

I am trying to alter some data inside of my database, however I am getting the error once my api request is called:
Uncaught (in promise) SyntaxError: Unexpected end of JSON input
at JSON.parse (<anonymous>)
Along with the corresponding network error of 404. I am not quite sure why it isn't recognnizing my api call, here is the initial fetch call:
import fetch from '../../../../core/fetch/fetch.server';
import history from '../../../../core/history';
export default function checkIn(orderId) {
debugger;
return async (dispatch, getState) => {
// dispatch({ type: BOXOFFICE_CHECKING_IN });
const response = await fetch(`/api/orders/${orderId}/checkIn`, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
checkedIn: true,
}),
}
);
if (response.status === 200) {
// dispatch({ type: BOOKING_CHECKED_IN });
} else {
const errorResponse = await response.json();
if (errorResponse.code === 'card_error') {
// dispatch({ type: BOXOFFICE_CHECKED_IN_ERROR });
}
}
} catch (err) {
throw err;
}
};
}
And my api file (removed everything that isn't relevant):
import { Router } from 'express';
import checkIn from '../handlers/api/orders/checkInCustomer';
export default (resources) => {
const router = new Router();
router.post('/orders/:orderId/checkIn', checkIn(resources));
return router;
};
Which ultimately is meant to call my js file that changes the data databse entry:
import { defaultTo } from 'lodash';
import guuid from '../../../../../../core/guuid';
import authenticateAdmin from '../authenticateAdmin';
import order from '../../../../client/reducers/ui/modals/order';
export default ({ knex }) =>
authenticateAdmin(knex)(async (req, res) => {
try {
console.log('checkinCustomer');
const { orderId } = req.params;
const { isCheckedIn } = req.body;
console.log(orderId);
console.log(isCheckedIn);
await knex('orders').where('is_checked_in', '=', orderId).update({ is_checked_in: isCheckedIn }).where({ id: orderId });
res.status(201).end();
} catch (err) {
console.error(err.stack || err);
}
});
Can anyone spot something that is fundamentally wrong in my code, I can assure you the file paths are all correct and all functions are visible to each other, maybe it's the way I have parsed my data?
EDIT
I thought it maybe of use to include that I am also getting a CORS error:
Access to XMLHttpRequest at 'http://localhost:3000/api/orders/c7216fc0-1197-4cb6-99d4-15760f00b6e7/checkIn' from origin 'my site name' has been blocked by CORS policy:
FURTHER EDIT
I have managed to remove the original JSON error, however I am still getting the 404 network error as well as the original CORS error... In addition to this, if I change the last section of the fetch from checkIn to say cancel which is a fully working api call, the same errors persist.

You should not use JSON.stringify() in passing data to your body.
You should pass json format in your body as you are using application/json.

I have a solution! Turns out my import fetch from '../../../../core/fetch/fetch.server';
in the initial file was wrong and should have been import fetch from '../../../../core/fetch/fetch';!

Related

React Native access token expiration/renewal upon 403 response code from a RTK Query API

I am calling an API defined using RTK Query, within a React Native + Redux Toolkit + Expo app. This is secured with an authentication / authorization system in place i.e. access token (short expiration) and refresh token (longer expiration).
I would like to avoid checking any access token expiration claim (I've seen people suggesting to use a Redux middleware). Rather, if possible, I'd like to trigger the access token renewal when the API being requested returns a 403 response code, i.e. when the access token is expired.
This is the code calling the API:
const SearchResults = () => {
// get the SearchForm fields and pass them as the request body
const { fields, updateField } = useUpdateFields();
// query the RTKQ service
const { data, isLoading, isSuccess, isError, error } =
useGetDataQuery(fields);
return ( ... )
the RTK Query API is defined as follows:
import { createApi, fetchBaseQuery } from "#reduxjs/toolkit/query/react";
import * as SecureStore from "expo-secure-store";
import { baseUrl } from "~/env";
export const api = createApi({
reducerPath: "api",
baseQuery: fetchBaseQuery({
baseUrl: baseUrl,
prepareHeaders: async (headers, { getState }) => {
// retrieve the access_token from the Expo SecureStore
const access_token = await SecureStore.getItemAsync("access_token");
if (access_token) {
headers.set("Authorization", `Bearer ${access_token}`);
headers.set("Content-Type", "application/json");
}
return headers;
},
}),
endpoints: (builder) => ({
getData: builder.query({
// body holds the fields passed during the call
query: (body) => {
return {
url: "/data",
method: "POST",
body: body,
};
},
}),
}),
});
export const { useGetDataQuery } = api;
I understand that when the API returns isError = true and error = something 403 I need to renew the access token within the Expo SecureStore (and there's a function already in place for that). However I have no idea about how can I query the RTKQ API again, on the fly, when it returns a 403 response code, and virtually going unnoticed by the user.
Can someone please point me in the right direction?
I got the hang of it, massive thanks to #phry! I don't know how I could have missed this example from RTKQ docs but I'm a n00b for a reason after all.
This being said, here's how to refactor the RTKQ api to renew the access token on the fly, in case some other react native beginner ever has this problem. Hopefully this is a reasonable way of doing this
import { createApi, fetchBaseQuery } from "#reduxjs/toolkit/query/react";
import * as SecureStore from "expo-secure-store";
import { baseUrl } from "~/env";
import { renewAccessToken } from "~/utils/auth";
// fetchBaseQuery logic is unchanged, moved out of createApi for readability
const baseQuery = fetchBaseQuery({
baseUrl: baseUrl,
prepareHeaders: async (headers, { getState }) => {
// retrieve the access_token from the Expo SecureStore
const access_token = await SecureStore.getItemAsync("access_token");
if (access_token) {
headers.set("Authorization", `Bearer ${access_token}`);
headers.set("Content-Type", "application/json");
}
return headers;
},
});
const baseQueryWithReauth = async (args, api) => {
let result = await baseQuery(args, api);
if (result.error) {
/* try to get a new token if the main query fails: renewAccessToken replaces
the access token in the SecureStore and returns a response code */
const refreshResult = await renewAccessToken();
if (refreshResult === 200) {
// then, retry the initial query on the fly
result = await baseQuery(args, api);
}
}
return result;
};
export const apiToQuery = createApi({
reducerPath: "apiToQuery",
baseQuery: baseQueryWithReauth,
endpoints: (builder) => ({
getData: builder.query({
// body holds the fields passed during the call
query: (body) => {
return {
url: "/data",
method: "POST",
body: body,
};
},
}),
}),
});
export const { useGetDataQuery } = apiToQuery;

Error: This is caused by either a bug in Node.js or incorrect usage of Node.js internals

I was creating authentication mechanism for my service. And at some moment I had problem with cookies. More you can find here, so I solved this.
The problem was that I was trying to send cookie through 2 requests. My Next.js front-end sends request to its internal API, and only then, internal API sends this request to back-end.
The solution of this problem was very easy, what I had to do - is to set cookie on back-end and return it in headers. Here is how flow looks, like.
This is how it looks like, endpoint in Next.js front-end. Except of data in response, it receives header, where cookie is set (response from back-end) and send it in header of response, that will be send on front-end, where cookie will be set:
import { NextApiRequest, NextApiResponse } from "next";
import { AxiosError } from "axios";
import { api } from "../../../api";
export default async (
req: NextApiRequest,
res: NextApiResponse
) => {
try {
const { data, headers } = await api.post('/user/sign-in', req.body)
if (headers["set-cookie"]) {
res.setHeader("Set-Cookie", headers["set-cookie"]);
}
return res.json(data)
} catch (error) {
return res
.status((error as AxiosError).response?.status as number)
.json((error as AxiosError).response?.data);
}
}
And endpoint on back-end:
import { Response as Res } from 'express';
import * as dayjs from 'dayjs';
...
async signIn(#Body() signInUserDto: SignInUserDto, #Response() res: Res) {
const { _at, _rt } = await this.userService.signIn(signInUserDto);
res.cookie('_rt', _rt, {
httpOnly: true,
expires: dayjs().add(7, 'days').toDate()
});
return res.send(_at);
}
And here is the problem, because of this Response class of express I keep getting this warning:
Error: This is caused by either a bug in Node.js or incorrect usage of Node.js internals.
Please open an issue with this stack trace at https://github.com/nodejs/node/issues
at new NodeError (node:internal/errors:371:5)
at assert (node:internal/assert:14:11)
at ServerResponse.detachSocket (node:_http_server:249:3)
at resOnFinish (node:_http_server:819:7)
at ServerResponse.emit (node:events:390:28)
at onFinish (node:_http_outgoing:830:10)
at callback (node:internal/streams/writable:552:21)
at afterWrite (node:internal/streams/writable:497:5)
at afterWriteTick (node:internal/streams/writable:484:10)
at processTicksAndRejections (node:internal/process/task_queues:82:21)
It is definitely because of how this signIn function looks like, because I was trying to return just like this - return this.userService.signIn(signInUserDto) - and it worked, but I can't cookie in this case.
So, my question is - what is this error? Can I just ignore it? If not, then how can I fix it?
Thanks in advance!
TL;DR
Finally, I was able to fix this error, first of all, as I said, my goes through 2 API's, from back-end to front-end API, and only then, this front-end API sends this request to actual front-end.
So, what I did, is just returned 2 tokens - refresh and access - as body.
#ApiOperation({ summary: 'Resource for sign in user.' })
#ApiResponse({ status: 200, type: TokensDto })
#Post('/sign-in')
async signIn(#Body() signInUserDto: SignInUserDto) {
return this.userService.signIn(signInUserDto);
}
Then, on front-end, I installed cookie and #types/cookie and in this front-end endpoint, in headers, I just serialized this refresh token from body payload, and removed from it.
import { NextApiRequest, NextApiResponse } from "next";
import { AxiosError } from "axios";
import { api } from "../../../api";
import { serialize } from 'cookie';
export default async (
req: NextApiRequest,
res: NextApiResponse
) => {
try {
const { data } = await api.post('/user/sign-in', req.body)
res.setHeader('Set-Cookie', serialize(
'_rt',
data._rt,
{ path: '/', httpOnly: true })
);
delete data._rt
return res.json(data)
} catch (error) {
return res
.status((error as AxiosError).response?.status as number)
.json((error as AxiosError).response?.data);
}
}
And it works perfectly fine, I don't have this Node.js error any more because of response with Express response class, and I'm able to set cookie.
EDIT
I have improved this code in even better way by using fastify and in the whole pipeline cookie is set in header. First of all, on back-end install #fastify/cookie and #nestjs/platform-fastify. Then, add this in file, where you start you Nest.js app:
import {
FastifyAdapter,
NestFastifyApplication
} from '#nestjs/platform-fastify';
import { fastifyCookie } from '#fastify/cookie';
async function bootstrap() {
const PORT = process.env.PORT || 3002;
const app = await NestFactory.create<NestFastifyApplication>(
AppModule,
new FastifyAdapter()
);
await app.register(fastifyCookie, {
secret: 'my-secret'
});
This will allow you to use FastifyReply from fastify, this will eliminate this Node.js error as response class:
import { FastifyReply } from 'fastify';
#ApiTags('User')
#Controller('user')
export class UserController {
constructor(private userService: UserService) {}
#Post('/sign-in')
async signIn(
#Body() signInUserDto: SignInUserDto,
#Res({ passthrough: true }) res: FastifyReply
) {
const { _at, _rt } = await this.userService.signIn(signInUserDto);
res.setCookie('_rt', _rt);
return res.send(_at);
}
...
And the last step, on front-end endpoint, using cookie, parse this cookie and send it to front.
const { data, headers } = await api.post('/user/sign-in', req.body)
if (headers["set-cookie"]) {
const refreshToken = headers["set-cookie"][0].split('=')[1];
res.setHeader('Set-Cookie', serialize(
'_rt', refreshToken, { path: '/', httpOnly: true })
);
}
return res.json(data)
And this is the best way, that I've found, because it allows you to send cookie in header though all pipeline, not in body and then delete it, and this solution eliminates this strange Node.js error.

Error handling API calls with axios Interceptors. Am I doing this right?

Hello I'am completly new with React/Redux so there is a possibility that I violated some principles with the below code , so bare with me.
I'm building a React App which will consume my Express API. Everything is working perfectly but when I was building the Action Creators I couldnt think of a good way to handle any errors coming from the API without wrapping every single axios request with try/catch blocks.
Both in PHP world where I come from and Express you can create a global Error handler.
For any async requests in my Express APP I wrap them with the below function so I can catch them the same way as the synchronous.
module.exports = (fn) => {
return (req, res, next) => {
fn(req, res, next).catch((err) => next(err));
};
};
From what I've learned through googling is that, there is an ErrorBoundary HOC for handling errors inside Components and for axios calls I should use axios interceptors. So I created this:
AxiosFactory Class
import axios from "axios";
import { setError } from "../actions/utilActions";
import store from "../store";
class AxiosFactory {
constructor(baseURL) {
this.instance = axios.create({
baseURL,
});
this.instance.interceptors.response.use(
function (response) {
// Any status code that lie within the range of 2xx cause this function to trigger
// Do something with response data
return response;
},
function (error) {
// Any status codes that falls outside the range of 2xx cause this function to trigger
// Do something with response error
// Getting the errors from Express API
const {
response: {
data: { errors },
},
} = error;
store.dispatch(setError(errors));
return Promise.reject(error);
}
);
}
getInstance() {
return this.instance;
}
}
export default AxiosFactory;
User API Caller
import AxiosFactory from './AxiosFactory';
const axios = new AxiosFactory('/api/v1/users/').getInstance();
export default axios;
User ActionCreator
import { SUCCESS_LOGIN } from "./types/userTypes";
import userApi from "../apis/user";
// Tries to login the user
export const signInUser = () => {
return async (dispatch) => {
// Test
const {data:{data:{user} = await userApi.post("login", {
email: "test#test.com",
password: "test12345!",
});
dispatch({
type: SUCCESS_LOGIN,
payload: user,
});
}
Error ActionCreator
import { HAS_ERROR } from "./types/utilTypes";
export const setError = (errors) => {
return async (dispatch) => {
dispatch({
type: HAS_ERROR,
payload: errors,
});
};
};
The interceptor dispatches succesfuly the setError and the error state is getting updated like a charm, which means I dont need to manual dispatch on each call. Although I still need to catch the Promise rejection from Interceptor.
My 2 questions are:
Is there a way to lets say "stop the dispatch from executing" inside my User ActionCreator without try/catching the Promise ?
Does this whole thing I setup makes sense ? Or there is a better way to do it?

React App: EventSource's response has a MIME type ("application/json") that is not "text/event-stream". Aborting the connection

Refactoring my app to use a new price API and I'm getting the following error:
EventSource's response has a MIME type ("application/json") that is not "text/event-stream". Aborting the connection.
The free API doc for /currencies/ticker http://docs.nomics.com/#tag/Currencies
Here is my code after I added 'Content-type': 'text/event-stream'
const headers: IHeaders = {
baseURL: NOMICS_API_BASE_URL, // 'https://api.nomics.com/v1/'
headers: {
'Content-Type': 'text/event-stream'
},
params: {
key: NOMICS_KEY
}
}
// * GET Currencies
export const getCurrenciesRequest = async () => {
console.log('getCurrenciesRequest...')
const nomics = axios.create(headers)
try {
const currencies = await nomics.get(`currencies/ticker&ids=BTC,ETH,XRP&interval=1d,30d&convert=USD`)
console.log('currencies', currencies)
return currencies
} catch (err) {
return err
}
}
Also just tried
const currencies = await axios.get(`https://api.nomics.com/v1/currencies/ticker?key=demo-26240835858194712a4f8cc0dc635c7a&ids=BTC,ETH,XRP&interval=1d,30d&convert=USD`)
and lower cased key
'content-type': 'text/event-stream'
Not sure what I'm missing, hoping for some thoughts here...
UPDATE
I am able to get the response back now by removing axios.create(headers)
export const getCurrenciesRequest = async () => {
console.log('getCurrenciesRequest...')
try {
const currencies = await axios.get(`https://api.nomics.com/v1/currencies/ticker?key=demo-26240835858194712a4f8cc0dc635c7a&ids=BTC,ETH,XRP&interval=1d,30d&convert=USD`)
console.log('currencies', currencies)
return currencies
} catch (err) {
return err
}
}
However I still get the same error
EventSource's response has a MIME type ("application/json") that is not "text/event-stream". Aborting the connection.
Are you using a CORS extension? I had this issue as well and ran across this post: SSE `this.eventSource.onmessage` call fails. Error `"EventSource's response has a MIME type ("application/json") that is not "text/event-stream"
You may need to disable the CORS extension to remove that console error.

axios header Authorization using vue.js

I am trying to use a header token with axios. However I am presented with a CORS error as I am clearly not passing over the token correctly (moving to a not authorized feed works)
Here is my http-common.js file
const token = `08E1B4C220E671AC6A48`
// my user app token from micro.blog 08E1B4C220E671AC6A48
export const HTTP = axios.create({
// baseURL: 'https://micro.blog/feeds/adamprocter.json'
baseURL: 'https://micro.blog',
headers: {
Authorization: `Bearer ${token}`
}
})
and here is my Timeline.vue component
import { HTTP } from '#/http-common'
export default {
components: {
MicroPosts
},
data() {
return {
posts: []
}
},
created() {
// no auth get = HTTP.get('')
HTTP.get('/account/verify')
.then(response => {
//console.log(response.data)
this.posts = response.data.items
})
.catch(error => {
console.log('caught error' + error.response)
})
}
}
The URL is correct but the token is failing (I believe)
POST /account/verify — Accepts an app token (which I have set up) and returns an auth token and other details.
This is the API documentation which is a little sparse but
http://help.micro.blog/2017/api-json/
http://help.micro.blog/2018/api-authentication/
I am sure it is something obvious, any help much appreciated.
The documentation says /account/verify accepts POST. You are sending a GET.

Categories

Resources