How to use previously acquired data in the GraphQL query? - graphql

Let's say we have two entities.
users that has uuid, name and age
users_books that has user_uuid, book_id and recommended_age.
user_uuid was added as foreign key pointing to uuid in Users
Using user name I want to get all the books that that user reads and have recommended age equal to users age.
Following query will get me all the books that user reads
query getUserBooks($uuid: uuid!) {
users_by_pk(uuid: $uuid) {
uuid
name
age
users_books() {
book_id
recommended_age
}
}
}
And this is the query I am trying to create:
query getUserBooksWithRestrictedAge($uuid: uuid!) {
users_by_pk(uuid: $uuid) {
uuid
name
age
users_books(where:{recommended_age:{_eq: *WHAT_SHOULD_GO_HERE?*}}) {
book_id
recommended_age
}
}
}
Is this even possible?

Hasura supports columns comparison operators only when setting permissions. To accomplish what you need you have to create a view.
CREATE OR REPLACE VIEW users_books_by_age AS
SELECT * from users_books
Set the relationships between the view, user and books table. In the Hasura view permission builder filter the age comparing the columns. Something like
{
user: {
age: {
_ceq: recommended_age
}
}
}
This way you can run your query like:
query getUserBooks($uuid: uuid!) {
users_by_pk(uuid: $uuid) {
uuid
name
age
users_books_by_age {
book_id
recommended_age
}
}
}
Another solution would be to create a computed field. Something like:
CREATE FUNCTION author_full_name(user_row user)
RETURNS SETOF users_books AS $$
SELECT * FROM users_books where recommended_age = user_row.age
$$ LANGUAGE sql STABLE;
I hope that helps.

Related

GraphQL - Select different fields of a returned list per item

Lets say i have the following query:
query getBooksQuery($userId: String) {
getBooks(userId: $userId) {
id
description
image
user {
email
firstName
lastName
image
}
}
}
Imagine i have to show a list with books of a user and display the author details above the list. Since I'm querying with a userId, i know that all books belong to the same user.
So, i would not like to get for each book of the list the same user details, but get them only once in the response.
Is there any way to declare that i want these info only for the first book i.e.?
Or is it the only way to have two objects in the response, like:
query getBooksQuery($userId: String) {
getBooks(userId: $userId) {
books {
id
description
image
}
user {
email
firstName
lastName
image
}
}
}
Unfortunately with the second solution, even if its clearer at this point, will require server-side work to support this custom query. So i was thinking if it is still feasible using the current api.
(Also ofc i can do two requests, one for the user and one for the books, but meh...)
I'm using Apollo without relay, but I'm experimenting at this point, so if not feasible in Apollo but feasible otherwise I'm still interested
You can use the same param for 2 'subqueries' in one request
query getBooksQuery($userId: String) {
getBooks(userId: $userId) {
books {
id
description
image
}
}
getUsers(userId: $userId) {
user {
email
firstName
lastName
image
}
}
}

How make related insert and update in one transaction?

Imagine two tables:
People {
id: uuid
home_id: uuid
}
Homes {
id: uuid
}
Table People is already populated. What I would like to do, is to insert a new Home and update home_id field in the People table at the same time. Is this possible?
I ended up solving this by creating a HomeOwnership table { person_id: uuid, home_id: uuid } and removing home_id from the People table. Then I established foreign key relationships between the tables. Then the mutation would look like following:
mutation MyMutation($person_id: uuid = "some_id") {
insert_home_one(object: {home_membership: {data: {person_id: $person_id}}}) {
id
home_membership{
person{
name
}
}
}
}

Graphql schema design problem that I can't solve

I am trying to design a schema for an application and I have a problem that I can't solve. Here is a detail of the application
User A LIKES User B
User B MATCHS User B
Now User A and User B can start chatting with each others.
I also keep track of who visited each profile
User A VISITED BY User B
User A Visited BY User C
In the application, I have Me type with details of the user running the app. I have a me query that looks like this:
me {
id
name
email
...
...
likes { ## users who liked me
nextToken
edges {
node { ## user
id
name
...
}
}
}
matchs { ## users who matched with me
nextToken
edges {
node { ## user
id
name
...
...
}
}
}
Vists { ## users who visited me
nextToken
edges {
node { ##
id
name
...
...
}
}
}
}
In addition to that, I have listUsers query that list users nearby to Me and looks something like this:
listUsers {
nextToken
total
edges {
distance
node { ## user
id
name
...
...
}
}
}
MY QUESTION
Since there is a relationship between users (LIKED_BY, MATCHED_WITH) where do I use this relationship in my schema such that it is cashable. Keep in mind the relationship can change at the client from NO_RELATIONSHIP to LIKED_BY to MATCHED_WITH so if the relationship is duplicated in multiple places, this will be a problem.
I would really appreciate any help as I am out of ideas.
Thanks in advance.
In the GraphQL schema you'd generally make links like this explicit references to the other object.
type User {
id: ID!
...
matches: [User!] # actual users, not their IDs
likes: [User!]
visitedBy: [User!]
}
In your top-level Query type you can return the current user
type Query {
me: User
}
Now if I want the names and email addresses of people who have visited me, I can find out
query WhoVisitedMe {
me {
visitedBy { name, email }
}
}
You can traverse this graph, for example, to get recommendations: for people who visit you, who do they like?
query WhoDoVisitorsLike {
me {
visitedBy {
likes {
id
name
}
}
}
}
Your application's resolver code would need to fill in these object references.
You clarified in a comment:
While listing users in the search query, I need to be able to know the relationship between Me and the users
That is, you're trying to ask about some user
type User {
relationToMe: [RelationshipType!]
}
The relationToMe field can have any number (including zero) of relationship types; you can define those as a GraphQL enum
enum RelationshipType {
LIKES, LIKED_BY,
VISITED, VISITED_BY,
MATCHES # bidirectional
}
Then in your query you can ask
query FindSomeUsers($input: SearchUsersInput!) {
searchUsers(input: $input) {
users {
id, name, email
relationToMe
}
}
}
and get back a response like
{
"data": {
"searchUsers": {
"users": [
"id": "12345",
"name": "John Smith",
"email": "jsmith#example.com",
"relationToMe": ["VISITED", "MATCHES"]
]
}
}
You'd need to implement a custom resolver in your implementation to populate this field. If you had something like a many-to-many SQL join table, you could query that to fill in this field, if requested.

GraphQL - How retrieve id of previous mutation, during query of multiple mutations

i would like run multiple mutations in the same query.
In the example below, i create an order and after i create a product record, concerning previously created.
I must have 2 mutations.
First, i insert an order. In output, i retrieve among others, idorder.
Then, i insert an product. This product
mutation {
createOrder(input: {
order: {
ordername: "My order"
}
}) {
order {
idorder
ordername
}
},
createProduct(input: {
product: {
quantity: 3
idrefproduct: 25 # link to refProduct
idorder: XXXX # how can i retrieve idorder from output of createOrder above ? 🤔
}
}) {
product {
idproduct
}
}
}
Real example with SQL structure :
user(iduser, othersFields);
scenarios(idscenario, iduser, name, otherFields);
cultA(idcultA, idscenario, ...); // this table need of idscenario field
cultB(idcultB, idscenario, ...); // this table need of idscenario field
cultC(idcultC, idscenario, ...); // this table need of idscenario field
how can i retrieve idorder from output of createOrder above ? 🤔
It is possible ?
If i forgot some informations, don't hesitate.
Thanks in advance.
EDIT :
With PostGraphile, plugin "postgraphile-plugin-nested-mutations" or "custom mutations" (with PL PGSQL function)
Without PostGraphile, a resolver as the example of #xadm permits this particular nested mutation.
IMHO you can search for "nested mutations" - not described here, you'll easily find examples/tutorials.
Proposed DB structure (n-to-n relation):
order{orderID,lines[{orderLineID}] } >
order_line{orderLineID, productID, anount, price} >
product {productID}
... created in nested mutations (in reverse order product>order_line>order)
Product don't need orderID, but when you ask for it [in product resolver]
query product(id) {
id
orderedRecently {
orderID
date
price
}
}
... you can simply get it (or rather many - array) from orderLines and orders tables [using simple SQL query - where price will be read from orderLines]
orderedRecently resolver can get product id from parent object (usually 1st param)
Of course you can (and should) return data as order and orderLine types (to be cached separately, normalized):
query product($id: ID!) {
product(id: $id) {
id
orderedRecently {
id
date
orderLine {
id
amount
price
}
}
}
}
where type orderedRecently: [Order!] - array can be empty, not eordered yet
update
I slightly misunderstood your requirements (naming convention) ... you already have proper db structure. Mutation can be 'feeded' with complex data/input:
mutation {
createOrder(input: {
order: {
ordername: "My order"
products: [
{
quantity: 3
idrefproduct: 25
},
{
quantity: 5
idrefproduct: 28
}
]
}
}) {
order {
id
ordername
products {
id
idrefproduct
quantity
}
}
}
}
Your product is my orderLine, idrefproduct is product.
createOrder creates/inserts order and then use its id for creation of product records (order.id, idrefproduct and quantity). Resolver can return only order id or structured data (as above).

Best practice for schema naming of entity/collection

I am building a Graphql Schema and I was wandering what is the best practice of returning single vs collection items of a type. Let's say we want to retrieve users,
One option (if possible somehow) would be to have a query like this where the ID is optional, if ID is passed we return a single item, if not a collection of all users
query {
user (id: 1234) {
name
}
}
// return a single [User]
query {
user (id: null) {
name
}
}
// return a collection [User,User,User,...]
Another option would be to have user and users
query {
user (id: 1234) {
name
}
}
// return a single User
query {
users {
name
}
}
// return a collection [User,User,User,...]
I was wondering what is the best practice, or if you can pin-point me some resources related to that to read.
I am using the singular and plurals nouns to name the query field that return a single object and a list of object respectively. I think this naming style is very natural to most of the developers.
So to return a single user, it is :
type Query {
user(id:Int!) : User
}
It always return a single user. Just make the id input parameter as mandatory such that it cannot accept NULL.
And to return a list of user , normally it is:
type Query {
users : [User]
}
But in case it can have many users , most probably you need to consider something like pagination that allows developers to get the user page by page. For the offset -based pagination , I am doing something like below :
type Query {
users(offset:Int limit:Int) : UserPage
}
type UserPage {
data : [User]
pageInfo : PageInfo
}
type PageInfo {
# When paginating forwards, are there more items?
hasNextPage : Boolean!
# When paginating backwards, are there more items?
hasPreviousPage: Boolean!
# Total number of records in all page
total : Long
}
Depending on the requirements , you can consider to add an orderBy or a filter input parameter to the users query field to provide more options to the developers to get the result set that they are interested.
If you want to return the user list in the cursor-based pagination style, you can take a look on Relay Specification.

Resources