Variable "$file" got invalid value {}; Upload value invalid - javascript

I am using GraphQLClient from graphql-request to send requests to my server. I am trying to upload a file by doing the following:
const graphQLClient = new GraphQLClient('http://localhost:4000/graphql', {
credentials: 'include',
mode: 'cors',
});
const source = gql`
mutation uploadImage($file: Upload!) {
uploadImage(file: $file)
}
`;
const file: RcFile = SOME_FILE; // RcFile (from antd) extends File
await graphQLClient.request<{uploadImage: boolean}>(source, { file });
However, when I send a request to my server this way I get the following error:
GraphQLError: Variable \"$file\" got invalid value {}; Upload value invalid
This is what my request looks like in the console:
operations: {
"query":"\n mutation uploadProfileImage($file: Upload!){\n uploadProfileImage(file: $file)\n }\n",
"variables":{"file":null}
}
map: {"1":["variables.file"]}
1: (binary)
Has anyone else had this issue? I can't seem to upload a file to my backend.

I fixed the issue by setting the uploads option to false in the ApolloServer configuration.
new ApolloServer({ schema, context, uploads: false })
And then using the graphqlUploadExpress() middleware from graphql-upload.
app.use(graphqlUploadExpress({ maxFileSize: 10000, maxFiles: 10 }));
Hopefully this helps anyone who runs into the same issue I did 😊

it depends on ApolloClient that you used.
1- If used import { ApolloClient } from 'apollo-client' must be used "createUploadLink" instead of "createHttpLink "means,
import { createUploadLink } from 'apollo-upload-client'
const httpLink = createUploadLink({
uri: httpEndpoint,
})
2- if used createApolloClient, exact this package:
import { createApolloClient, restartWebsockets } from 'vue-cli-plugin-apollo/graphql-client'
const { apolloClient, wsClient } = createApolloClient({
...defaultOptions,
...options,
})
``
You do not need to set anything and Upload work complete.

In addition to the ApolloServer implementation described in the accepted answer (and to clarify #Masoud 's answer), make sure you also have the following client implementation using apollo-upload-client:
import { ApolloClient, InMemoryCache } from "#apollo/client";
import { createUploadLink } from 'apollo-upload-client';
const client = new ApolloClient({
cache: new InMemoryCache(),
link: createUploadLink({
uri: 'http://localhost:4000/graphql'
}),
});

Related

Can't pass request body through next-http-proxy-middleware

I've been trying to create a frontend using nextjs for a backend I have running in Java.
I'm using the npm package next-http-proxy-middleware and it seems that either my request body is being dropped or I'm using the package incorrectly.
Here's the entirety of my ./src/api/[...all].ts file
import type { NextApiRequest, NextApiResponse } from 'next'
import httpProxyMiddleware from 'next-http-proxy-middleware'
type Data = {
name: string
}
export const config = {
api: {
externalResolver: true,
},
}
export default (req: NextApiRequest, res: NextApiResponse) => {
httpProxyMiddleware(req, res, {
target: `http://${process.env.REACT_APP_URL}`,
changeOrigin: true,
})
}
and here's the snippet of code where I attempt to login, the form that supplies this data is built using react-hook-form
const onSubmit: SubmitHandler<Inputs> = async (data: any) => {
//console.log('username', watch('username'), 'password', watch('password'))
const response = await axios.post(
'/api/session',
new URLSearchParams({
email: data.username,
password: data.password,
})
)
}
I do see that it's connecting to my backend since I'm getting an error that's generated by my Java program and not by next.js, but it just tells me that the email is null which leads me to believe that my args are being lost in translation
"Cannot invoke \"String.trim()\" because \"email\" is null - NullPointerException (DataManager:149 < PermissionsManager:508 < SessionResource:111 < ...)"
I am able to get it working using vanilla react so I'm sure it's something I'm doing wrong in the implementation of the next-http-proxy-middleware but I've been bashing my head against the wall a little too long on this. I appreciate all the help, thanks!
I figured out the solution and I feel a bit silly for not realizing earlier:
https://github.com/vercel/next.js/discussions/11036
Nextjs comes with an automatic body parser and I had to disable it.
export const config = {
api: {
externalResolver: true,
bodyParser: false,
},
}

GraphQL Client with React frontend subscription not working

I started to add a websocket to my React app and I managed to handle the back-end part with nodejs just fine, working perfectly with GraphQL playground. However, I can't get my subscriptions to work from the client side.
I did everything the documentation told me to, set up my websocket and my websocket connect without any trouble.
const wsLink = new WebSocketLink({
uri: ws://myUrl,
options: {
connectionParams
}
);
function splitByOperation({ query }) {
const { kind, operation } = getMainDefinition(query);
return kind === 'OperationDefinition' && operation === 'subscription';
}
let link = ApolloLink.from([
authLink,
errorLink,
uploadLink,
stateLink,
ApolloLink.split(
// split based on operation type
splitByOperation,
wsLink,
httpLink
)
]);
const Client = new ApolloClient({
uri: '/graphql',
cache,
resolvers,
link
});
which result in :
Request URL: ws://myUrl:3000/subscription
Request Method: GET
Status Code: 101 Switching Protocols
in my console.
My main problem is : when using "useSubscription" like that :
const COMMENTS_SUBSCRIPTION = gql`
subscription OnCommentAdded {
commentAdded {
id
content
}
}
`;
const { data: { commentAdded }, loading } = useSubscription(
COMMENTS_SUBSCRIPTION
);
My data commentAdded is always null, I strongly suspect the useSubscription hook doesn't subscribe at all since my subscription resolve in the backend act like there's no one to send the data to.
Also, in the network part of my chrome dev tools there's nothing except my first interaction with the websocket when setting up apollo client.
I have no error to work with and I'm pretty much lost at this point.
The names and field requested match perfectly, nothing wrong when using my backend playground, still in my frontend playground I get this when trying to use the subscription :
Could not connect to websocket endpoint ws://myUrl/subscription. Please check if the endpoint url is correct.
I'm using apollo-client 2.6.4.
finally figured this out.
const wsLink = new WebSocketLink({
uri: 'ws://localhost:3000/subscription',
options: {
connectionParams
}
});
function splitByOperation({query}) {
const definition = getMainDefinition(query);
return (
definition.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
);
}
const link = ApolloLink.from([ApolloLink.split(
// split based on operation type
splitByOperation,
wsLink,
authLink.concat(httpLink)
),
uploadLink,
stateLink,
errorLink]);
// apollo client definition
const Client = new ApolloClient({
uri: '/graphql',
cache,
resolvers,
link
});
correct syntaxe :
function splitByOperation({query}) {
const definition = getMainDefinition(query);
return (
definition.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
);
}
const link = ApolloLink.from([ApolloLink.split(
// split based on operation type
splitByOperation,
wsLink,
authLink.concat(httpLink)
),
uploadLink,
stateLink,
errorLink]);
const Client = new ApolloClient({
uri: '/graphql',
cache,
resolvers,
link
});
i was using HTTP all along.

Using react hooks inside NextJS /pages/api [duplicate]

I need a graphql client lib to run on node.js for some testing and some data mashup - not in a production capacity. I'm using apollo everywhere else (react-apollo, apollo's graphql-server-express). My needs are pretty simple.
Is apollo-client a viable choice? I can find no examples or docs on using it on node - if you're aware of any, please share.
Or maybe I should/can use the reference graphql client on node?
Apollo Client should work just fine on Node. You only have to install cross-fetch.
Here is a complete TypeScript implementation of Apollo Client working on Node.js.
import { ApolloClient, gql, HttpLink, InMemoryCache } from "#apollo/client";
import { InsertJob } from "./graphql-types";
import fetch from "cross-fetch";
const client = new ApolloClient({
link: new HttpLink({ uri: process.env.PRODUCTION_GRAPHQL_URL, fetch }),
cache: new InMemoryCache(),
});
client.mutate<InsertJob.AddCompany, InsertJob.Variables>({
mutation: gql`mutation insertJob($companyName: String!) {
addCompany(input: { displayName: $companyName } ) {
id
}
}`,
variables: {
companyName: "aaa"
}
})
.then(result => console.log(result));
Newer Apollo version provide a simpler approach to perform this, as described in Apollo docs, check the section "Standalone". Basically one can simply use ApolloLink in order to perform a query or mutation.
Below is copy of the example code from the docs as of writing this, with node-fetch usage as config to createHttpLink. Check the docs for more details on how to use these tools.
import { execute, makePromise } from 'apollo-link';
import { createHttpLink } from 'apollo-link-http';
import gql from 'graphql-tag';
import fetch from 'node-fetch';
const uri = 'http://localhost:4000/graphql';
const link = createHttpLink({ uri, fetch });
const operation = {
query: gql`query { hello }`,
variables: {} //optional
operationName: {} //optional
context: {} //optional
extensions: {} //optional
};
// execute returns an Observable so it can be subscribed to
execute(link, operation).subscribe({
next: data => console.log(`received data: ${JSON.stringify(data, null, 2)}`),
error: error => console.log(`received error ${error}`),
complete: () => console.log(`complete`),
})
// For single execution operations, a Promise can be used
makePromise(execute(link, operation))
.then(data => console.log(`received data ${JSON.stringify(data, null, 2)}`))
.catch(error => console.log(`received error ${error}`))
If someone is looking for a JavaScript version:
require('dotenv').config();
const gql = require('graphql-tag');
const ApolloClient = require('apollo-boost').ApolloClient;
const fetch = require('cross-fetch/polyfill').fetch;
const createHttpLink = require('apollo-link-http').createHttpLink;
const InMemoryCache = require('apollo-cache-inmemory').InMemoryCache;
const client = new ApolloClient({
link: createHttpLink({
uri: process.env.API,
fetch: fetch
}),
cache: new InMemoryCache()
});
client.mutate({
mutation: gql`
mutation popJob {
popJob {
id
type
param
status
progress
creation_date
expiration_date
}
}
`,
}).then(job => {
console.log(job);
})
You can make apollo-client work, but it's not the best option for this use case.
Try graphql-request instead.
Minimal GraphQL client supporting Node and browsers for scripts or simple apps
Features per npmjs:
Most simple & lightweight GraphQL client
Promise-based API (works with async / await)
Typescript support
Isomorphic (works with Node / browsers)
example:
import { request, gql } from 'graphql-request'
const query = gql`
{
Movie(title: "Inception") {
releaseDate
actors {
name
}
}
}
`
request('https://api.graph.cool/simple/v1/movies', query).then((data) => console.log(data))
I have no affiliation with this package.
Here is simple node js implementation.
'graphiql' client is good enough for development activities.
1. run npm install
2. start server with "node server.js"
3. hit "http://localhost:8080/graphiql" for graphiql client
server.js
var graphql = require ('graphql').graphql
var express = require('express')
var graphQLHTTP = require('express-graphql')
var Schema = require('./schema')
// This is just an internal test
var query = 'query{starwar{name, gender,gender}}'
graphql(Schema, query).then( function(result) {
console.log(JSON.stringify(result,null," "));
});
var app = express()
.use('/', graphQLHTTP({ schema: Schema, pretty: true, graphiql: true }))
.listen(8080, function (err) {
console.log('GraphQL Server is now running on localhost:8080');
});
schema.js
//schema.js
var graphql = require ('graphql');
var http = require('http');
var StarWar = [
{
"name": "default",
"gender": "default",
"mass": "default"
}
];
var TodoType = new graphql.GraphQLObjectType({
name: 'starwar',
fields: function () {
return {
name: {
type: graphql.GraphQLString
},
gender: {
type: graphql.GraphQLString
},
mass: {
type: graphql.GraphQLString
}
}
}
});
var QueryType = new graphql.GraphQLObjectType({
name: 'Query',
fields: function () {
return {
starwar: {
type: new graphql.GraphQLList(TodoType),
resolve: function () {
return new Promise(function (resolve, reject) {
var request = http.get({
hostname: 'swapi.co',
path: '/api/people/1/',
method: 'GET'
}, function(res){
res.setEncoding('utf8');
res.on('data', function(response){
StarWar = [JSON.parse(response)];
resolve(StarWar)
console.log('On response success:' , StarWar);
});
});
request.on('error', function(response){
console.log('On error' , response.message);
});
request.end();
});
}
}
}
}
});
module.exports = new graphql.GraphQLSchema({
query: QueryType
});
In response to #YakirNa 's comment:
I can't speak to the other needs I described, but I have done a fair amount of testing. I ended up doing all of my testing in-process.
Most testing ends up being resolver testing, which I do via a jig that invokes the graphql library's graphql function with a test query and then validates the response.
I also have an (almost) end-to-end test layer that works at the http-handling level of express. It creates a fake HTTP request and verifies the response in-process. This is all within the server process; nothing goes over the wire. I use this lightly, mostly for testing JWT authentication and other request-level behavior that's independent of the graphql request body.
I was running into your same question, because I wanted to create a middleware service to prepare data from graphQL to a final frontend application,
to have :
optimised data representation (and standard output data interface)
faster response time
assuming that graphQL server is provided by an external provider , so no ownership to data model, directly with GQL
So I didn't want to implement GraphQL Apolloclient directly in a frontend framework like React / Angular, Vuejs... but manage the queries via Nodejs at backend of a REST API.
So this is the class wrapper for Apolloclient I was able to assemble (using typescript):
import ApolloClient from "apollo-client";
import { ApolloLink } from 'apollo-link'
import { HttpLink } from 'apollo-link-http'
import { onError } from 'apollo-link-error'
import fetch from 'node-fetch'
import { InMemoryCache, IntrospectionFragmentMatcher } from 'apollo-cache-inmemory'
import introspectionQueryResultData from '../../fragmentTypes.json';
import { AppConfig } from 'app-config';
const config: AppConfig = require('../../../appConfig.js');
export class GraphQLQueryClient {
protected apolloClient: any;
constructor(headers: { [name: string]: string }) {
const api: any = {
spaceId: config.app.spaceId,
environmentId: config.app.environmentId,
uri: config.app.uri,
cdnApiPreviewToken: config.cdnApiPreviewToken,
};
// console.log(JSON.stringify(api));
const ACCESS_TOKEN = api.cdnApiPreviewToken;
const uri = api.uri;
console.log(`Apollo client setup to query uri: ${uri}`);
const fragmentMatcher = new IntrospectionFragmentMatcher({
introspectionQueryResultData
});
this.apolloClient = new ApolloClient({
link: ApolloLink.from([
onError(({ graphQLErrors, networkError }:any) => {
if (graphQLErrors) {
graphQLErrors.map((el:any) =>
console.warn(
el.message || el
)
)
graphQLErrors.map(({ message, locations, path }:any) =>
console.warn(
`[GraphQL error - Env ${api.environmentId}]: Message: ${message}, Location: ${JSON.stringify(locations)}, Path: ${path}`
)
)
}
if (networkError) console.log(`[Network error]: ${networkError}`)
}),
new HttpLink({
uri,
credentials: 'same-origin',
headers: {
Authorization: `Bearer ${ACCESS_TOKEN}`
},
fetch
})
]),
cache: new InMemoryCache({ fragmentMatcher }),
// fetchPolicy as network-only avoids using the cache.
defaultOptions: {
watchQuery: {
fetchPolicy: 'network-only',
errorPolicy: 'ignore',
},
query: {
fetchPolicy: 'network-only',
errorPolicy: 'all',
},
}
});
}
}
After this constructor I run queries like :
let response = await this.apolloClient.query({ query: gql`${query}` });
As you might have noticed:
I needed to inject fetch on Httplink
I had to setup Authorization headers to access external provider graphQL endpoint
I used IntrospectionFragmentMatcher in order to use Fragments in my queries, along with building schema type ("fragmentTypes.json" with an init script)
Posting this to just add my experience and maybe more info for the question.
Also looking forward for comments and points of improvement for this wrapper.

How to make requests to GraphQL-server from my redux action?

I have react+redux application and I want to replace rest-requests in my actions to grapqhl-requests. What is the most simple method to do it?
I read about apollo, but I just to want add graphql in my existing requests.
You must change the way you will get the data because Apollo is a layer between you and the Api requested where you can join many Apis into one single query call.
The example above is very simple and just to explain the way will place your querys and how it connects to Apollo Server. You can set middlewares to log requests on add some headers too.
You will install [https://github.com/apollographql/react-apollo] and [https://github.com/apollographql/graphql-tag]
After that create a connection file in your project that imports
graphClient.js
import { ApolloClient, createNetworkInterface } from 'react-apollo';
const networkInterface = createNetworkInterface({
uri: 'yourserver address'
});
const client = new ApolloClient({
networkInterface,
});
export default client;
yourRequestFile.js
import gql from 'graphql-tag';
import client from './graphClient';
export const getYourData = () => ({
query: gql`
query {
products() {
info {
value {
qty
value
}
}
}
}`,
});
const returnYourData = async () => client.query(getYourdata());
export default returnYourData;

How to use 2 instances of Axios with different baseURL in the same app (vue.js)

I'm trying to learn vue.js so I made a little app that displays news articles from an API and, in another view, allows the user to log into another server.
For this I'm using Axios. I know I got it to work pretty well at some point, but today when starting my project, it's just impossible to get both apis to work simultaneously.
Here is my login service:
import axiosTrainingAPI from 'axios'
axiosTrainingAPI.defaults.baseURL = 'https://api.example.com'
const trainingAPI = {
login (credentials) {
return new Promise((resolve, reject) => {
axiosTrainingAPI.post('/services/auth.php', credentials)
.then(response => {
resolve(response.data)
}).catch(response => {
reject(response.status)
})
})
}
}
export default trainingAPI
Here is my news service:
import axiosGoogleNewsAPI from 'axios'
axiosGoogleNewsAPI.defaults.baseURL = 'https://newsapi.org'
const googleNewsAPI = {
getPosts (newsId) {
return new Promise((resolve, reject) => {
axiosGoogleNewsAPI.get(`/v2/everything?q=${newsId}&sortBy=publishedAt&apiKey=***********`)
.then(response => {
resolve(response.data)
}).catch(response => {
reject(response.status)
})
})
}
}
export default googleNewsAPI
Both those services are in different JS files and are imported in different vue files but it seems that now they cannot coexist and there is always one overwriting the baseURL of the other (not always the same) almost like if the Axios instance was the same in both cases. So some time the first service uses the second one's baseURL, sometimes it's the second that uses the first one's baseURL...
I don't know exactly the scope of 'import' because it's pretty new to me but both instances are in different files, have different names so I don't really understand how they get mixed up. Except if 'import' always calls the same instance of a module but then how do I work with 2 apis? And why did it work yesterday... I'm confused.
You'll want to create a new instance of Axios with a custom config for each API you want that has a distinct baseURL.
var instance = axios.create({
baseURL: 'https://example.com/api/',
timeout: 1000,
headers: {'X-Custom-Header': 'foobar'}
});
You can simply use multiple instances of Axios with each having its own configuration.
For example,
import axios from "axios";
// For common config
axios.defaults.headers.post["Content-Type"] = "application/json";
const mainAxios = axios.create({
baseURL: 'https://some-domain.example/api/'
});
const customAxios = axios.create({
baseURL: 'https://some-custom-domain.example/api/'
});
export {
mainAxios,
customAxios
};
Yea, for clarity:
let config = {baseURL: 'https://some-domain.example/api/',
timeout: 1000,
headers: {
'X-Custom-Header': 'foobar',
'Authorization' : `Bearer ${auth.token}` //where applicable
}
};
let instance = axios.create(config);
Also, You can specify config defaults that will be applied to every request.
axios.defaults.baseURL = 'https://api.example.com';
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-
urlencoded';
I had the same question and to solve it, I created an interface and a function (Example in TS):
export function createClient(baseURL: string) {
return axios.create({
baseURL: baseURL,
headers: { "Content-Type": "application/json" }
});
}
export interface ConfigurableApi {
configure(config: Configuration);
}
And for every client, I created a class
#Singleton()
export class ApiOfClientA implements ConfigurableApi {
client!: AxiosInstance;
configure(config: Configuration) {
this.client = createClient(config.baseURL);
}
...
}
If you want to use JS, you can probably do something like:
import axios from "axios";
let clientA;
const ClientA = {
init(baseURL) {
clientA = axios.create({
baseURL: `${baseURL}`,
headers: {"Content-Type": "application/json"}
});
},
...
};
export {ClientA};
and then just import it in the file you need to use it:
import {ClientA} from "./api/client-a";

Categories

Resources