Trying to make a Nextjs app with apollo, graphql and prisma. When I'm trying to query users on my front end, I'm getting a 400 error. The query works in apollo studio / sandbox so I have no idea how to fix it.
error.message: Response not successful: Received status code 400
And nothing in the server log.
schema.ts:
export const typeDefs = gql`
type User {
id: String
name: String
email: String
image: String
}
type Query {
AllUsersQuery: [User]!
}
my resolver.ts:
export const resolvers = {
Query: {
AllUsersQuery: async (_parent: any, __args: any, context: any) => await context.prisma.user.findMany(),
},
};
And where I'm calling it from board.tsx:
const AllUsersQuery = gql`
query {
user{
id
name
email
}
}
`
const Board = () => {
const { data, loading, error } = useQuery(AllUsersQuery);
if (loading) return <div> Loading... </div>
if (error) return <div> Oops something went wrong: {error.message}</div>
return (
<>
<div>{data?.user.email}</div>
</>
)
}
export default Board
response/request header:
XHRPOSThttp://localhost:3000/api/graphql
[HTTP/1.1 400 Bad Request 13ms]
POST
http://localhost:3000/api/graphql
Status
400
Bad Request
VersionHTTP/1.1
Transferred766 B (1.21 kB size)
Referrer Policystrict-origin-when-cross-origin
HTTP/1.1 400 Bad Request
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Content-Type: application/json
Vary: Accept-Encoding
Content-Encoding: gzip
Date: Sun, 02 Oct 2022 03:20:09 GMT
Connection: keep-alive
Keep-Alive: timeout=5
Transfer-Encoding: chunked
POST /api/graphql HTTP/1.1
Host: localhost:3000
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:105.0) Gecko/20100101 Firefox/105.0
Accept: */*
Accept-Language: en-CA,en-US;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate, br
Referer: http://localhost:3000/board
content-type: application/json
Content-Length: 91
Origin: http://localhost:3000
Connection: keep-alive
Cookie: _ga=GA1.1.1971964746.1663710154; next-auth.csrf-token=33c501d5216dea4b6a029d34c13d640814228810867843882008780ce09eb536%7C83d7939882d2f38e49f72a501e895459abb7fbac2fbab0d106c6462fe35cbe7e; next-auth.callback-url=http%3A%2F%2Flocalhost%3A3000%2Flogin; next-auth.session-token=c7358df1-2605-4a34-bb5d-a1091a00b871
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
What am I doing wrong? Thanks
It appears you need to clean and flip some naming conventions. You likely want to define a resolver on your root Query type simply as users, which in turn is the field you'll access on the returned optional (data) on execution. Defining the query in this way provides flexibility; for example perhaps you add a filter arg in the future with a simple id or more complex input type. In the case, the lack of flexibility becomes evident.
export const typeDefs = gql`
type User {
id: String
name: String
email: String
image: String
}
type Query {
users: [User]!
}
// update resolver to reflect schema change on Query
Query: {
users: async (_parent: any, __args: any, context: any) => await context.prisma.user.findMany(),
},
Now, for your (currently filterless) query operation it makes sense to declare AllUsersQuery, an accurate description of your client implementation. In the case you added an arg to fetch a subset (which would require updating resolver to something like users(ids: [String]): [User], a different naming convention could be provided for the operation (UsersByIdsQuery)
const AllUsersQuery = gql`
query {
users {
id
name
email
}
}
`
const Board = () => {
const { data, loading, error } = useQuery(AllUsersQuery);
if (loading) return <div> Loading... </div>
if (error) return <div> Oops something went wrong: {error.message}</div>
return (
<>
<div>{data?.users.email}</div>
</>
)
}
export default Board
If you wanted to leave your schema, resolver, and operation as is, you would need to update the field requested on your query as reflected in your schema, and similarly access that field on the data optional in your code.
const AllUsersQuery = gql`
query {
AllUsersQuery {
id
name
email
}
}
Related
I am running a Vue project on my local dev server with a firebase function also running on local dev. Whenever I try to make a fetch request to my "beckend" I get a CORS error.
PREFLIGHT REQEUST
OPTIONS /api/url HTTP/1.1
Host: localhost:5001
Connection: keep-alive
Accept: */*
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type
Origin: http://localhost:8080
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site
Sec-Fetch-Dest: empty
Referer: http://localhost:8080/
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
RESPONSE
HTTP/1.1 400 Bad Request
x-powered-by: Express
access-control-allow-origin: http://localhost:8080
access-control-allow-methods: POST, OPTIONS
access-control-allow-headers: Content-Type
content-type: application/json; charset=utf-8
content-length: 44
etag: W/"2c-1mdAJaORqKZ8xUSbM/cjasU4RC0"
date: Tue, 20 Jul 2021 14:40:25 GMT
connection: keep-alive
keep-alive: timeout=5
Here's my code:
FRONTEND
fetch(/api/url, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
currency: "usd",
paymentMethodType: "card",
amount: 1880,
}),
}).then();
BACKEND
exports.myFunctionName = functions.https.onRequest(async (req, res) => {
const origin = req.headers.origin;
if (ALLOWED_ORIGINS.includes(origin)) {
res.setHeader("Access-Control-Allow-Origin", origin);
}
res.setHeader("Access-Control-Allow-Methods", "POST, OPTIONS");
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
const {paymentMethodType, currency, amount} = req.body;
const params = {
payment_method_types: [paymentMethodType],
amount: +amount,
currency: currency,
};
try {
// Create a PaymentIntent with the amount, currency, and a payment method type.
const paymentIntent = await stripe.paymentIntents.create(params);
// Send publishable key and PaymentIntent details to client
res.status(200).json({
clientSecret: paymentIntent.client_secret,
});
} catch (e) {
return res.status(400).json({
error: {
message: e.message,
},
});
}
}
I can't seem to figure this out, I've been working at it for a few hours. Can anyone help?
The problem is your function treats the preflight request as if it were the actual POST request, but they're separate and not sent simultaneously.
The browser automatically sends the OPTIONS preflight request (which has no body) before the POST. Your function tries to pass non-existent body parameters from OPTIONS to the Stripe API, resulting in an exception caught by your catch handler, which responds with a 400.
The backend function should respond to OPTIONS with an ok status (e.g., 200) before the browser can send the POST request:
exports.myFunctionName = functions.https.onRequest(async (req, res) => {
// Handle preflight request
if (req.method === "OPTIONS") {
// allow `POST` from all origins for local dev
res.set("Access-Control-Allow-Origin", "*");
res.setHeader("Access-Control-Allow-Methods", "POST");
return res.sendStatus(200);
} else {
// Handle `POST` request here...
}
}
I not understand how to invalidate cache, because my appliaction not called when I return request with Expires header in first time and then application not called when try to call same request again
$response
->setExpires($this->helper->getExpiresHttpCache());
And I added class
class CacheKernel extends HttpCache
{
protected function invalidate(Request $request, $catch = false)
{
if ('PURGE' !== $request->getMethod()) {
return parent::invalidate($request, $catch);
}
if ('127.0.0.1' !== $request->getClientIp()) {
return new Response(
'Invalid HTTP method',
Response::HTTP_BAD_REQUEST
);
}
$response = new Response();
if ($this->getStore()->purge($request->getUri())) {
$response->setStatusCode(Response::HTTP_OK, 'Purged');
} else {
$response->setStatusCode(Response::HTTP_NOT_FOUND, 'Not found');
}
return $response;
}
protected function getOptions()
{
return [
'default_ttl' => 0,
];
}
}
and in index.php add
$kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']);
// Wrap the default Kernel with the CacheKernel one in 'prod' environment
if ('prod' === $kernel->getEnvironment()) {
$kernel = new CacheKernel($kernel);
}
but http cache works for each envirometns. Only when in browser disable checkbox Disable cache application will calling.
In browser I had correct header Expires: Fri, 01 May 2020 11:59:00 GMT. And if sent request again, with enable debug, debug not entered in application, but response contain result data. In this case General section contain Status Code: 200 OK (from disk cache)
Request URL: http://symfony.localhost/api/products/extra_fields
Request Method: GET
Status Code: 200 OK (from disk cache)
Remote Address: [::1]:80
Referrer Policy: no-referrer-when-downgrade
But wehere this point where compare current time and exprires time ? Example I want use global variables for expires time. Example after call some another api request api/product/{id} I want to change expires time for /api/products/extra_fields, but aplication not called, where I can manage it or it possible ? Could someone help me figure out how to manage it ?
Response
HTTP/1.1 200 OK
Server: nginx
Content-Type: application/json
X-Powered-By: PHP/7.3.14
Cache-Control: private, must-revalidate
Date: Fri, 01 May 2020 07:39:35 GMT
Expires: Fri, 01 May 2020 11:59:00 GMT
Request
GET /api/products/extra_fields HTTP/1.1
accept: application/json
Referer: http://symfony.localhost/api/doc
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36
I am trying to apply JWT(JSON Web Token) in Meteor server-side API's with Iron-router.
I am able to create token but I can't get method in iron-router to write middleware for verifying that token.
Can you Please explain Which method is used to write middleware and how to define it ?
In iron-router, middleware is defined using the onBeforeAction method (see "Server Hooks and Connect" in the guide). So for JWT verification, you can use something like this:
import { Meteor } from 'meteor/meteor';
import { Router } from 'meteor/iron:router';
import jwt from 'jsonwebtoken';
Router.onBeforeAction(function(req, res, next) {
jwt.verify(req.query.token, "secret", (err, decoded) => {
if (err) {
console.log(`Failed to verify token. Error: "${err}"`);
res.statusCode = 401;
res.end('invalid token');
} else {
next();
}
});
}, {where: 'server'});
// example server-side route for testing
Router.route('/server', { where: 'server' })
.get(function () {
this.response.end('ok');
})
Failing example (true negative):
> curl -i localhost:3000/server?token=totally-wrong
HTTP/1.1 401 Unauthorized
date: Mon, 25 Nov 2019 16:41:33 GMT
connection: keep-alive
transfer-encoding: chunked
invalid token
Succeeding example (true positive):
> curl -i localhost:3000/server?token=eyJhbGciOiJIUzI1NiJ9.MTIzNDU2Nzg5MA.oy8UH-Y_-5ztrLKo8zgx-8b8AKvUTy4ijH-ItJU32qo
HTTP/1.1 200 OK
date: Mon, 25 Nov 2019 16:41:52 GMT
connection: keep-alive
transfer-encoding: chunked
ok
There're a handful of ways to achieve this, this simplest in my opinion is to apply your logic within the action method of the route like so:
Router.route("/dashboard", {
name: "dashboard",
action: function() {
const token = this.params.query.token
try {
var decoded = jwt.verify(token, "secret");
this.render()
} catch (err) {
this.render("home"); // go home
}
}
});
You may also try tinkering with hooks or plugins as mentioned in the docs.
I am working on simple web app on port 4000 with a RESTful API on port 8080. I have Express setup with the cors package and I seem to be sending the correct headers back from an OPTIONS request, see below:
X-Powered-By: Express
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET,HEAD,PUT,PATCH,POST,DELETE
Vary: Access-Control-Request-Headers
Access-Control-Allow-Headers: content-type
Content-Length: 0
This is returned with a status 204 ok. Then I see the PUT request in FF Network console and after about 45 secs the request header appears:
PUT /movies/updatemovie/59f4ee92be4becd967a573ca HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:56.0) Gecko/20100101 Firefox/56.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/json
Referer: http://localhost:4000/movies
Content-Length: 151
Origin: http://localhost:4000
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
The request parameters are:
documentID=59f4ee92be4becd967a573ca&title=Captain%20America%3A%20Civil%20UnWar&release=2016&score=9&reviewer=%20Janet%20Garcia&publication=%20MoviesNow
Chrome shows the PUT as having failed after 4mins.
The Data is not getting posted to Mongo, nor do I get any response from the server.
Here is the relevant code:
Web App
$(".panel").each(function (index) {
if ($(this).find("input[name = 'update']").is(":checked")) {
updatedDocument = {
documentID: $(this).data("id"),
title: $(this).find("input#name").val(),
release: $(this).find("input#release").val(),
score: $(this).find("input#score").val(),
reviewer: $(this).find("input#reviewer").val(),
publication: $(this).find("input#publication").val()
};
JSON.stringify(updatedDocument);
console.log(Object.values(updatedDocument));
} // end if
}); // .each
// Now we have the document stored in JSON object, so lets form
// an AJAX req and grab the updated data from our document and send
// a PUT to our API endpoint
$.ajax({
type: 'PUT',
data: updatedDocument,
url: 'http://localhost:8080/movies/updatemovie/' + updatedDocument.documentID,
dataType: 'JSON',
contentType: 'application/json'
}).done(function (response) {
// Check for successful (blank) response
if (response.msg === '') {
// do nothing
}
else {
alert('Error: ' + response.msg);
}
}); // end .done
API
/*
* Put to Update movie.
*/
router.options('updatemovie/:id', cors());
router.put('/updatemovie/:id', cors(), function(req, res) {
const db = req.db;
console.log(req.params.id);
console.log(req.body.publication);
const movieCollection = db.get("movies");
const movieToUpdate = req.params.id; // Assign collection document id from url :id value
const movieTitle = req.body.title;
const movieRelease = req.body.release;
const movieScore = req.body.score;
const movieReviewer = req.body.reviewer;
const moviePublication = req.body.publication;
// Update the movie document from PUT info
movieCollection.update({'_id' : movieToUpdate},
{
$set: {
title: movieTitle,
release: movieRelease,
score: movieScore,
reviewer: movieReviewer,
publication: moviePublication
}
},
function(err) {
res.send((err === null) ? {msg: ''} : {msg: err});
}); // movieCollection .update
});
I get nothing from the console.logs in the API. Any suggestions most welcomed.
UPDATE: By removing line from APP contentType: application/JSON everything now works. I thought I wanted to send data as JSON to my API? Anybody who has any thoughts or input most welcome.
You aren't sending the data as JSON. You've set the content-type header to application/json but the data will still be encoded as application/x-www-form-urlencoded. To send JSON data using jQuery you'd need to encode it yourself:
data: JSON.stringify(updatedDocument),
On the server you'd then need a suitable bodyParser configuration:
app.use(bodyParser.json());
or:
app.use(express.json());
When you removed the line contentType: 'application/json' that header fell back to its default value of application/x-www-form-urlencoded. That matches the format of the data and presumably you have app.use(bodyParser.urlencoded()) or app.use(express.urlencoded()) configured to parse that data. As your data is a flat string/string data structure it doesn't really matter which format you choose, you'll end up with the same values in res.body.
I've read node.js Extracting POST data.
But here's my problem, how to extract POST data with Express when I received a HTTP request looking like this?
POST /messages HTTP/1.1
Host: localhost:3000
Connection: keep-alive
Content-Length: 9
User-Agent: Mozilla/5.0 (X11; Linux i686) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.52 Safari/536.5
Content-Type: application/xml
Accept: */*
Accept-Encoding: gzip,deflate,sdch
Accept-Language: zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4 Accept-Charset: UTF-8,*;q=0.5
msg=hello
I can't seem to get the msg=hello key-value pair out of the body with Express.
I've tried all of these methods req.header() req.param() req.query() req.body but they seem to be empty.
How to get the body's content?
app.post('/messages', function (req, res) {
req.??
});
Your problem is bodyParser does not handle 'application/xml', I solved this mainly by reading this post: https://groups.google.com/forum/?fromgroups=#!topic/express-js/6zAebaDY6ug
You need to write your own parser, I've published the below with more detail to github:
https://github.com/brandid/express-xmlBodyParser
var utils = require('express/node_modules/connect/lib/utils', fs = require('fs'), xml2js = require('xml2js');
function xmlBodyParser(req, res, next) {
if (req._body) return next();
req.body = req.body || {};
// ignore GET
if ('GET' == req.method || 'HEAD' == req.method) return next();
// check Content-Type
if ('text/xml' != utils.mime(req)) return next();
// flag as parsed
req._body = true;
// parse
var buf = '';
req.setEncoding('utf8');
req.on('data', function(chunk){ buf += chunk });
req.on('end', function(){
parser.parseString(buf, function(err, json) {
if (err) {
err.status = 400;
next(err);
} else {
req.body = json;
next();
}
});
});
}
then use it with
app.use (xmlBodyParser);
If you have this in the config:
app.use(express.bodyParser());
And this in your view:
form(name='test',method='post',action='/messages')
input(name='msg')
Then this should work:
app.post('/messages', function (req, res) {
console.log(req.body.msg);
//if it's a parameter then this will work
console.log(req.params.msg)
});
I believe you need to configure express to use the bodyParser middleware.
app.use(express.bodyParser());
See the express documentation.
It says:
For example we can POST some json, and echo the json back using the bodyParser middleware which will parse json request bodies (as well as others), and place the result in req.body
req.body() should now return the expected post body.
I hope this helps!
It's POSSIBLE (not sure what it depends on, but it happened to me once, it might be the bodyParser) that the request body is formatted in such a way that your JSON data is ITSELF being treated as a key in a key-value pair, with a blank corresponding value. What's worked for me in this situation was to extract the JSON object first and then proceed as normal:
var value;
for (var item in req.body)
{
var jObject = JSON.parse(item);
if (jObject.valueYouWant != undefined)
{
value = jObject.valueYouWant;
}
}
This is probably pretty suboptimal, but if nothing else works (I tried for ages trying to find a better way and found none) this might work for you.
You are posting xml as I can see, the answers you got were based on JSON input. If you want the content of your xml displayed, process the raw request :
app.post('/processXml',function (req, res)
{
var thebody = '';
req.on('data' , function(chunk)
{
thebody += chunk;
}).on('end', function()
{
console.log("body:", thebody);
});
});
As an example using curl as your postman:
curl -d '<myxml prop1="white" prop2="red">this is great</myxml>' -H
"Content-type: application/xml" -X POST
http://localhost:3000/processXml
Outputting:
'<myxml prop1="white" prop2="red">this is great</myxml>'
Make sure your body-parser middleware doesn't get in the way: body-parser-xml processes your request object on the fly to a json object, after which you cannot process your raw request anymore. (And you can guess who was stuck several hours after this...)