I'll do my best to explain my issue.
I'm building a NextJS site and using Apollo to handle Graph QL, and it's been working perfectly fine when I test the API using Postman, but when I try to run in dev I get CORS errors:
This operation has been blocked as a potential Cross-Site Request Forgery (CSRF). Please either specify a 'content-type' header (with a type that is not one of application/x-www-form-urlencoded, multipart/form-data, text/plain) or provide a non-empty value for one of the following headers: x-apollo-operation-name, apollo-require-preflight
This is my code that runs the Apollo code.
index.ts:
import { ApolloServer } from '#apollo/server';
import { startServerAndCreateNextHandler } from '#as-integrations/next';
import { applyMiddleware } from 'graphql-middleware';
import { makeExecutableSchema } from '#graphql-tools/schema';
import { getUserFromToken } from './../../../server/security/jwt';
import typeDefs from './schema';
import { resolvers } from './resolvers';
import { permissions } from './permissions';
const schema = applyMiddleware(
makeExecutableSchema({
typeDefs: typeDefs,
resolvers,
}),
permissions
);
const server = new ApolloServer({
schema,
includeStacktraceInErrorResponses: false,
});
export default startServerAndCreateNextHandler(server, {
context: async (req, res) => {
const cookies = req.cookies;
const token = cookies.titanAuthToken || '';
var user;
if (token) {
user = getUserFromToken(token);
}
return { req, res, user, token };
},
});
I have tried a few suggestions from google including this:
const server = new ApolloServer({
schema,
includeStacktraceInErrorResponses: false,
cors: {
origin: true,
credentials: true, // true if you need cookies/authentication
methods: ['GET', 'POST', 'OPTIONS'],
}
});
But when I try the above I get this error:
Argument of type '{ schema: GraphQLSchemaWithFragmentReplacements; includeStacktraceInErrorResponses: false; cors: { origin: boolean; credentials: boolean; methods: string[]; }; }' is not assignable to parameter of type 'ApolloServerOptions<BaseContext>'.
Object literal may only specify known properties, and 'cors' does not exist in type 'ApolloServerOptionsWithSchema<BaseContext>
Any help to fix this CORS error would be greatly appreciated.
Related
I got a nestjs application. It is listening on localhost:3000. I have health check, i can ping with curl or insomnia and it is working correctly. I can use localhost/3000/api/register to register a new user without any problem. I wanted to try it with sveltekit. And i had an issue when i tried to fetch data it and i got an error:
TypeError: fetch failed
at fetch (/Users/marcelljuhasz/Development/svelte-kit-demo/node_modules/undici/index.js:105:13)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async send (/Users/marcelljuhasz/Development/svelte-kit-demo/src/lib/api.ts:16:13)
at async default (/Users/marcelljuhasz/Development/svelte-kit-demo/src/routes/register/+page.server.ts:23:15)
at async handle_action_json_request (file:///Users/marcelljuhasz/Development/svelte-kit-demo/node_modules/#sveltejs/kit/src/runtime/server/page/actions.js:51:16)
at async resolve (file:///Users/marcelljuhasz/Development/svelte-kit-demo/node_modules/#sveltejs/kit/src/runtime/server/index.js:356:17)
at async respond (file:///Users/marcelljuhasz/Development/svelte-kit-demo/node_modules/#sveltejs/kit/src/runtime/server/index.js:229:20)
at async file:///Users/marcelljuhasz/Development/svelte-kit-demo/node_modules/#sveltejs/kit/src/exports/vite/dev/index.js:444:22
I checked my server i got the cors enabled. The front end is listening to: localhost:5173.
I have this code inside:
app.enableCors({
origin: 'http://localhost:5173',
methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
allowedHeaders: 'Content-Type, Accept',
preflightContinue: true,
});
I am learning sveltekit now and i checked a project inside the github repository to see how it is works but i read the documentations too:
https://github.com/sveltejs/realworld
The structure almost the same i have the lib folder with the api.ts
import { error } from '#sveltejs/kit';
const base = 'http://localhost:3000/api';
async function send({ method, path, data }) {
const opts = { method, headers: {} };
if (data) {
opts.headers['Content-Type'] = 'application/json';
opts.body = JSON.stringify(data);
}
const res = await fetch(`${base}/${path}`, opts);
if (res.ok || res.status === 422) {
const text = await res.text();
return text ? JSON.parse(text) : {};
}
console.log(res);
throw error(res.status);
}
export function get(path) {
return send({ method: 'GET', path });
}
export function del(path) {
return send({ method: 'DELETE', path,});
}
export function post(path, data) {
return send({ method: 'POST', path, data });
}
export function put(path, data) {
return send({ method: 'PUT', path, data });
}
I have a register.svelte in the routes dir. With +page.svelte and +page.server.ts is the same like in the repository, i just exclude my own fields. The data input is return in the correct format.
+page.server.ts looks like this, almost the same like in the repo:
import { fail, redirect } from '#sveltejs/kit';
import * as api from '$lib/api.js';
/** #type {import('./$types').PageServerLoad} */
export async function load({ parent }) {
const { user } = await parent();
if (user) throw redirect(307, '/');
}
/** #type {import('./$types').Actions} */
export const actions = {
default: async ({ request }) => {
const data = await request.formData();
const user = {
username: data.get('username'),
email: data.get('email'),
password: data.get('password')
};
const body = await api.post('register', { ...user });
if (body.errors) {
return fail(401, body);
}
console.log(body)
throw redirect(307, '/');
}
};
So in a nutshell i got typerror after i hit the sign uo button. On my server log tells nothing. I see this log in the sveltekit log. I tried to check cors, but it is okey and i haven't got any cors errors in dev console. I checked in my console with curl to check if is the server available. I tried to post, get with insomnia and curl. And it is worked as expected. I have no clue for this. It is wierd if i check the chrome dev tool the request. In the general tab the request url is: localhost:5173 which is the default vite.config for sveltekit server. But i passing my own server which is localhost:3000 and i dont understand what is this behavor. If anybody have experience with sveltekit i am curious what is wrong. I tried to fetch data with an own svelte file without +page.server.ts, i put this fetch method into the component file and it is worked. Wierd.
I have an app made with React, Node.js and Socket.io
I deployed Node backend to heroku , frontend to Netlify
I know that CORS errors is related to server but no matter what I add, it just cant go through that error in the picture below.
I also added proxy script to React's package.json as "proxy": "https://googledocs-clone-sbayrak.herokuapp.com/"
And here is my server.js file;
const mongoose = require('mongoose');
const Document = require('./Document');
const dotenv = require('dotenv');
const path = require('path');
const express = require('express');
const http = require('http');
const socketio = require('socket.io');
dotenv.config();
const app = express();
app.use(cors());
const server = http.createServer(app);
const io = socketio(server, {
cors: {
origin: 'https://googledocs-clone-sbayrak.netlify.app/',
methods: ['GET', 'POST'],
},
});
app.get('/', (req, res) => {
res.status(200).send('hello!!');
});
const connectDB = async () => {
try {
const connect = await mongoose.connect(process.env.MONGODB_URI, {
useUnifiedTopology: true,
useNewUrlParser: true,
});
console.log('MongoDB Connected...');
} catch (error) {
console.error(`Error : ${error.message}`);
process.exit(1);
}
};
connectDB();
let defaultValue = '';
const findOrCreateDocument = async (id) => {
if (id === null) return;
const document = await Document.findById({ _id: id });
if (document) return document;
const result = await Document.create({ _id: id, data: defaultValue });
return result;
};
io.on('connection', (socket) => {
socket.on('get-document', async (documentId) => {
const document = await findOrCreateDocument(documentId);
socket.join(documentId);
socket.emit('load-document', document.data);
socket.on('send-changes', (delta) => {
socket.broadcast.to(documentId).emit('receive-changes', delta);
});
socket.on('save-document', async (data) => {
await Document.findByIdAndUpdate(documentId, { data });
});
});
console.log('connected');
});
server.listen(process.env.PORT || 5000, () =>
console.log(`Server has started.`)
);
and this is where I make request from frontend;
import Quill from 'quill';
import 'quill/dist/quill.snow.css';
import { useParams } from 'react-router-dom';
import { io } from 'socket.io-client';
const SAVE_INTERVAL_MS = 2000;
const TextEditor = () => {
const [socket, setSocket] = useState();
const [quill, setQuill] = useState();
const { id: documentId } = useParams();
useEffect(() => {
const s = io('https://googledocs-clone-sbayrak.herokuapp.com/');
setSocket(s);
return () => {
s.disconnect();
};
}, []);
/* below other functions */
/* below other functions */
/* below other functions */
}
TL;DR
https://googledocs-clone-sbayrak.netlify.app/ is not an origin. Drop that trailing slash.
More details about the problem
No trailing slash allowed in the value of the Origin header
According to the CORS protocol (specified in the Fetch standard), browsers never set the Origin request header to a value with a trailing slash. Therefore, if a page at https://googledocs-clone-sbayrak.netlify.app/whatever issues a cross-origin request, that request's Origin header will contain
https://googledocs-clone-sbayrak.netlify.app
without any trailing slash.
Byte-by-byte comparison on the server side
You're using Socket.IO, which relies on the Node.js cors package. That package won't set any Access-Control-Allow-Origin in the response if the request's origin doesn't exactly match your CORS configuration's origin value (https://googledocs-clone-sbayrak.netlify.app/).
Putting it all together
Obviously,
'https://googledocs-clone-sbayrak.netlify.app' ===
'https://googledocs-clone-sbayrak.netlify.app/'
evaluates to false, which causes the cors package not to set any Access-Control-Allow-Origin header in the response, which causes the CORS check to fail in your browser, hence the CORS error you observed.
Example from the Fetch Standard
Section 3.2.5 of the Fetch Standard even provides an enlightening example of this mistake,
Access-Control-Allow-Origin: https://rabbit.invalid/
and explains why it causes the CORS check to fail:
A serialized origin has no trailing slash.
Looks like you haven't imported the cors package. Is it imported anywhere else?
var cors = require('cors') // is missing
I'm currently trying to redirect inside of getStaticProps based on an API call which checks for a cookie. If the cookie is present, user is authenticated, which means no redirect, but if missing, redirect.
import React from "react";
import { GetStaticProps } from "next";
const Chat: React.FC = () => {
return null;
};
export const getStaticProps: GetStaticProps = async ({ locale, defaultLocale }) => {
const { authenticated } = await fetch("http://localhost:4000/api/auth").then(res => res.json());
if (!authenticated) {
return {
redirect: {
permanent: false,
destination: "/user/login",
},
};
}
return {
props: {
defaultLocale,
locale,
},
};
};
export default Chat;
This works fine during runtime, but on build, I get the following error:
Error: redirect can not be returned from getStaticProps during prerendering (/chat)
How so? The official next.js doc even shows how to redirect from getStaticProps. I even added the redirect to next.config.js, but somehow it still fails.
module.exports = {
async redirects() {
return [
{
source: "/chat",
destination: "/user/login",
permanent: false,
},
];
},
...
}
FYI the application uses i18n to danle translations, locales etc.
I am implementing ngx-translate-router in an Angular Universal app that is already working in SSR but once I added this module SSR does not run anymore.
But works correctly with ng serve so it means that the integration of this module without SSR is working fine.
But when running in SSR mode : npm run serve:ssr i get this error :
NetworkError
at XMLHttpRequest.send (F:\GitaLab\vyv-angular\dist\server\main.js:1:819512)
at Observable_Observable._subscribe (F:\GitaLab\vyv-angular\dist\server\main.js:1:3285565)
at Observable_Observable._trySubscribe (F:\GitaLab\vyv-angular\dist\server\main.js:1:576303)
at Observable_Observable.subscribe (F:\GitaLab\vyv-angular\dist\server\main.js:1:576085)
at CatchOperator.call (F:\GitaLab\vyv-angular\dist\server\main.js:1:3994238)
at Observable_Observable.subscribe (F:\GitaLab\vyv-angular\dist\server\main.js:1:575939)
at DoOperator.call (F:\GitaLab\vyv-angular\dist\server\main.js:1:3343772)
at Observable_Observable.subscribe (F:\GitaLab\vyv-angular\dist\server\main.js:1:575939)
at F:\GitaLab\vyv-angular\dist\server\main.js:1:3315893
at Observable_Observable._subscribe (F:\GitaLab\vyv-angular\dist\server\main.js:1:3316238)
I implemented the SSR part based on the instructions of ngx-translate-router so what i did additional for the SSR part are the following :
1 - implemented an interceptor in the app.server.module.ts to be able to access the translations within the server part. Here is the interceptor :
import { REQUEST } from '#nguniversal/express-engine/tokens';
import * as express from 'express';
import {Inject, Injectable} from '#angular/core';
import {HttpHandler, HttpInterceptor, HttpRequest} from '#angular/common/http';
#Injectable()
export class TranslateInterceptor implements HttpInterceptor {
private readonly DEFAULT_PORT = 4200;
private readonly PORT = process.env.PORT || this.DEFAULT_PORT;
constructor(#Inject(REQUEST) private request: express.Request) {}
getBaseUrl(req: express.Request) {
const { protocol, hostname } = req;
return this.PORT ?
`${protocol}://${hostname}:${this.PORT}` :
`${protocol}://${hostname}`;
}
intercept(request: HttpRequest<any>, next: HttpHandler) {
if (request.url.startsWith('./assets')) {
const baseUrl = this.getBaseUrl(this.request);
request = request.clone({
url: `${baseUrl}/${request.url.replace('./assets', 'assets')}`
});
}
return next.handle(request);
}
}
2 - I modified the server.ts to access the different locales and added the routes for them, but I think the issues lies there. I think I incorrectly added the routes listening in the server.ts but i did not find help about this anywhere...
here is the server.ts
// The Express app is exported so that it can be used by serverless Functions.
export function app(): express.Express {
const server = express();
const distFolder = join(process.cwd(), 'dist/browser');
const fs = require('fs');
const data: any = JSON.parse(fs.readFileSync(`src/assets/locales.json`, 'utf8'));
const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index';
server.use(cookieParser());
// Our Universal express-engine (found # https://github.com/angular/universal/tree/master/modules/express-engine)
server.engine('html', ngExpressEngine({
bootstrap: AppServerModule,
}));
server.set('view engine', 'html');
server.set('views', distFolder);
server.get('/', (req, res) => {
const defaultLang = 'en';
const lang = req.acceptsLanguages('en', 'de', 'fr', 'es', 'pt');
let cookieLang = req.cookies.lang;
if (!cookieLang) {
cookieLang = req.cookies.LOCALIZE_DEFAULT_LANGUAGE; // This is the default name of cookie
}
const definedLang = cookieLang || lang || defaultLang;
console.log('domain requested without language');
res.redirect(301, `/${definedLang}/`);
});
// Example Express Rest API endpoints
// server.get('/api/**', (req, res) => { });
// Serve static files from /browser
server.get('*.*', express.static(distFolder, {
maxAge: '1y'
}));
console.log('routes for the locales:');
console.log(data);
data.locales.forEach(route => {
server.get(`/${route}`, (req: express.Request, res: express.Response) => {
console.log('domain requested with language' + req.originalUrl);
res.render(indexHtml, {
req, providers: [
{ provide: REQUEST, useValue: req }
]
});
});
server.get(`/${route}/*`, (req: express.Request, res: express.Response) => {
console.log('page requested with language ' + req.originalUrl);
res.render(indexHtml, {
req, providers: [
{ provide: REQUEST, useValue: req }
]
});
});
});
return server;
}
function run(): void {
const port = process.env.PORT || 4000;
// Start up the Node server
const server = app();
server.use(compression());
server.listen(port, () => {
console.log(`Node Express server listening on http://localhost:${port}`);
});
}
// Webpack will replace 'require' with '__webpack_require__'
// '__non_webpack_require__' is a proxy to Node 'require'
// The below code is to ensure that the server is run only when not requiring the bundle.
declare const __non_webpack_require__: NodeRequire;
const mainModule = __non_webpack_require__.main;
const moduleFilename = mainModule && mainModule.filename || '';
if (moduleFilename === __filename || moduleFilename.includes('iisnode')) {
run();
}
export * from './src/main.server';
when I start the SSR server and then request the page http://localhost
I can see the redirect working to the default language and the console logs the "domain requested with language /en/" before the error reported above.
I think the issue is that the server.ts does not manage to map the requested url to something within the routes declared in the app-routing.module.ts but i don't know how to do that.
In the GitHub repository of ngx-translate-router they say :
// let node server knows about the new routes:
let fs = require('fs');
let data: any = JSON.parse(fs.readFileSync(`src/assets/locales.json`, 'utf8'));
app.get('/', ngApp);
data.locales.forEach(route => {
app.get(`/${route}`, ngApp);
app.get(`/${route}/*`, ngApp);
});
but they don't describe what "ngApp" is so i just extrapolated it base on how the server.ts was before integrating this plugin:
// All regular routes use the Universal engine
server.get('*', (req: express.Request, res: express.Response) => {
res.render(indexHtml, {
req, providers: [
{ provide: REQUEST, useValue: req }
]
});
});
So my question is double. Do you think I am right to keep searching on the direction that I dentified ? (server.ts implementation is wrong).
If yes do you have an idea how to correct it ?
If no, any other direction to look for ?
The error was not in the server.ts, but the interceptor. I originally designed this interceptor and I thought it could be shared just like that to retrieve the data from ./assets/locales.json as it was working fine in SSR for retrieving the ./assets/i18n/en.json, but no luck. ngx-translate and ngx-translate-router cannot share the same interceptor, I don't know why but that's it. So I had to create a second interceptor (code below) and this solved my issue.
import { REQUEST } from '#nguniversal/express-engine/tokens';
import * as express from 'express';
import {Inject, Injectable, PLATFORM_ID} from '#angular/core';
import {HttpHandler, HttpInterceptor, HttpRequest} from '#angular/common/http';
import {isPlatformServer} from '#angular/common';
#Injectable()
export class LocalizeInterceptor implements HttpInterceptor {
constructor(#Inject(REQUEST) private request: express.Request, #Inject(PLATFORM_ID) private platformId: any) {}
intercept(request: HttpRequest<any>, next: HttpHandler) {
if (request.url.startsWith('assets') && isPlatformServer(this.platformId)) {
const req = this.request;
const url = req.protocol + '://' + req.get('host') + '/' + request.url;
request = request.clone({
url
});
}
return next.handle(request);
}
}
Hi I'm setting default axios headers in request interceptor but these headers are not accessible in another function... in axios axios documentation it is mentioned that global-axios-defaults are global...below is my sample code need help
axios.interceptors.request.use(function (config) {
axios.defaults.headers.accesstoken= "some_access_token"
axios.defaults.headers.client = "some_client"
axios.defaults.headers.uid = "some_uid"
return config;
},function (error) {
return Promise.reject(error);
});
On page load componentDidmount executes but axios default headers are undefined in this function
componentDidMount: function() {
console.log(axios.defaults.headers) #its giving me undefined
axios.get("http://some_url_for_get_request.json", {
headers: {
accesstoken: axios.defaults.headers.accesstoken,
uid: axios.defaults.headers.uid,
client: axios.defaults.headers.client
}
})
}
You can set the default Custom Headers in Axios for every XHR call like this:
axios.defaults.headers.common = {
"X-Requested-With": "XMLHttpRequest",
"X-CSRFToken": "example-of-custom-header"
};
You can also add configurations onward like this:
window.axios.defaults.headers.post['xsrfCookieName'] = 'CSRFToken';
window.axios.defaults.headers.post['xsrfHeaderName'] = 'X-CSRFToken';
window.axios.defaults.headers.post['responseType'] = 'json';
window.axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
See the global options here (Request Config)
Also, you can create a configuration passed into an instance.
See more: here (Axios Create Config)
on your MAIN.JS
import axios from "axios";
const base = axios.create({
baseURL: "http://127.0.0.1:8000/",
});
Vue.prototype.$http = base;
Vue.prototype.$http.interceptors.request.use(
config => {
let accessToken = localStorage.getItem('token');
if (accessToken) {
config.headers = Object.assign({
Authorization: `Bearer ${accessToken}`
}, config.headers);
}
return config;
},
error => {
return Promise.reject(error);
}
);