I am currently making a NextJS website with KeystoneJS as the CMS. I can't find any good guides on how to set this up correctly so I have just jumped in. An issue that I am currently experiencing is that I am unable to request data from GraphQL. I can do it through the playground, just not the nextjs pages.
I have tried the following methods:
Axios => Module not found: Can't resolve 'async_hooks'
fetchAPI => Module not found: Can't resolve 'async_hooks'
getItems => Error: No executable schema named 'public' is available. Have you setup '#keystonejs/app-graphql'?
I am pretty sure that I should be using getItems since it is imported from #/keystonejs/server-side-graphql-client. However I'm not sure if I have set it up correctly.
My current project setup is installing KeystoneJS - Blank template. and then I have manually installed NextJS based on the installation guide. My module exports looks like this:
module.exports = {
keystone,
apps: [
new GraphQLApp(),
new AdminUIApp({
name: PROJECT_NAME,
enableDefaultRoute: false,
authStrategy,
isAccessAllowed: isAdmin,
})
],
};
Here is my getItems request:
export async function getStaticProps() {
const posts = await getItems({
keystone,
listKey: 'Post',
returnFields: 'name'
});
console.log(posts);
return {
props: {
posts,
},
}
};
When sending images via axios I found I have to use formdata. I add my images here but when sending the formdata my entire backend just freezes, just says "pending".
Ive been following this
And my attempt so far:
backend:
Apollo:
import { ApolloServer, makeExecutableSchema } from 'apollo-server-fastify';
const schema = makeExecutableSchema({ typeDefs, resolvers });
const apolloServer = new ApolloServer({
schema,
uploads: {
maxFileSize: 10000000,
maxFiles: 5,
},
});
(async function() {
app.register(apolloServer.createHandler({ path: '/api' }));
})();
schema:
scalar DateTime
scalar Upload
input addUser {
Email: String!
Password: String
FirstName: String!
LastName: String!
Age: DateTime!
JobTitle: String!
File: Upload
}
type Mutation {
register(input: addUser!): Boolean
}
resolver:
Mutation: {
register: async (obj, args, context, info) => {
// how to get the formData?
},
}
FrontEnd:
I build the request like this:
const getMutation = (mutate: MutationNames, returParams?: any): any => {
const mutation = {
login: print(
gql`
mutation($email: String!, $password: String!) {
login(email: $email, password: $password) {
token
refreshToken
}
}
`
),
register: print(
gql`
mutation(
$firstName: String!
$email: String!
$lastName: String!
$age: DateTime!
$jobTitle: String!
$file: Upload
) {
register(
input: {
FirstName: $firstName
LastName: $lastName
Email: $email
Age: $age
JobTitle: $jobTitle
File: $file
}
)
}
`
),
}[mutate];
if (!mutation) return {};
return mutation;
};
In this case im using the register mutation.
I have a few hooks on how I handle the data fetching so Im not going to include it since it is alot of code. The data is fetched correctly in the front end and before posting to the backend im putting everything to a formData object:
const submitForm: SubmitForm = (obj: SendObject) => {
const Fdata = new FormData();
Fdata.append('0', fileImp.file);
Fdata.append('operations', JSON.stringify(obj.data));
const map = {
'0': ['variables.file'],
};
Fdata.append('map', JSON.stringify(map));
callAxiosFn(
{
method,
url: 'http://localhost:4000/api',
data: Fdata,
// headers: obj.headers,
},
qlType.toString()
);
};
gets called like this:
const response = await axios({
headers: {
Accept: 'application/json',
'x-token': localStorage.getItem('token'),
'x-refresh-token': localStorage.getItem('refreshToken'),
...(config.headers || {}),
},
...config,
});
config is AxiosRequestConfig
What Im sending:
I dont exactly understand How the formdata will hit my resolver endpoint and for that reason im doing something wrong since the backend returns:
(node:748) UnhandledPromiseRejectionWarning: [object Array] (node:748)
UnhandledPromiseRejectionWarning: Unhandled promise rejection. This
error originated either by throwing inside of an async function
without a catch block, or by rejecting a promise which was not handled
with .catch(). (rejection id: 1) (node:748) [DEP0018]
DeprecationWarning: Unhandled promise rejections are deprecated. In
the future, promise rejections that are not handled will terminate the
Node.js process with a non-zero exit code.
I realize this is alot but Im at the end of my vits here, been at this the entire day. Any help is deeply appreciated.
EDIT:
Since my backend was questioned I thought I would just show that when sending data without appending Formdata like I do above then I get it working:
const submitForm: SubmitForm = (obj: SendObject) => {
callAxiosFn(
{
method,
url: 'http://localhost:4000/api',
data: obj.data,
},
qlType.toString()
);
};
obj.data is:
{query: "mutation ($firstName: String!, $email: String!, $l… Age: $age, JobTitle: $jobTitle, File: $file})↵}↵", variables: {…}}
query: "mutation ($firstName: String!, $email: String!, $lastName: String!, $age: DateTime!, $jobTitle: String!, $file: Upload) {↵ register(input: {FirstName: $firstName, LastName: $lastName, Email: $email, Age: $age, JobTitle: $jobTitle, File: $file})↵}↵"
variables:
age: "1977-04-04"
email: "JhoneDoe#hotmail.com"
file: File {name: "something.jpg", lastModified: 1589557760497, lastModifiedDate: Fri May 15 2020 17:49:20 GMT+0200 (centraleuropeisk sommartid), webkitRelativePath: "", size: 32355, …}
firstName: "Jhon"
jobTitle: "SomethingCool"
lastName: "Doe"
password: "CoolPassword!"123"
__proto__: Object
__proto__: Object
query getting sent in the browser:
Backend reciving the data but the image is not included:
EDIT:
Recently found that my fastify backend might have issues with reading formData.
tried installing
fastify-multipart
but got errors when registering it:
FST_ERR_CTP_ALREADY_PRESENT(contentType) ^ FastifyError
[FST_ERR_CTP_ALREADY_PRESENT]:
After that I tried:
npm uninstall fastify-file-upload
Error remained.
Well, I have not explored this topic yet. But I know that axios with GraphQL does not really work that well. Axios is made mainly for REST API calls. However, I really like and have learned a lot from this channel Ben Awad. The guy is really awesome and explains things clearly and nice. But the most important he is a GraphQL enthusiast and explores and presents various topic about it, as well with React.js, TypeORM & PostgreSQL. Here are some helpful links, from his channel, that might help with your issue:
Upload Files in GraphQL Using Apollo Upload
How to Upload a File to Apollo Server in React
I hope this helps! Please let me know if the content is helpful!
This took some time and usally when you take something for granted it takes time to find the mistake.
For anyone having the same problem please remember that the order you add something MATTERS!
What I did:
const Fdata = new FormData();
Fdata.append('0', fileImp.file); // NOTICE THIS
Fdata.append('operations', JSON.stringify(obj.data));
const map = { // NOTICE THIS
'0': ['variables.file'],
};
Fdata.append('map', JSON.stringify(map));
Problem:
Remember when I said order of appending things matter? Well the case here was that the mapping was added after the file was added.
The correct way:
const Fdata = new FormData();
Fdata.append('operations', JSON.stringify(obj.data));
const map = { // NOTICE THIS
'0': ['variables.file'],
};
Fdata.append('map', JSON.stringify(map));
Fdata.append('0', fileImp.file); // NOTICE THIS
Also note that in my qestion I missed setting the file itself to null in the variables:
variables: {
file: null,
},
This has to be done.
For more info read here
#CodingLittle glad you figured out the answer was related to the multipart form field ordering.
Some things to add (answering as I don't have the 50 reputation required to make a comment on your answer, despite being the graphql-upload author)…
Also note that in my qestion I missed setting the file itself to null in the variables
This is true, and good to get right, although in reality a lot of GraphQL multipart request spec server implementations will simply replace whatever is at the mapped path for a file with the upload scalar value without caring what was there — in theory, you could replace files in variables with asdf instead of null and it would still work. JSON.stringify would have replaced the file instances with something like {}.
A lot of your headaches could have been avoided if the backend responded with a clear 400 status and descriptive error message instead of throwing a gnarly UnhandledPromiseRejectionWarning error. If your graphql-upload dependency was up to date on the backend, you should have seen a descriptive error message when the requests were not conforming to the GraphQL multipart request spec regarding field ordering, as can be seen in the graphql-upload tests:
https://github.com/jaydenseric/graphql-upload/blob/v11.0.0/test/public/processRequest.test.js#L929
Try running npm ls graphql-upload in your backend project to check only one version is installed, and that it’s the latest published to npm (v11 at the time of this answer). Note that if you’re relying on Apollo Server to install it for you, they use a very out of date version (v8).
I'm new to nodejs and marklogic, and I'm following a tutorial for a simple app, I have setup and configured my marklogin login credentials,
when I run this sample code by running node sample.js
the output is write document list cannot process response with 404 status
I wonder why I'm encountering this error,
here is the code from the tutorial,
my-connection.js
module.exports = {
connInfo: {
host: '127.0.0.1',
port: 8001,
user: 'user',
password: 'password'
}
};
sample.js
const marklogic = require('marklogic');
const my = require('./my-connection.js');
const db = marklogic.createDatabaseClient(my.connInfo);
const documents = [
{ uri: '/gs/aardvark.json',
content: {
name: 'aardvark',
kind: 'mammal',
desc: 'The aardvark is a medium-sized burrowing, nocturnal mammal.'
}
},
{ uri: '/gs/bluebird.json',
content: {
name: 'bluebird',
kind: 'bird',
desc: 'The bluebird is a medium-sized, mostly insectivorous bird.'
}
},
{ uri: '/gs/cobra.json',
content: {
name: 'cobra',
kind: 'mammal',
desc: 'The cobra is a venomous, hooded snake of the family Elapidae.'
}
},
];
db.documents.write(documents).result(
function(response) {
console.log('Loaded the following documents:');
response.documents.forEach( function(document) {
console.log(' ' + document.uri);
});
},
function(error) {
console.log('error here');
console.log(JSON.stringify(error, null, 2));
}
);
I hope someone can tell me what is wrong with the code,
Thank You!
The MarkLogic NodeJS Client library is meant to run against a so-called MarkLogic REST-api instance. There is typically one running at port 8000, but you can also deploy other ones at different ports by issuing a POST call to :8002/v1/rest-apis, as described here:
http://docs.marklogic.com/REST/POST/v1/rest-apis
Port 8001 however is reserved for the MarkLogic Admin UI, which doesn't understand the REST calls that the NodeJS Client library is trying to invoke, hence the 404 (not found)..
HTH!
I have migrated data from the parse website to Azure's version of parse and notice some components were missing like an email adapter. So I follow the instructions from here https://www.npmjs.com/package/parse-server-postmark-adapter. I'm able to receive email to change my password.
But I get this error when I click on the link to change my password,
"level":"error","message":"Uncaught internal server error. [Error: Can't set headers after they are sent.]
Can anyone explain why I'm getting this message? Also, I put the code to configure postmark in my config.js file.
Edit:
var PostmarkAdapter = require('parse-server-postmark-adapter');
module.exports = {
server: {
appName: 'myapp',
publicServerURL: 'http://myapp.azurewebsites.net/parse',
verifyUserEmails: true, // Enable email verification
emailAdapter: PostmarkAdapter({
apiKey: 'api-key-0000',
fromAddress: 'someemail#email.com',
})
},
dashboard: {},
storage: {},
push: {}
}
I have a migrated Parse application running on Parse 2.1.6. It uses a simple MongoDB 3.0.8 (RocksDB engine) setup, with authentication. The user it's using has the permissions:
[{ "role" : "dbAdmin", "db" : "parse" },
{ "role" : "readWrite", "db" : "parse"}]
When the Parse server boots up or tries to handle any request, I get:
[MongoError: not authorized for query on parse._SCHEMA]
Which seems very weird, given that:
the user clearly has permissions to query any collection on this database,
that same user is indeed able to query this collection via mongo console or robomongo,
Parse.com works normally when using this database, with that same user,
I can actually run the same Parse application locally, pointing to this same remote MongoDB with that user, and it works without such errors.
This is kinda crazy. I get the feeling that I'm missing something pretty basic here. Any thoughts?
UPDATE:
As request, sample code showing how the server is booting:
var api = new ParseServer({ appId: 'the app id',
masterKey: 'the master key',
clientKey: 'client key',
cloud: '/absolute_path/cloud/main.js',
serverURL: 'https://myserver.com/parse',
push:
{ ios:
{ production: false,
bundleId: 'thebundleid.com',
pfx: <Buffer ... > } },
oauth: { facebook: { appIds: 'appId' } },
databaseURI: 'mongodb://user#pass:127.0.0.1:27017'
});
app.use('/parse', api);