Meteor/React/ApolloServer/BodyParser - Payload too large - meteor

I am trying to save quite a big object thanks to a Mutation object in my meteor/react app but I am getting a Payload too large error in the console :
PayloadTooLargeError: request entity too large
I know my object is more than the 100kb which is the default limit for bodyparser but I can not managed to have it changed.
I have tried the following while initiating the Apollo Server:
const server = new ApolloServer({
typeDefs,
resolvers,
context: async ({ req }) => {
return ({
user: await getUser(req.headers.authorization)
})
}
})
server.applyMiddleware({
app: WebApp.connectHandlers,
path: '/graphql'
})
WebApp.connectHandlers.use(bodyParser.json({limit: '100mb', extended: true}));
WebApp.connectHandlers.use('/graphql', (req, res) => {
if (req.method === 'GET') {
res.end()
}
})
But I am still getting the same error. I think my object is around 400kb. I am hoping one of you could help me. Thanks in advance.

apollo-server-express already includes body-parser so you shouldn't add it again as middleware. Instead, you can pass body-parser options to Apollo when calling applyMiddlware:
server.applyMiddleware({
app: WebApp.connectHandlers,
path: '/graphql',
bodyParserConfig: {
limit: '100mb',
},
})
See the docs for a full list of available options.

Related

What is the best practice to bypass my specific dynamic code evaluation in next.js middleware?

I use next.js middleware to retrieve a data stored inside a cookie, and to check in a db (using strapi) if this specific user exists, or if he needs to register before going further.
// middleware.js
import { getToken } from 'next-auth/jwt';
import qs from 'qs';
import { MY_DB } from './constants';
export async function middleware(request) {
const token = await getToken({
req: request,
secret: process.env.SECRET,
});
const params = qs.stringify({
filters: {
address: {
$eq: token.sub,
},
},
});
const url = MY_DB + '/api/users/?' + params;
const result = await fetch(url, {
method: 'GET',
headers: { accept: 'application/json' },
});
// remaining code checks if the request is empty or not and returns the appropriate page
(...)
building my project returns the following error :
Failed to compile.
./node_modules/.pnpm/function-bind#1.1.1/node_modules/function-bind/implementation.js
Dynamic Code Evaluation (e. g. 'eval', 'new Function', 'WebAssembly.compile') not allowed in Edge Runtime
Learn More: https://nextjs.org/docs/messages/edge-dynamic-code-evaluation
Import trace for requested module:
./node_modules/.pnpm/function-bind#1.1.1/node_modules/function-bind/implementation.js
./node_modules/.pnpm/function-bind#1.1.1/node_modules/function-bind/index.js
./node_modules/.pnpm/get-intrinsic#1.1.3/node_modules/get-intrinsic/index.js
./node_modules/.pnpm/side-channel#1.0.4/node_modules/side-channel/index.js
./node_modules/.pnpm/qs#6.11.0/node_modules/qs/lib/stringify.js
./node_modules/.pnpm/qs#6.11.0/node_modules/qs/lib/index.js
> Build failed because of webpack errors
 ELIFECYCLE  Command failed with exit code 1.
I highly suspect the qs.stringify call given the stacktrace, but how can I overcome this in an elegant way ?

NextJS Actioncable Proxy

So I'm trying to do two things at the same time and it's not going too well.
I have a NextJS app and a Rails API server this app connects to. For authentication I'm using a JWT token stored in an http-only encrypted cookie that the Rails API sets and the front end should not be touching. Naturally that creates a necessity for the frontend to send all the api requests though the NextJs server which proxies them to the real API.
To do that I have set up a next-http-proxy-middleware in my /pages/api/[...path] in the following way:
export const config = { api: { bodyParser: false, externalResolver: true } }
export default function handler(
req: NextApiRequest,
res: NextApiResponse
) {
httpProxyMiddleware(req, res, {
target: process.env.BACKEND_URL,
pathRewrite: [{ patternStr: "^/?api", replaceStr: "" }],
})
}
Which works great and life would be just great, but turns out I need to do the same thing with ActionCable subscriptions. Not to worry, found some handy tutorials, packed #rails/actioncable into my package list and off we go.
import {useCurrentUser} from "../../../data";
import {useEffect, useState} from "react";
const UserSocket = () => {
const { user } = useCurrentUser()
const [roomSocket, setRoomSocket] = useState<any>(null)
const loadConsumer = async () => {
// #ts-ignore
const { createConsumer } = await import("#rails/actioncable")
const newCable = createConsumer('/api/wsp')
console.log('Cable loaded')
setRoomSocket(newCable.subscriptions.create({
channel: 'RoomsChannel'
},{
connected: () => { console.log('Room Connected') },
received: (data: any) => { console.log(data) },
}))
return newCable
}
useEffect(() => {
if (typeof window !== 'undefined' && user?.id) {
console.log('Cable loading')
loadConsumer().then(() => {
console.log('Cable connected')
})
}
return () => { roomSocket?.disconnect() }
}, [typeof window, user?.id])
return <></>
}
export default UserSocket
Now when I go to load the page with that component, I get the log output all the way to Cable connected however I don't see the Room Connected part.
I tried looking at the requests made and for some reason I see 2 requests made to wsp. First is directed at the Rails backend (which means the proxy worked) but it lacks the Cookie headers and thus gets disconnected like this:
{
"type": "disconnect",
"reason": "unauthorized",
"reconnect": false
}
The second request is just shown as ws://localhost:5000/api/wsp (which is my NextJS dev server) with provisional headers and it just hangs up in pending. So neither actually connect properly to the websocket. But if I just replace the /api/wsp parameter with the actual hardcoded API address (ws://localhost:3000/wsp) it all works at once (that however would not work in production since those will be different domains).
Can anyone help me here? I might be missing something dead obvious but can't figure it out.

How to use runtime config in composable?

I want to do this
composables/apiFetch.ts
import { $fetch } from 'ohmyfetch'
export const useApiFetch = $fetch.create({ baseURL: useRuntimeConfig().apiUrl })
And use it within Pinia so I don't repeat myself writing $fetch.create over and over again for every single API call.
somewhere_in_pinia.ts
...TRIM...
actions: {
async doSomething(payload: SomeNicePayload): Promise<void> {
const response = await useApiFetch('/something', { method: 'POST', body: payload })
}
}
...TRIM...
But Nuxt won't allow me
[nuxt] [request error] nuxt instance unavailable
at useNuxtApp (/D:/XXXX/frontend/prms-fe/.nuxt/dist/server/server.mjs:472:13)
at Module.useRuntimeConfig (/D:/XXXX/frontend/prms-fe/.nuxt/dist/server/server.mjs:480:10)
at $id_Yl353ZXbaH (/D:/XXXX/frontend/prms-fe/.nuxt/dist/server/server.mjs:38358:90)
at async __instantiateModule__ (/D:/XXXX/frontend/prms-fe/.nuxt/dist/server/server.mjs:40864:3)
I have been looking for solution online, followed instruction from the official discussion to no avail.
EDIT
I don't want to use Nitro, since my backend is already written on Laravel. I need to access the host without re-typing it all over the place so I thought I could use .env and runtimeConfig.
you are trying to access Nuxt instance while it's not ready yet. To make it work, write your composable as a function :
import { $fetch } from 'ohmyfetch'
export const useApiFetch = (url, params) => {
const instance = $fetch.create({ baseURL: useRuntimeConfig().apiUrl })
return instance(url, params)
}

Axios post request to Firebase Auth REST API produces 400 error

I have an instance of Axios:
import axios from 'axios';
const instance = axios.create({
baseURL: 'https://identitytoolkit.googleapis.com/v1'
});
export default instance;
Then I import it in my signup.vue file:
<script>
import axios from '../../axios-auth';
...
</script>
In that Vue file I have a signup form, which runs the following method once I hit the Submit button:
onSubmit() {
const formData = {
email: this.email,
age: this.age,
password: this.password,
confirmPassword: this.confirmPassword,
country: this.country,
hobbies: this.hobbyInputs.map(hobby => hobby.value),
terms: this.terms
};
console.log(formData);
axios.post('/accounts:signUp?key=my_key_goes_here', {
email: formData.email,
password: formData.password,
returnSecureToken: true
})
.then(res => {
console.info(res);
})
.catch(error => {
console.error(error);
});
}
I'm getting a 403 error - forbidden 400 error - bad request.
I tried to change headers:
instance.defaults.headers.post["Access-Control-Allow-Origin"] = "localhost";
instance.defaults.headers.common["Content-Type"] = "application/json";
But that didn't help.
I'm working from localhost and I saw that localhost is allowed by default. I tried also to add 127.0.0.1 to the list, but that also didn't help.
What am I missing? How can I make this request work?
If you get a 400 error it is maybe because you get an error from the API itself:
Common error codes
EMAIL_EXISTS: The email address is already in use by another account.
OPERATION_NOT_ALLOWED: Password sign-in is disabled for this project.
TOO_MANY_ATTEMPTS_TRY_LATER: We have blocked all requests from this device due to unusual activity. Try again later.
As a matter of fact, those errors return an HTTP Status Code of 400.
You can see the exact response message (e.g. EMAIL_EXISTS) by doing the following with axios:
axios.post('/accounts:signUp?key=my_key_goes_here', {
email: formData.email,
password: formData.password,
returnSecureToken: true
})
.then(res => {
console.info(res);
})
.catch(error => {
if (error.response) {
// The request was made and the server responded with a status code
// that falls out of the range of 2xx
console.log(error.response.data);
} else if (error.request) {
console.log(error.request);
} else {
console.log("Error", error.message);
}
});
See https://github.com/axios/axios#handling-errors
I agree with you as i have tried many approaches but was not getting the result. Hence i have tried to change the code.
You need to make two changes in your code.
1] You need to comment the instance.defaults.headers.post["Access-Control-Allow-Origin"] = "localhost"; because you are providing the authentication globally. As, firebase provides the feature of authentication and you are connecting the web app with REST API.
2] You need to add { headers: {'Content-Type': 'application/json' } in the axios.post() method to prevent it from CORS Error.
Following this approach i hope you can get the respective output.
Happy Coding!
Directly call
https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=[yourkey]
No need to keep it in a separate file
Anyone who comes to the thread in future. I faced this issue and lost in debugging and worked with fetch. It was tiresome and took me a day but i made axios work. Here is the code.
const data = JSON.stringify({
idToken: authContext.token,
password: enteredNewPassword,
returnSecureToken: false,
});
// Send the valid password to the endpoint to change password
axios
.post(
"https://identitytoolkit.googleapis.com/v1/accounts:update?key=[Your Key]",
data,
{
headers: {
"Content-Type": "application/json",
},
}
)
.then((response) => {
console.log(response.data);
})
.catch((err) => {
console.log(err.message);
});
Remember to Stringify the data you want to send. Stringify it outside of the http request and then pass that variable. Don't know why but this helps!
Lastly remember to add the header when sending the request to firebase. Make sure axios.post is on the same line. My formatter gave a line break which was also cause of error.
Hope it helps :)

Meteor + Apollo Subscription: Websocket connection closed

I'm using meteor and trying to make Apollo Subscriptions to work, but I'm getting
WebSocket connection to 'ws://127.0.0.1:3000/sockjs/401/m892wugm/websocket' failed: Connection closed before receiving a handshake response in the client.
I followed apollographql.com guide for Server Configuration and Client Configuration but I'm not quite sure how to connection the client to the server yet.
In the client, I'm using ApolloClient and ApolloLink to pass the Meteor auth to GraphQL.
Here's the code:
Client
import { ApolloClient } from 'apollo-client'
import { createHttpLink } from 'apollo-link-http'
import { InMemoryCache } from 'apollo-cache-inmemory'
import { ApolloLink } from 'apollo-link'
const httpLink = new createHttpLink()
const authLink = new ApolloLink((operation, forward) => {
operation.setContext(() => ({
headers: { 'meteor-login-token': Accounts._storedLoginToken() },
}))
return forward(operation)
})
export default ApolloClient = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache(),
})
Server
createApolloServer({
schema,
tracing: true,
cacheControl: true,
})
new SubscriptionServer({
schema,
execute,
subscribe,
}, {
server: WebApp.httpServer,
path: '/subscriptions',
})
Package.json (not everything, of course)
Meteor 1.6.1.1
...
"apollo-client": "^2.2.5",
"apollo-link": "^1.2.1",
"apollo-link-context": "^1.0.7",
"apollo-link-http": "^1.5.3",
"apollo-link-ws": "^1.0.8",
"subscriptions-transport-ws": "^0.9.9",
...
I read somewhere that passing noServer: true to the SubscriptionServer() resolve the conflict. The error indeed goes away, but the subscription doesnt seem to work either.
And yes, I have followed the Meteor Integration guide from apollographql, but the info there is outdated and does not work.

Resources