How to make Graphql run with CORS - graphql

I've read several articles about this, but none of them work for me.
https://github.com/graphql/express-graphql/issues/14
Here is my expressjs code:
app.use("/graphql", function (req, res, next) {
res.header('Access-Control-Allow-Origin', 'http://localhost:8080');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization, Content-Length, X-Requested-With');
if (req.method === 'OPTIONS') {
res.sendStatus(200);
} else {
next();
}
});
// apply graphql middleware
app.use('/graphql', graphqlHTTP({
schema: schema,
rootValue: rootResolver,
graphiql: true,
}))
If I do it this way, the pre-flight OPTIONS is successful, but the actual POST request fails.
I am using this function to make request to local graphql server.
function postFn(url, payload) {
return $.ajax({
method: 'POST',
url: url,
contentType: 'application/json',
xhrFields: {
withCredentials: true
},
data: payload
});
}
Here is the front-end code to trigger the POST request:
let query = `
query myqury($offset: Int, $limit: Int) {
clients(limit:$limit , offset:$offset ) {
docs{
_id
full_name
}
total
limit
offset
}
}
`
var variables = {
offset: offset,
limit: limit
}
let payload = {
query: query,
variables: variables
}
return request.post(graphQlEndpoint, payload)
The error message is:
No 'Access-Control-Allow-Origin' header is present on the requested resource

I had the same issue as you. Using the graphQL on an express server.
Try using express cors
Use it in your express code like this
const express = require( `express` );
const graphqlHTTP = require( `express-graphql` );
const cors = require( `cors` );
const app = express();
app.use( cors() );
app.use(
`/graphql`,
graphqlHTTP( {
schema: schema, // point to your schema
rootValue: rootResolver, // point to your resolver
graphiql: true
} )
);
Fetch example based on GraphQL Documentation
fetch( url, {
method : `post`,
headers: {
'Content-Type': `application/json`,
'Accept' : `application/json`
},
body: JSON.stringify( {
query: `
{
person {
name
}
}`
} )
} )
.then( response => response.json() )
.then( response => console.log( response ) );

I had the same issue when making calls using Vue client. The only way I could resolve was to disable the Cross-Origin restriction on the browser for testing purposes.

Please insert below code in your server.js file
const graphQLServer = express();
const corsOptions = {
origin(origin, callback) {
callback(null, true);
},
credentials: true
};
graphQLServer.use(cors(corsOptions));
var allowCrossDomain = function(req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
res.header('Access-Control-Allow-Headers', 'Content-Type,token');
next();
}
graphQLServer.use(allowCrossDomain);
I think this may solve your problem

Related

Use laravel sanctum session-bases with next.js in sever side (like getServerSideProps metod)

I have laravel as backend and next.js as frontend of my website.
I use laravel sanctum for authentication.
My laravel app and next.js app are in the same host, then I can use session-base sanctum for authentication without use token.
After login there is no problem when I want to access the routes that are protected with middleware aute:sanctum in client side of next.js but in the server side always get error unauthenticated.
This is my axios config and my function for fetch data in next.js:
// axios instance
const axiosInstance = axios.create({
baseURL: 'localhost:8000',
withCredentials: true,
headers: {
'X-Requested-With': 'XMLHttpRequest',
},
});
const onRequest = (config) => {
if ((
config.method == 'post' ||
config.method == 'put' ||
config.method == 'delete'
/* other methods you want to add here */
) &&
!Cookies.get('XSRF-TOKEN')) {
return setCSRFToken()
.then(response => config);
}
return config;
}
const setCSRFToken = () => {
return axiosInstance.get('api/v1/csrf-cookie');
}
// attach your interceptor
axiosInstance.interceptors.request.use(onRequest, null);
// fetch data:
export async function getServerSideProps(context) {
const { query, req } = context;
const {
page,
search_term,
nip,
tableName
} = query;
try {
const response = await ax.post(`api/v1/admin/${tableName}`, { page: page, search_term: search_term, nip: nip || 10 }, {
withCredentials: true,
headers: {
Cookie: req.headers.cookie,
}
});
return {
props: {
initItems: response.data.data,
initMeta: response.data.meta,
initSearchTerm: search_term || '',
iniNip: nip || 10
}
}
} catch (err) {
console.log(err);
return {
notFound: true,
}
}
}

Write on session with Sapper and Svelte

I wrote a Sapper app with session management following the RealWorld example:
polka()
.use(bodyParser.json())
.use(session({
name: 'kidways-app',
secret: 'conduit',
resave: false,
saveUninitialized: true,
cookie: {
maxAge: 31536000
},
store: new FileStore({
path: 'data/sessions',
})
}))
.use(
compression({ threshold: 0 }),
sirv('static', { dev }),
pdfMiddleware,
sapper.middleware({
session: req => ({
token: req.session && req.session.token
})
})
)
.listen(PORT, err => {
if (err) console.log('error', err);
});
Then on my _layout.sevlte:
<script context="module">
export async function preload({ query }, session) {
console.log('preload', session)
return {
// ...
};
}
</script>
<script>
import { onMount, createEventDispatcher } from 'svelte';
import { Splash } from 'project-components';
import * as sapper from '#sapper/app';
import { user } from '../stores';
import client from '../feathers';
const { session } = sapper.stores();
onMount(async () => {
try {
await client.reAuthenticate();
const auth = await client.get('authentication');
user.set(auth.user);
$session.token = 'test';
} catch (e) {
} finally {
loaded = true;
}
});
console.log($session)
</script>
<h1>{$session.token}</h1>
This work on client side rendering, but the token is still undefined on preload, making my SSR template rendering broken.
What did I missed?
When a page renders, session is populated according to the return value of the function you specified here:
sapper.middleware({
session: req => ({
token: req.session && req.session.token
})
})
So while the client may have an up-to-date token, it won't take effect on page reload unless you somehow persist the token to the server in such a way that the session middleware knows about it.
Typically you'd achieve this by having a server route, like routes/auth/token.js or something...
export function post(req, res) {
req.session.token = req.body.token;
res.writeHead(200, {
'Content-Type': 'application/json'
});
res.end();
}
...and posting the token from the client:
onMount(async () => {
try {
await client.reAuthenticate();
const auth = await client.get('authentication');
user.set(auth.user);
await fetch(`auth/token`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ token })
});
// writing to the session store on the client means
// it's immediately available to the rest of the app,
// without needing to reload the page
$session.token = 'test';
} catch (e) {
} finally {
loaded = true;
}
});

How should a botframework webchat conversation be maintained for over an hour?

I have looked through the documentation for botframework-webchat and have not been able to find any documentation on how conversations over 1 hour should be handled properly. This situation is most likely to occur if a web page is left idle in the background for an extended period of time.
The directline connection is maintained as long as the webchat remains active on a web page. The problem occurs after a page refresh.
The initial short term solution is to store the relevant conversation information in session storage, such as a token. The problem is that the token for the conversation is refreshed every 15 minutes. The refreshed token must be retrieved in order to maintain the conversation upon a page refresh.
I am sure a hacky work around exists for retrieving the refreshed token from the directline client object using an event callback.
Ideally, I am looking for a clean framework designed approach for handling this situation.
Though a working solution is better than no solution.
Relevant Link:
https://github.com/microsoft/BotFramework-WebChat
Thanks.
You can achieve this by implementing cookies in your client side. you can set cookies expiration time to 60 min and you can use watermark to make your chat persistent for one hour.
Passing cookie to and from Bot Service.
You can achieve this by setting up a "token" server. In the example below, I run this locally when I am developing/testing my bot.
You can use whatever package you want, however I landed on "restify" because I include it in the index.js file of my bot. I simply create a new server, separate from the bot's server, and assign it a port of it's own. Then, when I run the bot it runs automatically, as well. Put your appIds, appPasswords, and secrets in a .env file.
Then, in your web page that's hosting your bot, simply call the endpoint to fetch a token. You'll also notice that the code checks if a token already exists. If so, then it set's an interval with a timer for refreshing the token. The interval, at 1500000 ms, is set to run before the token would otherwise expire (1800000 ms). As such, the token is always getting refreshed. (Just popped in my head: may be smart to log the time remaining and the amount of time that passed, if the user navigated away, in order to set the interval to an accurate number so it refreshes when it should. Otherwise, the interval will reset with the expiration time being something much less.)
Also, I included some commented out code. This is if you want your conversations to persist beyond page refreshes or the user navigating away and returning. This way current conversations aren't lost and the token remains live. May not be necessary depending on your needs, but works well with the above.
Hope of help!
Token Server
/**
* Creates token server
*/
const path = require('path');
const restify = require('restify');
const request = require('request');
const bodyParser = require('body-parser');
const ENV_FILE = path.join(__dirname, '.env');
require('dotenv').config({ path: ENV_FILE });
const corsToken = corsMiddleware({
origins: [ '*' ]
});
// Create HTTP server.
let server = restify.createServer();
server.pre(cors.preflight);
server.use(cors.actual);
server.use(bodyParser.json({
extended: false
}));
server.listen(process.env.port || process.env.PORT || 3500, function() {
console.log(`\n${ server.name } listening to ${ server.url }.`);
});
// Listen for incoming requests.
server.post('/directline/token', (req, res) => {
// userId must start with `dl_`
const userId = (req.body && req.body.id) ? req.body.id : `dl_${ Date.now() + Math.random().toString(36) }`;
const options = {
method: 'POST',
uri: 'https://directline.botframework.com/v3/directline/tokens/generate',
headers: {
'Authorization': `Bearer ${ process.env.directLineSecret }`
},
json: {
user: {
ID: userId
}
}
};
request.post(options, (error, response, body) => {
// response.statusCode = 400;
if (!error && response.statusCode < 300) {
res.send(body);
console.log('Someone requested a token...');
} else if (response.statusCode === 400) {
res.send(400);
} else {
res.status(500);
res.send('Call to retrieve token from DirectLine failed');
}
});
});
// Listen for incoming requests.
server.post('/directline/refresh', (req, res) => {
// userId must start with `dl_`
const userId = (req.body && req.body.id) ? req.body.id : `dl_${ Date.now() + Math.random().toString(36) }`;
const options = {
method: 'POST',
uri: 'https://directline.botframework.com/v3/directline/tokens/refresh',
headers: {
'Authorization': `Bearer ${ req.body.token }`,
'Content-Type': 'application/json'
},
json: {
user: {
ID: userId
}
}
};
request.post(options, (error, response, body) => {
if (!error && response.statusCode < 300) {
res.send(body);
console.log('Someone refreshed a token...');
} else {
res.status(500);
res.send('Call to retrieve token from DirectLine failed');
}
});
});
webchat.html
<script>
(async function () {
let { token, conversationId } = sessionStorage;
[...]
if ( !token || errorCode === "TokenExpired" ) {
let res = await fetch( 'http://localhost:3500/directline/token', { method: 'POST' } );
const { token: directLineToken, conversationId, error } = await res.json();
// sessionStorage[ 'token' ] = directLineToken;
// sessionStorage[ 'conversationId' ] = conversationId;
token = directLineToken;
}
if (token) {
await setInterval(async () => {
let res = await fetch( 'http://localhost:3500/directline/refresh', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify( { token: token } )
} );
const { token: directLineToken, conversationId } = await res.json();
// sessionStorage[ 'token' ] = directLineToken;
// sessionStorage[ 'conversationId' ] = conversationId;
token = directLineToken;
}, 1500000)
}
// if ( conversationId ) {
// let res = await fetch( `https://webchat.botframework.com/v3/directline/conversations/${ conversationId }`, {
// method: 'GET',
// headers: {
// 'Authorization': `Bearer ${ token }`,
// 'Content-Type': 'application/json'
// },
// } );
// const { conversationId: conversation_Id, error } = await res.json();
// if(error) {
// console.log(error.code)
// errorCode = error.code;
// }
// conversationId = conversation_Id;
// }
[...]
window.ReactDOM.render(
<ReactWebChat
directLine={ window.WebChat.createDirectLine({ token });
/>
),
document.getElementById( 'webchat' );
});
</script>
The solution involved storing the conversation id in session storage instead of the token. Upon a page refresh a new token will be retrieved.
https://github.com/microsoft/BotFramework-WebChat/issues/2899
https://github.com/microsoft/BotFramework-WebChat/issues/2396#issuecomment-530931579
This solution works but it is not optimal. A better solution would be to retrieve the active token in the directline object and store it in session storage. The problem is that a way to cleanly way to retrieve a refreshed token from a directline object does not exist at this point.

Network error: Unexpected token < in JSON at position 0

I apply all the solutions in the internet about this error but still i have this problem
i don't know where is the problem !!
1- i checked the link.
2- i checked the query.
(i use React-Apollo-GraphQL).
const authLink = setContext((_, { headers }) => {
const token = localStorage.getItem("authToken") || "";
return {
headers: {
...headers,
Authorization: token ? `JWT ${token}` : ""
}
};
});
const httpLink = new createHttpLink({
uri: 'http://localhost:8000/graphql/',
fetchOptions: {
credentials: "include"
},
});
const wsLink = () => {
const token = localStorage.getItem("authToken");
return new WebSocketLink({
uri: `ws://localhost:8000/graphql/`,
options: {
reconnect: true,
timeout: 30000,
connectionParams: {
Authorization: `JWT ${token}`,
authToken: token
}
}
});
};
const link = split(
({ query }) => {
const { kind, operation } = getMainDefinition(query);
return kind === 'OperationDefinition' && operation === 'subscription';
},
wsLink(),
authLink.concat(httpLink),
)
const client = new ApolloClient({
link,
cache: new InMemoryCache(),
clientState: {
defaults: {
isLoggedIn: !!localStorage.getItem("authToken")
}
},
})
can you help me please
Thank you.
** Note When i use this code(below) it work successful.
const client = new ApolloClient({
uri: "http://localhost:8000/graphql/",
fetchOptions: {
credentials: "include"
},
request: operation => {
const token = localStorage.getItem("authToken") || "";
operation.setContext({
headers: {
Authorization: `JWT ${token}`
}
});
},
clientState: {
defaults: {
isLoggedIn: !!localStorage.getItem("authToken")
}
},
});
apollo-boost does not support configuring the link or cache options for its client. These are the only supported configuration options. If you're passing in some other parameter, you should be seeing a warning your console about it.
If you need to customize your ApolloClient instance, you need to migrate to using the full client.
Restarting my mac solved the issue !

ReactJS rxjs obervable dom ajax, get request treated as an option method

I am using 2 seperate libraries to make an http get in my reactjs application as follows
import { ajax } from 'rxjs/observable/dom/ajax';
import { Observable } from 'rxjs/Observable';
import * as actionType from '../types';
import types from '../types';
export default (action$, store) => {
return action$.ofType(types.getIssuers.requested).mergeMap(action => {
return ajax({
url: 'http://127.0.0.1:8181/api/v1/status',
responseType: 'json',
method: 'GET',
timeout: 2000
}).map(xhr => {
console.log("Issuer Epic")
const jsonBody = xhr.response;
return {
type: types.getIssuers.completed,
payload: jsonBody,
};
}).catch(error => {
return Observable.of({ type: actionType.LOAD_CUSTOMER_ERROR });
});
});
};
var request = require('request');
request('http://127.0.0.1:8181/api/v1/status', function (error, response, body) {
console.log('error:', error);
console.log('statusCode:', response && response.statusCode);
console.log('body:', body);
});
I can retrieve data without an issue if um using request library
I need to use rxjs/observable/dom/ajax as per the project requirement but it seems my HTTP GET request turned out to be an HTTP OPTION. Is there a fix for this ?

Resources