Apollo / GraphQL Same Query with Different Parameters - graphql

How would I go about having an Apollo query with different parameters. Let's say my app has the concept of users and I want my API to be able to find a user either by ID or username.
It doesn't seem I can do this
type Query {
user(id: ID!): User
user(username: String!): User
}
do I have to resort to something like this
type Query {
userById(id: ID!): User
userByUsername(username: String!): User
}

Create a new input type with ID and username as fields and specify it as parameter.
type Query {
getUser(filters:UserFilter): User
}
input UserFilter{
ID : Int,
Username : String
}
Query should look like this ...
Query{
getUser(filters:{ID:123123})
}
OR for username filter
Query{
getUser(filters:{Username:"Tom"})
}

I believe your second option is the best however you could do something like this.
type Query {
user(id: ID, username: String): User
}
If the parameters are not required then your resolver could use some logic to determine if it should get the user based on the id or the username.
if (args.id) {
return getUserById(args.id)
} else {
return getUserByUsername(args.username)
}
However for reasons gone over in this talk https://youtu.be/pJamhW2xPYw?t=750 (maybe see at about 12:30), I believe your second option is a better design choice. Hope that helps!

Related

Is there any way limit the form of the query in graphql

For example, if there are two types User and Item
type User {
items: [Item!]!
}
type Item {
id: ID!
name: String!
price: Int!
}
If one user has PARTNER role.
I want to prevent it from being called only in the form of the query below.
query Query1 {
user {
items {
name
}
}
}
If user call another query, I want to indicate that user doesn't have permission.
query Query2 {
user {
items {
id
name
}
}
}
In short. if (Query1 != Query2) throw new Error;
Your question is a bit hard to follow but a couple things:
A GraphQL server is stateless - you cannot (and really should not) have a query behave differently based on a previous query. (If there's a mutation in between sure but not two queries back to back)
access management is normally implemented in your resolvers. You can have the resolver for the item id check to see if the user making the query has the right to see that or not and return an error if they don't have access.
Note that it can be bad practice to hide the id of objects from queries as these are used as keys for caching on the client.

How to use custom input types in AppSync GraphQL schema?

I'm experimenting with AppSync + DynamoDB. I want to have the following types in my GraphQL Schema:
type User {
user_id: String!
}
type Box {
name: String!
user: User!
}
How can I create, in DynamoDB, a table storing items pointing to another table (In my case, I want the field user of the table BoxTable to be a reference to a user in the table UserTable?
How can I, in AppSync, define the above schema? When I set user: User!, I get the error Expected User! to be a GraphQL input type.?
As per my understanding of your question, these are my answers.
How can I create, in DynamoDB, a table storing items pointing to another table
DynamoDB is not a relational database and does not offer foreign keys or table joins. Therefore, to achieve what you have mentioned in your post, you would still require two calls to DynamoDB to get all the information for the Box i.e. first get the Box item from BoxTable and then get user from UserTable based on user_id. If your use case is such that you get user first, then you can get the Box using filter by user_id.
Now to the second part of your post,
How can I, in AppSync, define the above schema?
With DynamoDB unit resolvers, you can query against a single table (outside of DynamoDB Batch Operations but those are reserved for bulk use cases).
One way of doing this is by defining your schema that should look something like this;
type User {
user_id: String!
}
type Box {
name: String!
user: User!
}
input BoxInput {
name: String!
user: UserInput!
}
input UserInput {
user_id: String!
}
type Mutation {
createBox(input: BoxInput): Box
}
type Query {
getBox(input: BoxInput): Box
}
And this is how you can run query and mutation;
mutation createBox {
createBox(input: {
name: "abc"
user: { user_id: "1234-abcd-5678-efgh"}
}){
name
user { user_id }
}
}
query getBox {
getBox(input: {
name: "abc"
user: { user_id: "1234-abcd-5678-efgh"}
}){
name
user { user_id }
}
}
So, beware of the above query and mutation. These will show user as null unless you attach a separate resolver with you user type within your Box type. For example:
Query that returns Box --> Resolver
type Box {
name
user --> Attach resolver to get user_id from your UserTable
}
Other way is to utilize the pipeline resolvers in which you can create multiple functions, each of which can use the results of the previous function and query a database. These functions run in an order you specify. For example:
Function to get Box from BoxTable.
Function to get user from UserTable by using user_id from ctx.prev.result.
And finally consolidating above two result into one JSON object depending upon Box type in your schema.

Passing variables in GraphQL

I'm trying to run a GraphQL query in the AWS AppSync console:
query MyQuery {
getUserInfoById(id: "1234566789") {
account {
id // need this value for getAvailableCourses
}
}
getAvailableCourses(accountId: "", pageNumber: 0) {
data {
id
name
type
}
}
}
Basically I need the value account.id in getUserInfoById for getAvailableCourses. I'm obviously new to GraphQL. How would I go about this?
To the best of my knowledge, there can be two ways you can do this.
You can handle this in your frontend by getting user's id
from the session info and pass it to the other query.
You can also merge these two queries and make it one. You will also have to change the respective fields. Then attach a resolver with AvailableCourses and use $ctx.source.id in the resolver to get further details. Schema would look something like this
type Account {
id : ID!
availableCourses: AvailableCourses
..
}
type AvailableCourses {
name: String!
type: String!
..
}
type Query {
getUserInfoById(id: ID!): Account
}
Using the returned fields as inputs for a second query into your datasource is precisely what field resolvers are for. I can't say for sure since I don't know your schema or access patterns but it looks like you need to make available courses a sub field of the user.

How to filter a Graphql query with a field value in Laravel 5.8 with Lighthouse

I have users who can create lists. Lists can be starred ("featured" if you prefer). I want to build a Graphql query in Lighthouse/Laravel that would retrieve all the starred/featured lists for a given user.
I tried this query and it works:
clistsByStarred(user_id: Int! #eq, starred: Boolean! #eq): [Clist] #all
and then I can use it like that:
query {
clistsByStarred(user_id: 1, starred: true) {
id
name
starred
}
}
But it feels like a dirty hack to me.
What I would like to achieve is this:
query {
user(id: 1) {
id
name
clistsByStarred {
id
name
starred
}
}
}
Any idea on how to implement it ?
type User {
id: ID!
clists(starred: Boolean #eq): Clist #hasMany
This example gives you a user type which has a list of Clist types.
You then just add a field in your query to get a single user by id.
When you query this, you can then specify the parameter starred to only get the starred.
For this to work the relationship has to exist on your eloquent model.
https://lighthouse-php.com/master/eloquent/relationships.html

How to search string values in GraphQL

How do you query using GraphQL in a manor similar to SQL's like operator?
Example: What users have a first name starting with jason?
select * from users where first_name like "jason%"
The short answer is: you don't.
The longer answer is that you have to write that code yourself. GraphQL isn't a database query language like SQL, it's an application query language. What that means is that GraphQL won't let you write arbitrary queries out of the box. It will only support the types of queries defined in your GraphQL schema.
If you want to be able to write a query that contains like, you have to
a) declare that in the schema, and
b) write a resolve function that fetches the data
For example, you could have this schema:
type Query {
users(firstName: String!): [User]
}
type User {
firstName: String
lastName: String
}
You would have to define the following resolve function for the users field:
{
Query: {
users(root, args){
return sql.raw('SELECT * FROM `users` WHERE `firstName` LIKE ?', args.firstName);
}
}
}
And finally write this query to get a list of firstName and lastName of all the users that match your search criteria:
{
users(firstName: 'jason%'){
firstName
lastName
}
}
Here's a post I wrote a while ago that clarifies some of the concepts around GraphQL: https://medium.com/apollo-stack/how-do-i-graphql-2fcabfc94a01
And here's a post that explains the interplay between GraphQL schemas and resolve functions: https://medium.com/apollo-stack/graphql-explained-5844742f195e
Not sure if this is relevant to you because you want it to start with "jason" (ie would return "jason bourne" but not "bourne jason") but I recently came across a way to query GraphQL in a "%Like%" manner. For your use case it would look something like this:
export const allUsersQuery = `
query allUsers($UserName: String!){
allUsers(
filter: {first_name_contains: $UserName}
) {
id
first_name
...INSERT_OTHER_FIELDS_HERE...
}
}
`;
FWIW: I did this using a GraphCool BAAS. I don't think you were using GraphCool because GraphCool doesn't allow "_" in variable names.
Hope this helps someone down the line :)

Categories

Resources