Apollo Server, Graphql - Must provide query string - javascript

Im not sure what im doing wrong here? I've been stuck now for soem time on getting my mutations to run with my apollo-server-lambda in my serverless setup, my queries works fine bu when i try to run a query like this:
{ "mutation": "{ signIn(username: \"SomeUser\", password: \"SomePassword\" ) { token } }" }
I just get the message: " Must provide query string." status 400.
I've set up my resolver like so:
const resolvers = {
Query: {
users: async (_, args, ctx) => User.load(args, ctx)
},
Mutation: {
signIn: async (_, { username, password }, ctx) => Auth.signIn({ username, password }, ctx)
}
};
For additional infor here is my typeDefs:
const typeDefs = gql`
type User {
id: ID!,
firstname: String,
lastname: String,
username: String,
createdAt: String,
role: String
}
type AuthToken {
token: String
}
type Query {
hello: String,
users(id: Int): [User]
}
type Mutation {
signIn(username: String!, password: String!): AuthToken!
}
`;
I'm using postman to test my graphql endpoint and my content type is application/json
I dont know if any one here can tell me what im doing wrong, i tryed to move it all to Query resolver, and it works replace "mutation" with "query" then but it dosent make sens to me using the "query" here and i guess later on when i actually want to use the Mutation to mutate data i would need this to work anyway?
Can any one tell me where im wrong here?
EDIT
I installed: graphql-playground-middleware-lambda and set up the serverless setup with: https://github.com/prisma/graphql-playground#as-serverless-handler and if i use Graphiql it works as intented, but im still interested if any one knows whats wrong with the json i send via postman?

When sending the request, your request body should be a properly-formatted JSON object, with a query property (and optionally, a variables property if including variables):
{
"query": "<GraphQL Document>",
"variables {},
}
This is the case whether the operation itself is a query or a mutation.
The actual value of the query property above must be a syntactically correct document, as outlined in the GraphQL specification. A document will typically consist of a single operation definition (either a query or a mutation) that includes all the requested fields for that operation. The document will also include fragments, if using any.
An operation definition looks like this:
OperationType [Name] [VariableDefinitions] [Directives] SelectionSet
So you could have a document like this:
mutation SomeMutation {
signIn(username: "SomeUser", password: "SomePassword") {
token
}
}
Here, the type of the operation is mutation, the name is SomeMutation and everything between the outermost set of curly brackets is the selection set. If you had any variables, their types would be declared in parentheses before the selection set.
The operation name is optional, but it's helpful to include it for debugging purposes on the backend. Technically, the operation type can be omitted as well, in which case GraphQL simply assumes the type is a query. For example, this is still a valid document:
{
users {
id
}
}
and is equivalent to
query SomeName {
users {
id
}
}
The former is referred to as query shorthand. Obviously, this cannot be used for mutations, so mutations must always explicitly state their operation type. A complete example:
{
"query": "mutation SomeName ($username: String!, $password: String!) { signIn(username: $username, password: $password) { token } }",
"variables {
"username": "SomeUser",
"password": "SomePassword"
},
}

Related

Mongoose - Deleting documents is unresponsive

I'm trying to use Mongoose (MongoDB JS library) to create a basic database, but I can't figure out how to delete the documents / items, I'm not sure what the technical term for them is.
Everything seems to work fine, when I use Item.findById(result[i].id), it returns a valid id of the item, but when I use Item.findByIdAndDelete(result[i].id), the function doesn't seem to start at all.
This is a snippet the code that I have: (Sorry in advance for bad indentation)
const testSchema = new schema({
item: {
type: String,
required: true
},
detail: {
type: String,
required: true
},
quantity: {
type: String,
required: true
}
})
const Item = mongoose.model("testitems", testSchema)
Item.find()
.then((result) => {
for (i in result) {
Item.findByIdAndDelete(result[i].id), function(err, result) {
if (err) {
console.log(err)
}
else {
console.log("Deleted " + result)
}
}
}
mongoose.connection.close()
})
.catch((err) => {
console.log(err)
})
I'm not sure what I'm doing wrong, and I haven't been able to find anything on the internet.
Any help is appreciated, thanks.
_id is a special field on MongoDB documents that by default is the type ObjectId. Mongoose creates this field for you automatically. So a sample document in your testitems collection might look like:
{
_id: ObjectId("..."),
item: "xxx",
detail: "yyy",
quantity: "zzz"
}
However, you retrieve this value with id. The reason you get a value back even though the field is called _id is because Mongoose creates a virtual getter for id:
Mongoose assigns each of your schemas an id virtual getter by default which returns the document's _id field cast to a string, or in the case of ObjectIds, its hexString. If you don't want an id getter added to your schema, you may disable it by passing this option at schema construction time.
The key takeaway is that when you get this value with id it is a string, not an ObjectId. Because the types don't match, MongoDB will not delete anything.
To make sure the values and types match, you should use result[i]._id.

How to resolve validation error when querying with graphql? (typescript)

I am trying to query by passing in the name field but I get two different errors.
"Validation error of type MissingFieldArgument:
Missing field argument id # 'getBlog'"
"Validation error of type UnknownArgument:
Unknown field argument name # 'getBlog'"
I was able to successfully query it with the id field. Im new to graphql and im using this on my app that also uses aws amplify and aws appsync.
schema.graphql
type Blog #model {
id: ID!
name: String!
posts: [Post] #connection(keyName: "byBlog", fields: ["id"])
}
queries.ts
// this is an auto generated file. This will be overwritten
export const getBlog = /* GraphQL */ `
query GetBlog($name: String!) { //changed from ($id: ID!)
getBlog(name: $name) { //changed from (id: $id)
id
name
posts {
items {
id
title
blogID
createdAt
updatedAt
}
nextToken
}
createdAt
updatedAt
}
}
`;
app.tsx
const getRecord = async () => {
const result = await API.graphql(graphqlOperation(getBlog, {name: "testing"}))
console.log(result)
}
I also tried pushing it to aws amplify push but it detected no changes. I didnt expect it to as i didnt change anything in the schema, only the queries. thanks in advance
If you look at your GraphQL schema you should see the definition for the GetBlog query. It likely looks like:
query {
GetBlog(id: ID!): Blog
}
That particular query can only be queried by id. Assuming you have control of the server you should also be able to define a GetBlogByName query:
query {
GetBlogByName(name: String): Blog
}
But then it will be up to you to ensure that name is unique or deal with potentially >1 response.

Getting error when using objection JS to create query: NJS-012: encountered invalid bind data type in parameter 2

I have been spending hours trying to debug this error. I am using Node, Joi and Oracledb. When I attempted to make a POST request to insert my data from the payload of the request to my table, it gives me the error: NJS-012: encountered invalid bind data type in parameter 2. Nothing I did has been productive to fix the issue. What did I do wrong to cause this issue? The code I use to update my table is in this manager:
manager.js
async function create(payload) {
try {
return await MassRepublishJob.query().insertAndFetch(payload)
} catch (error) {
log.error(`Error while creating mass republish jobs with payload: ${JSON.stringify(payload)}`, error)
}
}
This manager code is called from controller code:
Controller.js
async save(request, reply) {
const instance = await massRepublishJobManager.create(request.payload)
reply(instance)
}
This controller is the handler of my POST route:
method: 'POST',
path: `/${root}`,
config: {
tags: ['api'],
handler: controller.save,
description: 'Create new mass republish job',
notes: 'You can create a mass republish job by either sending a list of content ids or send an object of search querys',
validate: {
payload: {
counts: Joi.object().example(Joi.object({
total: Joi.number().example(1),
completed: Joi.number().example(1),
failed: Joi.number().example(0),
queued: Joi.number().example(0),
})),
type: Joi.string().example('manual'),
content_ids: Joi.array().items(Joi.number().example(11111)),
search_query: Joi.object().example(Joi.object({
query: Joi.string().example('string'),
})),
republishing_reason: Joi.string().example('string'),
duration: Joi.number().example(0),
}
}
}
And finally, I set up the table as described in this sql file, the fields "creation_date", "last_update_date", "created_by", "last_updated_by" are the field of the base model. This model extends that base models with extra fields: "counts", "type", "content_ids", "search_query", "republishing_reason", "duration"
CREATE TABLE wwt_atc_api.mass_republish_jobs (
id NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
counts CLOB DEFAULT('{}') CHECK(counts IS JSON), -- JSON object of
counts: each has completed, failed, total, queued
type VARCHAR(255) NOT NULL, -- right now only support 'manual' but can be more in the future
content_ids CLOB DEFAULT('[]'),
search_query CLOB DEFAULT('{}') CHECK(search_query IS JSON), -- JSON object of query: each has field, operator, value
republishing_reason VARCHAR(255),
duration NUMBER NOT NULL,
creation_date DATE NOT NULL,
last_update_date DATE NOT NULL,
created_by NUMBER NOT NULL,
last_updated_by NUMBER NOT NULL
);
GRANT SELECT, INSERT, UPDATE, DELETE ON WWT_ATC_API.mass_republish_jobs TO wwt_cf_atc_api;
Knex instance also prints out this debugging message:
method: 'insert',
options: {},
timeout: false,
cancelOnTimeout: false,
bindings: [
[ { id: 11111 } ],
'{"total":1,"completed":1,"failed":0,"queued":0}',
310242,
2022-06-23T18:53:28.463Z,
0,
310242,
2022-06-23T18:53:28.463Z,
'string',
'{"query":"string"}',
'manual',
ReturningHelper { columnName: 'ID' }
],
__.knexQueryUid: 'c5885af0-f325-11ec-a4bd-05b5ac65699c',
sql: 'insert into "WWT_ATC_API"."MASS_REPUBLISH_JOBS" ("CONTENT_IDS", "COUNTS",
"CREATED_BY", "CREATION_DATE", "DURATION", "LAST_UPDATED_BY", "LAST_UPDATE_DATE",
"REPUBLISHING_REASON", "SEARCH_QUERY", "TYPE") values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
returning "ID" into ?',
outBinding: [ [ 'ID' ] ],
returning: [ 'ID' ]
I would really appreciate any helps to get me debug this error.
The issue here is that CONTENT_IDS is not being stringified by Knex because the value is an array. This is a well known issue with Knex that occurs because its QueryBuilder does not have any metadata to determine what format it needs to convert a given binding value into in order for that value to be acceptable to underlying driver/database. Typically objects are stringified but arrays are left as is -hence the inconsistent behavior you are seeing in the bindings. The developers of Knex thus recommend stringifying all JSON values before passing them to the QueryBuilder.
In order to ensure that stringification always properly occurs on JSON values, you should define jsonAttributes on your MassRepublishJob Objection Model.
class MassRepublishJob extends Model {
...
static get jsonAttributes() {
return ["counts", "content_ids","search_query"];
}
}
This will ensure both that your model stringifies these values before binding and that it parses the stringified JSON when mapping rows from the database.
In addition, you can use the $parseJson or $toDatabaseJson lifecycle methods to manually modify how the Objection Model will bind arguments before any queries are run. You can further utilize objection helpers like val,raw and fn in $parseJson for fine grained control on how values for your model properties are bound.
import {fn, ...} from "objection"
...
class MassRepublishJobs extends Model {
...
$parseJson(json: Pojo, opt: ModelOptions) {
const superJson = super.$parseJson(json, opt);
superJson.job_id = randomUUID();
// will format sql with: to_date(?,?) and safely bind the arguments
superJson.creation_date = fn('to_date',new Date().toIsoString(),dateFormat);
return superJson;
}

Apollo client and graphQl query error : Variable '$where' expected value of type 'OrganizationWhereUniqueInput

Hi I'm making a backend server with GraphQL, Apollo client & Prisma. I'm trying to write a query where I get organization data back. The user who sends the query should get its organization data back based on their id. When running the query in playground I get this error.
error:
"message": "Variable '$where' expected value of type 'OrganizationWhereUniqueInput!' but got: {\"employees\":{\"id\":\"ckas83z13t9qk0992pucglc4k\"}}. Reason: 'employees' Field 'employees' is not defined in the input type 'OrganizationWhereUniqueInput'. (line 1, column 8):\nquery ($where: OrganizationWhereUniqueInput!) {\n ^",
I don't see what I did wrong. I'm still pretty new to it all. I tried to write the function in Query.js in different ways but no luck. Also, I still find the error messages you get in playground very confusing
schema:
type Query {
getOrganization: Organization!
}
type Organization {
id: ID!
name: String!
country: String!
street: String!
zipCode: Int!
houseNumber: Int!
addings: String
employees: [User!]
}
type User {
id: ID!
firstname:String!
lastname:String!
email: String!
services: [Service!]
organization: Organization!
}
query.js
function getOrganization(parent, args, context, info){
const userId = getUserId(context)
return context.prisma.organization({employees:{id:userId}})
}
// also tried this
/*
function getOrganization(parent, args, context, info){
const userId = getUserId(context)
return context.prisma.organization({where: {employees:{id:userId}}})
}*/
User.js
function services (parent, args, context){
return context.prisma.user({id: parent.id}).services()
}
function organization (parent, args, context){
return context.prisma.user({id: parent.id}).organization()
}
module.exports={
services,
organization
}
Organization.js
function employees(parent, args, context){
return context.prisma.organization({id: parent.id}).employees()
}
module.exports={
employees
}
Could anyone help me see what went wrong?
query in playground:
query{
getOrganization{
name
id
}}
HTTP HEADER:
{
"Authorization": "Bearer {contains user token }"
}
Just use OrganizationWhereInput instead of OrganizationWhereUniqueInput. It will return a list of organisations instead of a single result (might return an empty array), yet it should allow you to search for an organisation using an employee id.

GraphQL: Updating an array

I'm having some issues updating an array in the resolver. I'm building with typescript.
Description
I have in the datamodel.graphql for Prisma:
type Service #model {
id: ID! #unique
title: String
content: String
createdAt: DateTime!
updatedAt: DateTime!
comments: [Comment!]! // Line to be seen here
author: User!
offer: Offer
isPublished: Boolean! #default(value: "false")
type: [ServiceType!]!
}
type Comment #model {
id: ID! #unique
author: User! #relation(name: "WRITER")
service: Service!
message: String!
}
The Prisma is connected to the GraphQl server and in this one, I defined the mutation :
commentService(id: String!, comment: String!): Service!
So comes the time for implementing the resolver for the given mutation and I'm doing this :
async commentService(parent, {id, comment}, ctx: Context, info) {
const userId = getUserId(ctx);
const service = await ctx.db.query.service({
where: {id}
});
if (!service) {
throw new Error(`Service not found or you're not the author`)
}
const userComment = await ctx.db.mutation.createComment({
data: {
message: comment,
service: {
connect: {id}
},
author: {
connect: {id:userId}
},
}
});
return ctx.db.mutation.updateService({
where: {id},
data: {
comments: {
connect: {id: userComment.id}
}
}
})
}
The problem :
The only thing I'm receiving when querying the playground is null instead of the comment I've given.
Thanks for reading till so far.
Can you please share code where you expose mutation resolvers? You might get null in response in case you forgot to include commentService resolver in mutation resolver object.
Apart from this, I see one more issue in the code. Since you have relation between Service and Comment, you can use single mutation to create comment and add it in service. You don't need to write two separate mutations in order to achieve that. Your resolver can be changed to be as simple as below:
async commentService(parent, {id, comment}, ctx: Context, info) {
const userId = getUserId(ctx);
return ctx.db.mutation.updateService({
where: {id},
data: {
comments: {
create: {
message: comment,
author: {
connect: {id:userId}
}
}
}
}
})
}
Note that I also removed query to check if service exists before performing update. Reason being, updateService binding call will throw error in case it does not exist, we don't need to explicitly check for that.
If I understood the question correctly, you are calling this commentService mutation and you get null as a result? Following your logic, you should get whatever ctx.db.mutation.updateService resolves with, right? If you expect that to indeed be a Service object, then the only reason why you might not be getting it back is a missing await. You probably needed to write return await ctx.db.mutation.updateService({ ....

Categories