Populate an object and pass it in a request parameter in GraphQL - graphql

How can we populate an object and pass it in a request parameter in GraphQL. For example, i want to get rid of sequence of parameters here in the GraphQL request and want to create an object and pass it as a single parameter.
{
allBooks(first:20, orderBy:"myRating"){
isn
title
authors
publisher
}
}

You can use GraphQL Input Types.
In your schema definition you need to define the input into something like:
input BooksInput {
first: Int
orderBy: String
}
And require it as the args type in your query, then use it as:
{
allBooks($input: {first:20, orderBy:"myRating"}){
isn
title
authors
publisher
}
}

Related

How can I parse JSON data in Lighthouse?

I have notification table , where data field stored as json :
{"data":{"description":"Event Status has been changed to pending","event_id":19}}
I get this error
"errors": [
{
"debugMessage": "Expected a value of type \"String\" but received: {\"data\":{\"description\":\"Event Status has been changed to pending\",\"event_id\":19}}",
I have tried to add the following on notifications model:
public function getDataAttribute($data)
{
return json_decode($data, true);
}
But no solution.
I tried to use [String] in GraphQL schema but nothing.
If you want arbitrary JSON data to be returned as a string there are 2 options:
The first is to use a JSON scalar, you can either built your own or use a composer package. It wil encode the data to a valid JSON string.
The second option is to make sure you are returning just the JSON and not the decoded JSON. I'm assuming your data is already decoded to JSON because you are using model casts, if not you could just remove the json_decode call. You could add a getter to re-encode it back to JSON or add a getter to get the value from the attributes property on your model.
public function getRawDataAttribute(): string
{
return $this->attributes['data'];
}
// or
public function getRawDataAttribute(): string
{
return json_encode($this->data);
}
You can use this in your schema like this:
type MyType {
data: String! #rename(attribute: "raw_data")
}
But the first option is definitely the easiest and the best in my opinion because it correctly indicates in the schema the field contains JSON and handles the encoding (and decoding when used in inputs) for you.
Add this package for implementing JSON type to your app:
https://github.com/mll-lab/graphql-php-scalars
then add this line to your schema.graphql file:
scalar JSON #scalar(class: "MLL\\GraphQLScalars\\JSON")
After than you can easily use JSON type like this:
type MyType {
data: JSON! #rename(attribute: "raw_data")
}

Passing exact parameters to Web API in ASP.NET Core

I have written a Web API in ASP.NET Core, for which I need to pass 2 parameters; of them one is a string with grade, the other is of type list of studentInfo as shown here:
[HttpPost]
[Route("UpdateActiveStudents")]
public Response UpdateActiveStudents(string grade, [FromBody] List<StudentsInfo> lststudents)
{
try
{
// My Logic
}
catch(Exception ex)
{
resp.flag = false;
resp.message = ex.Message;
}
return resp;
}
To test this API, I used ARC (Advanced Rest Client). I passed the data as like this in a POST request:
{
"grade": "B",
"lststudents": [
{ "StudentName": "abcdef", "RollNo": "user1"},
{ "StudentName": "abcdef", "RollNo": "user1"}
]
}
It throws a HTTP 400 status error with the following message :
Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1[SchoolHub.Model.StudentList]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly. To fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object. Path 'lststudents', line 2, position 13.
I'm unaware of this exception.
You have this issue because you are not sending the data in the format at which ASP.Net Web API expects. ASP.net Web API needs some special format when dealing with a value like string and value type (int, bool, etc) parameter are marked with FromBody attribute.
Just remove FromBody it will work. For better understanding go with this link.
Why do we have to specify FromBody and FromUri?
In Web API, general parameter binding rules for POST method are as follows -
Query string -> Primitive type
Request body -> Complex type
Now if you want to use POST method with Mixed parameters i.e in your case you are passing primitive (string) and complex (List), Web API will get the grade parameter from query string and student parameter from the request body.
Possible solutions to try -
In the ARC request it seems you are passing grade in request body instead of as a query string parameter. Try passing grade as a query string parameter.
Also add a class viz. StudentInfoRequest to wrap List<StudentsInfo> lststudents and then use StudentInfoRequest object to pass as parameter to UpdateActiveStudents method.
You dont need to mention [FromBody] in UpdateActiveStudents method as by default complex parameters are read from the request body by Web API.
Hope this helps!
You must add [ApiController] in the controller level. Code is as follows
[ApiController]
[Route("[controller]")]
public class studentController : ControllerBase
{
[HttpPost]
[Route("UpdateActiveStudents")]
public Response UpdateActiveStudents(string grade, List<StudentsInfo>
lststudents)
{
//code
}
}

In GraphQL, can you change the structure of the output in an alias?

Let's say I've got a GraphQL query that looks like this:
query {
Todo {
label
is_completed
id
}
}
But the client that consumes the data from this query needs a data structure that's a bit different- e.g. a TypeScript interface like:
interface Todo {
title: string // "title" is just a different name for "label"
data: {
is_completed: boolean
id: number
}
}
It's easy enough to just use an alias to return label as title. But is there any way to make it return both is_completed and id under an alias called data?
There is no way to do that. Either change the schema to reflect the client's needs or transform the response after it is fetched on the client side.

Apollo Graphql modify input data

In Apollo Server, one could use a schema directive to implement a resolver middleware like such:
adminGetUsers(getUsersPL: GetUsersPL!): [User] #hasRole(role: "ADMIN")
#hasRole(role: "ADMIN") serves as a middleware to prevent any non-admin user from using this mutation.
So how would one sanitize/transform input data? For example,
getUser(userId: String! #transform): [User]
#transform will take in userId as a hashed id (ie: xyfd), and transform it to a numbered ID (ie: 12). This syntax is not allowed of course. Basically I want a way to modify input data before it goes into resolver.
That actually is valid syntax. You can define a directive that's applied to argument definitions like this:
directive #test on ARGUMENT_DEFINITION
type Query {
foo(input: String #test): String
}
Schema directives are not middleware. They are just ways of altering individual definitions inside your schema. Most commonly they are used to alter field definitions, but you can alter other definitions like object types, input object types, enums, unions, etc. When using a directive with a field, you can wrap the existing resolve function inside another one (or replace it altogether) -- in doing so, we can create "middleware" for resolvers. However, that's not the purpose of schema directives.
That aside, you can't use an argument directive to alter the value the argument is passed. At best, you can change the type of the argument to something else (like a custom scalar). However, you can just use a field directive to do what you're trying to accomplish:
class ExampleDirective extends SchemaDirectiveVisitor {
public visitFieldDefinition(field) {
const { resolve = defaultFieldResolver } = field
field.resolve = async function (
source,
args,
context,
info,
) {
args.someArg = doSomething(args.someArg)
return resolve.call(this, source, args, context, info);
}
}
}

Multiple field resolver resolves same rest API with different query parameters

We are planning to use graphql for orchestrations (For e.g. UI client invokes graphql service which goes to multiple rest endpoint and return the result). Problem here is from one rest endpoint we have to pass different types of query parameters based on the field requested by our client.
We use spring-boot-graphql and graphql-java-tools libraries to initialize graphql
type Query{
user(id: ID): User
}
type User{
phone: [Phone]
address: [Address]
}
type Phone{...}
type Address{...}
My code resolves user field and invoke rest endpoint to fetch phone and address information in a single call like
https:restservice.com\v1\user\123?fields=phone,address
How to resolve two fields expecting data from same rest service. I want something like when client request for phone then i needs to send fields in request parameters as phone alone without address. Can we do that? or is there any other way to define schema to solves this problem?
query {
user(userId : "xyz") {
name
age
weight
friends {
name
}
}
}
Knowing the field selection set can help make DataFetchers more efficient. For example in the above query imagine that the user field is backed by an SQL database system. The data fetcher could look ahead into the field selection set and use different queries because it knows the caller wants friend information as well as user information.
DataFetcher smartUserDF = new DataFetcher() {
#Override
public Object get(DataFetchingEnvironment env) {
String userId = env.getArgument("userId");
DataFetchingFieldSelectionSet selectionSet = env.getSelectionSet();
if (selectionSet.contains("user/*")) {
return getUserAndTheirFriends(userId);
} else {
return getUser(userId);
}
}
};
https://www.graphql-java.com/documentation/v12/fieldselection/

Resources