Graphql mutation return value [duplicate] - javascript

Hi I am trying to learn GraphQL language. I have below snippet of code.
// Welcome to Launchpad!
// Log in to edit and save pads, run queries in GraphiQL on the right.
// Click "Download" above to get a zip with a standalone Node.js server.
// See docs and examples at https://github.com/apollographql/awesome-launchpad
// graphql-tools combines a schema string with resolvers.
import { makeExecutableSchema } from 'graphql-tools';
// Construct a schema, using GraphQL schema language
const typeDefs = `
type User {
name: String!
age: Int!
}
type Query {
me: User
}
`;
const user = { name: 'Williams', age: 26};
// Provide resolver functions for your schema fields
const resolvers = {
Query: {
me: (root, args, context) => {
return user;
},
},
};
// Required: Export the GraphQL.js schema object as "schema"
export const schema = makeExecutableSchema({
typeDefs,
resolvers,
});
// Optional: Export a function to get context from the request. It accepts two
// parameters - headers (lowercased http headers) and secrets (secrets defined
// in secrets section). It must return an object (or a promise resolving to it).
export function context(headers, secrets) {
return {
headers,
secrets,
};
};
// Optional: Export a root value to be passed during execution
// export const rootValue = {};
// Optional: Export a root function, that returns root to be passed
// during execution, accepting headers and secrets. It can return a
// promise. rootFunction takes precedence over rootValue.
// export function rootFunction(headers, secrets) {
// return {
// headers,
// secrets,
// };
// };
Request:
{
me
}
Response:
{
"errors": [
{
"message": "Field \"me\" of type \"User\" must have a selection of subfields. Did you mean \"me { ... }\"?",
"locations": [
{
"line": 4,
"column": 3
}
]
}
]
}
Does anyone know what I am doing wrong ? How to fix it ?

From the docs:
A GraphQL object type has a name and fields, but at some point those
fields have to resolve to some concrete data. That's where the scalar
types come in: they represent the leaves of the query.
GraphQL requires that you construct your queries in a way that only returns concrete data. Each field has to ultimately resolve to one or more scalars (or enums). That means you cannot just request a field that resolves to a type without also indicating which fields of that type you want to get back.
That's what the error message you received is telling you -- you requested a User type, but you didn't tell GraphQL at least one field to get back from that type.
To fix it, just change your request to include name like this:
{
me {
name
}
}
... or age. Or both. You cannot, however, request a specific type and expect GraphQL to provide all the fields for it -- you will always have to provide a selection (one or more) of fields for that type.

Related

Resolve two GraphQL schema fields using one endpoint

There's a situation where there are two possible types to fill data property. I have made a union type for that (ComponentItem) to determine which field needs to be returned. The first schema (ComponentItem1) should just be a hardcoded list but the second one (ComponentItem2) is more dynamic where it gets a searchTerm from query and it actually calls an endpoint to fill the list and also has hasNextPage property.
Here are the schemas I made:
type Component {
id
title
data: ComponentItem
}
union ComponentItem = ComponentItem1 | ComponentItem2
type ComponentItem1 {
list: [List!]!
}
type ComponentItem2 {
hasNextPage: Boolean;
list: [List!]!
}
In the resolver I'm resolving the union type in order to generate proper __typename:
const resolver: {
ComponentItem: {
__resolveType(object) {
if(object.searchTerm){
return "ComponentItem2"
}
return "ComponentItem1"
},
},
}
What I'm currently doing is to resolve the list and hasNextPage individually but in this scenario I'm sending the request to the same endpoint twice.
const resolver = {
...resolver,
ComponentItem2: {
list: async (root, __, context) => {
const result = await fetch('search-endpoint')
return result?.items || []
}
hasNextPage: async (root, __, context) => {
const result = await fetch('search-endpoint')
return result?.hasNextPage || false
}
}
}
My question is how it is possible to share that result in another field resolver (Other than using "context"). Or if there's any better way to handle this situation let me know.

Send an enum to graphql API from react app

I have an input (attached image) that I need to send to the graphql api from react application.
I am using below code to send this object and enum with init to graphql api
Reactjs Code
const RequestActionEnum = {
NEW: 'New',
UPDATE: 'Update',
ARCHIVE: 'Archive'
}
LocalCodeMutation({
variables: {
data: {
id: null,
city: values.jurisdiction,
country: values.country,
description: values.description,
edition: values.edition,
name: values.codeName,
note: 'test',
requestType: RequestActionEnum.NEW, // this is where i am sending enum value to api
state: values.state
}
}
});
Below code is where I am calling the mutation
const [LocalCodeMutation] = useMutation(LOCALCODE_MUTATION, {
refetchQueries: () => [
{ query: GET_LOCALCODES },
],
});
export const LOCALCODE_MUTATION = gql`
mutation LocalCodeMutation($data: LocalCodeRequestParamsInput) {
localCodeMutation(data: $data) {
ok
errors
localCodeInsertedId
}
}
`;
I am getting this error when I send to the API:
Error: GraphQL error: Variable $data got invalid value.
How can I send enum value to graphQL api from react component.
Could any one please suggest any ideas on this?
The enum values for RequestActionEnum are
NEW
UPDATE
ARCHIVE
If you were using this enum as a literal (not a variable), you would write it like this:
{
someField(someArgument: NEW)
}
Similarly, if you're using variables, you would use "NEW". Instead, you're using "New", which is not a valid enum value for this particular enum.
FWIW, if you actually read through to the end of the error, it would tell you as much as well.

Mock Relay-style pagination with Apollo Server

I have a mock server using Apollo Server to return GraphQL responses. One of the queries is to get teams which has an array of metrics (see schema below):
const mocks = {
Query: () => ({
teams: (/*parent, args, context, info*/) => teamsFixture,
}),
};
const graphServer = new ApolloServer({ typeDefs: schema, mocks });
graphServer.applyMiddleware({ app });
And my query used to be (fields redacted):
teams {
bpxId
metrics {
timestamp
}
}
The real (java) server has changed this to allow me to query (Relay style) only the first item in the metrics array as it wasn't performant:
teams {
bpxId
metrics(first: 1) {
edges {
node {
timestamp
}
}
}
}
With the response in this shape:
metrics: {
edges: [
{
node: [Team]
__typename: "TeamMetricsConnectionEdge"
}
]
__typename: "TeamMetricsConnection"
}
I want to add this capability to my Apollo Server mocks but not sure how?
New schema (relevant bits):
type TeamMetrics {
timestamp: Int
# etc
}
type TeamMetricsConnection {
edges: [TeamMetricsConnectionEdge]
pageInfo: PageInfo
}
type PageInfo {
hasPreviousPage: Boolean!
hasNextPage: Boolean!
startCursor: String
endCursor: String
}
type Query {
teams(bpxId: Int): [Team]
}
type Team {
bpxId: Int!
metrics(first: Int! = 5, after: String): TeamMetricsConnection
}
How can I adjust my mock response to handle Relay pagination? Thanks
You can use graphql-relay to create mock resolver results. This could be an easy way to create pagination for static mock arrays. Use connectionFromArray to wrap an array of mocks to automatically create an object structure that fits the connection type. It is possible to access all the field arguments in the mock similarly how you would do it in a real resolver and pass them into the function.
I am not sure if this works when you call the function on a MockList though. Pagination is a bit tricky as well if the length of the results changes all the time (the pageInfo.hasNextPage might be mixed up and you might violate a lot of assumptions that Relay has according to the spec). So it might be fine to start with a simple static array if that satisfies your mocking needs.

Add custom GraphQL resolvers and types into Prisma/Nexus schema

Using: TypeScript, Prisma, MySQL, GraphQLServer, ApolloClient, building schema this way:
const schema = makePrismaSchema({
// Provide all the GraphQL types we've implemented
types: [Query, Mutation, User, Post],...
And then:
const server = new GraphQLServer({
schema,
context: { prisma }
});
How to combine that with custom resolvers and types unrelated to the SQL?
(I would like to call some REST endpoint by the GQL as well)
While nexus was created to be used alongside prisma, it's really just a schema builder. You could easily use it to create a schema without even utilizing Prisma. For example:
export const User = prismaObjectType({
name: 'User',
definition(t) {
t.list.field('comments', {
type: 'Comment',
resolve(root, args, ctx) {
return getComments();
},
});
},
})
export const Comment = prismaObjectType({
name: 'Comment',
definition(t) {
t.string('body');
},
})
Here getComments can return an array of comment objects, or a Promise that resolves to one. For example, if you're calling some other API, you'll normally return a Promise with the results of the call. As shown above, the resolver exposes the parent value, the field's arguments and a context object -- you can use any of this information in determining how to resolve a particular field.

Function with dynamic return in typescript

I am trying to create an API on NodeJS with typescript
I have the following interfaces :
export interface ISqlResonse<T> {
success?: string
error?: string
data: Array<T> //Nothing | array of object when are db operations
}
export interface IApiResponse<T> {
status: 'error' | 'success'
message: string
data: Array<T>
}
Each api call call a function that call an generic class name DB that select/insert/update/delate data from an database
For example the update function look like :
async updateData(input: IUpdateParam) : Promise<ISqlResonse<object>> {
...
...
}
API function call DB and look like :
async update(req): Promise<IApiResponse<IAccessPointsTableStructure>> {
let data = req.body ;
let updateObj = {
data ,
table: 'accessPoints',
excludeColumns: 'loggedUserId',
additionalColumns: { modifiedBy: '1', modifiedAt: crtDate },
validationRules,
where: `id=${data.id}`,
returningData: true
}
let sqlResults = await db.updateData(updateObj) ; // !!!
if(typeof sqlResults.error==="string") {
logger.log('error','Error on updating Access Points!',{sql: db.getQuery(), error: sqlResults.error});
return({status:'error',message: 'Error on updating Access Points!',data: sqlResults.data});
}
logger.log('success', 'Access Points data updated with success!');
return({status: 'error', message: 'Access Points data updated with success!', data: sqlResults.data})
}
My question is : how can I call the function db.updateData() and tell this function that I want to receive in data from ISqlResponse an array with objects like interface IAccessPointsTableStructure.
With other words i want to control the returning type of function. I teste several times with different approaches . (Replace wit in db.updateData(...) <..>...
Thank you in advice.
You haven't included the definition of IUpdateParam, but I will assume that its table property is what decides the type of thing updateData() returns. Everywhere I've commented "guess" is just for example; you should change them to fit your use cases.
You should be able to modify the signature for the updateData() to reflect the relationship between the type of IUpdateParam passed in and the type of Promise<ISqlResponse<{}>> returned. Here's one way to do it, using generics (you could use overloads instead). First, declare a type to represent the mapping between the table names and the data type for each table. For example:
export type TableNameToTypeMapping = {
accessPoints: IAccessPointsTableStructure,
otherThings: IOtherThingsTableStructure, // guess
// ...
}
Now, you can change the definition of IUpdateParam so that it only accepts the right values for table:
export interface IUpdateParam<K extends keyof TableNameToTypeMapping> {
data: any, // guess
table: K,
excludeColumns: string, // guess
additionalColumns: any, // guess
validationRules: any, // guess
where: string // guess
}
So an IUpdateParam<'accessPoints'> object is meant to deal with the accessPoints table, and it is different from an IUpdateParam<'otherThings'> object.
Now the signature for updateData() can be changed to:
async updateData<K extends keyof TableNameToTypeMapping>(
input: IUpdateParam<K>
): Promise<ISqlResonse<TableNameToTypeMapping[K]>> {
// implement this! The implementation is likely not
// type-safe unless you use runtime type guards
}
This means if you call updateData with a parameter of type IUpdateParam<'accessPoints'>, you will get back a Promise<ISqlResponse<TableNameToTypeMapping['accessPoints']>>. But TableNameToTypeMapping['accessPoints'] is just IAccessPointsTableStructure, so you are getting back a Promise<ISqlResponse<IAccessPointsTableStructure>> as desired.
Note that the object literal updateObj will have its table property inferred as type string, which is too wide. To make sure the call to updateData() works as desired, you will either need to assert that the updateObj.table property is of literal type 'accessPoints', like this:
let updateObj = {
data,
table: 'accessPoints' as 'accessPoints', // assertion
excludeColumns: 'loggedUserId',
additionalColumns: { modifiedBy: '1', modifiedAt: crtDate },
validationRules,
where: `id=${data.id}`,
returningData: true
}
or you will need to declare that updateObj is of type IUpdateParam<'accessPoints'>, like this:
// type declaration
let updateObj:IUpdateParam<'accessPoints'> = {
data ,
table: 'accessPoints',
excludeColumns: 'loggedUserId',
additionalColumns: { modifiedBy: '1', modifiedAt: crtDate },
validationRules,
where: `id=${data.id}`,
returningData: true
}
Either way should work.
Hope that helps; good luck!

Categories

Resources