graphql - querying multiple tables - graphql

I am new to graphql and I need to query multiple tables at once to display some data. I have a dashboard that shows information on a home where it comes from 5 tables: address, person, hostinfo, room, and image. I initially have the person_id to query address table which contains the person_id etc... Here's what a brief scratch up of a uml looks like:
entity Address {
AddressId BigInteger,
FK_PersonId BigInteger,
StreetNumber Integer,
...
}
entity HostInfo {
HostInfoId BigInteger,
FK_AddressId BigInt,
HomestayName String,
...
}
entity Room {
RoomId BigInteger,
FK_HostInfoId BigInteger,
RoomName String,
...
}
entity Image {
FK_AddressId BigInt,
FK_RoomID BigInt,
Name String,
....
}
entity Person {
PersonId,
FirstName String,
Age Integer required, //TODO remember to check age
}
My question is, how do I use graphql to grab all the data from these tables using just PersonId?
EDIT--------
I refactored the typedefiniton as follows:
export type Address = {
StreetNumber: number,
//..
Person:Person
}
export type HostInfo = {
HomestayName: string,
//..
Person:Person,
Room:[Room!],
Address:Address
}
export type Room = {
RoomName: string,
//..
RoomImage:[RoomImage!],
HostInfo:HostInfo
}
export type RoomImage = {
Name: string,
//..
Room:Room,
}
export type HostInfoImage = {
Name: string,
..
HostInfo:HostInfo
}
export type PersonImage = {
Name: string,
//..
Person:Person
}
export type Person = {
FirstName: string,
..
PersonImage:[PersonImage]
Address:Address
}
and perform the query as so:
query HostInfo($id: ID!) {
node(id: $id) {
... on Person {
firstName
address {
streetNumber
hostInfo {
HostName,
//...
Room {
RoomName,
[RoomImage],
//....
}
}
}
}
}
}
I would explicitly show the relationship both ways in my entities... I was expecting more of a sql(ly) way to do it.

Typically GraphQL would represent all of the links between objects explicitly in the schema: if in your database model an Address references a Person, then your Person GraphQL type would have a list of addresses. In the GraphQL schema language, you might have:
type Person implements Node {
id: ID!,
firstName: String,
age: Int!,
address: [Address!]!
}
If you knew the ID of a person, a typical query to retrieve much of the available data might hypothetically look like
query Person($id: ID!) {
node(id: $id) {
... on Person {
firstName
age
address {
streetNumber
hostInfo { ... }
}
}
}
}

Related

Search By Column in Laravel/Lighthouse GraphQL API

currently i'm creating a GraphQL implementation of an existing API.
i want to create Search Function not with primary column, which is not id, but id_position.
here is my scheme :
type Query #guard {
subordinate(
positionId: ID
): [Employee!]! #all
}
type Position #key(fields: "id") {
id: ID
name: String
}
"Account of a person who utilizes this application."
type Employee #key(fields: "id") {
id: ID
name: String
id_position: Int
}
but, when I run this :
query EmployeeSubordinate($id: ID) {
subordinate(positionId: $id) {
name
}
}
{
"id" : 93
}
I ve got result all rows of employee, not employee with id_position = 93
how to solve this?
I think the problem is here
type Query #guard {
subordinate(
positionId: ID
): [Employee!]! #all
}
The #all is getting all records from DB #all docs
The correct way is this
type Query #guard {
subordinate(
positionId: ID
): Employee
}
I removed ! from Employee because your ID is not required, so in some cases it masy return null, i you handle like thatin backend.
And alsoe I removed [] because you no longer getting many Employee you just getting one.

Querying Many-To-Many Relationships in AWS Amplify

I have two models in my graphql schema and the one I am trying to query on, Sessions, has two #belongsTo directives (read on a forum this matters). I can successfully save these models and view them on the AWS AppSync Queries Tab where I can query getSessions successfully BUT when I try to the exact same query locally following these docs:
(https://docs.amplify.aws/lib/graphqlapi/advanced-workflows/q/platform/flutter/#combining-multiple-operations)
I get an error locally:
type "Null" is not a subtype of type 'string'
What am I doing wrong and how do I fix this so I can successfully retrieve my nested query:
Here are my models as a reference:
Sessions:
type Session
#model
#auth(
rules: [
{ allow: public }
{ allow: owner }
{ allow: groups, groups: ["Admin"] }
]
) {
id: ID!
name: String
numPeoplePresent: Int
notes: String
eIdReader: String
weighTiming: String
cows: [Cow] #manyToMany(relationName: "CowSession")
proceduresID: ID
procedures: Procedures #hasOne(fields: ["proceduresID"])
}
Cow:
type Cow
#model
#auth(
rules: [
{ allow: public }
{ allow: owner }
{ allow: groups, groups: ["Admin"] }
]
) {
id: ID!
name: String!
RfId: String
weight: [Float!]!
temperament: [Int]
breed: String
type: String
dateOfBirth: AWSDate
sessions: [Session] #manyToMany(relationName: "CowSession")
procedures: [Procedures] #manyToMany(relationName: "CowProcedures")
}
This is the query that is causing the error:
const getSession = 'getSession';
String graphQLDocument = '''query getSession(\$id: ID!) {
$getSession(id: \$id) {
numPeoplePresent
notes
name
eIdReader
id
owner
proceduresID
updatedAt
weighTiming
cows {
items {
cow {
RfId
}
}
}
}
}''';
final getSessionRequest = GraphQLRequest<Session>(
document: graphQLDocument,
modelType: Session.classType,
variables: <String, String>{'id': sessID}, //parameter of the current session can hardcode to whatever you need here
decodePath: getSession,
);
final response =
await Amplify.API.query(request: getSessionRequest).response;
print('Response: ${response.data}');
The wonderful people at amplify answered this quickly so I will relay the information here:
the problem was the intermediary ids were not included in my local query so it was unable to retrieve the nested Cows. Updated query looks like this:
getSession = 'getSession';
String graphQLDocument = '''query getSession(\$id: ID!) {
$getSession(id: \$id) {
numPeoplePresent
notes
name
eIdReader
id
owner
proceduresID
updatedAt
weighTiming
cows {
items {
id <-- needed this one
cow {
id <-- and this id too
RfId
breed
dateOfBirth
name
type
weight
}
}
}
}
}''';

graphql: single mutation or one mutation per type

I have a GraphQL schema like this:
type User {
id: ID
name: String
email: String
addresses: [UserAddress]
}
type UserAddress {
id: ID
city: String
country: String
}
I always have doubts about how to make the best design for mutations. (I'm using apollo + prisma)
These are my options:
1) One single mutation
I need to create this mutation and input type:
input userAddressInput {
id: ID
city: String
country: String
}
mutation updateUser (
id: ID
name: String
email: String
addresses: UserAddressInput
): User
Then I execute mutations like this:
mutation updateUserData($id: ID, $name: String, $email: String) {
updateUser(id: $id, name: $name, email: $email) {
id
name
email
}
}
mutation updateUserAddress($id: ID, $userAddress: UserAddressInput) {
updateUser(id: $id, userAddress: $userAddress) {
id
addresses {
id
city
country
}
}
}
And resolvers like this:
Mutation: {
updateUser: (_, args) => {
if (args.name || args.email) {
// update model User by args.userData.id
}
if (args.userAddress) {
// update model UserAddress by args.userAddress.id
}
}
}
2) One mutation per type
I don't need to create any input type but I need two mutations:
mutation updateUser (
id: ID
name: String
email: String
): User
mutation updateUserAddress (
id: ID
city: String
country: String
): UserAddress
Then mutations like this:
mutation updateUser($id: ID, $name: String, $email: String) {
updateUser(id: $id, name: $name, email: $email) {
id
name
email
}
}
mutation updateUserAddress($id: ID, $city: String, $country: String) {
updateUserAddress(id: $id, city: $city, country: $country) {
id
city
country
}
}
And resolvers like this:
Mutation: {
updateUserAddress: (_, args) => {
// update model UserAddress by args.id
}
updateUser: (_, args) => {
// update model User by args.id
}
}
What is the best way to deal with such cases?
It depends what your use case is.
Does your GUI allow for update of a user's addresses without also updating the user info? If so you will likely need a separate mutation for updating only the addresses.
If you are allowing user and addresses to be edited and saved as one operation then individual mutations would require you to send multiple HTTP requests (one per mutation).
Do you need to update the user and addresses as an atomic transaction (i.e. all or nothing)? If so then you should use a single mutation.

GraphQL mutation - confusion designing gql tag for Apollo Client

I need help figuring out the GraphQL tag for use with Apollo Client. The Docs don't go far beyond the most basic use case for mutations.
My goal is to have the only required input be an email. If the other variables are present, I would like those to be accepted and create a proposal with all that information.
I have the mutation (in both only email and full variables scenarios) working successfully on the GraphQl Playground (if it helps, you can find it here and test it out, look at the schema, etc.,): https://prisma2-graphql-yoga-shield.now.sh/playground)
mutation {
createOneProposal(
data: {
email: "fake#gmail.com"
name: "Sean"
types: {
create: {
model: PURCHASE
name: "e-commerce"
cost: 600
services: {
create: [
{ service: "Responsive web design" }
{ service: "Another service!" }
{ service: "And yet another service!" }
]
}
}
}
}
) {
created_at
proposal_id
types {
cost
model
name
type_id
services {
service
service_id
}
}
}
}
Producing as a result:
{
"data": {
"createOneProposal": {
"created_at": "2020-02-27T21:28:53.256Z",
"proposal_id": 35,
"types": [
{
"cost": 600,
"model": "PURCHASE",
"name": "e-commerce",
"type_id": 6,
"services": [
{
"service": "Responsive web design",
"service_id": 10
},
{
"service": "Another service!",
"service_id": 11
},
{
"service": "And yet another service!",
"service_id": 12
}
]
}
]
}
}
}
My initial design for the gql tag:
export const NEW_PROPOSAL = gql`
mutation createOneProposal(
$email: String!
$name: String
$cost: Int
$model: Model
$service: Service
) {
createOneProposal(
email: $email
name: $name
cost: $cost
model: $model
service: $service
) {
created_at
proposal_id
types {
services {
service_id
}
}
}
}
`;
But, I get a lot of errors with this.
{"errors":[
{"Variable "$service" cannot be non-input type `"Service`".","locations":[{"line":1,"column":97}]},
{"Unknown argument "email" on field "createOneProposal`" of type "Mutation`".","locations":[{"line":2,"column":21}]},
{"Unknown argument "name" on field "createOneProposal`" of type "Mutation`".","locations":[{"line":2,"column":36}]},
{"Unknown argument"cost" on field "createOneProposal\" of type "Mutation`".","locations":[{"line":2,"column":49}]},
{"Unknown argument "model" on field "createOneProposal`" of type "Mutation`".","locations":[{"line":2,"column":62}]},
{"Unknown argument "service" on field "createOneProposal`" of type "Mutation`".","locations":[{"line":2,"column":77}]},
{"Field "createOneProposal" argument "data" of type "ProposalCreateInput!`" is required, but it was not provided.","locations":[{"line":2,"column":3}]}]}
So, how can I go about this... I figured out the query version (much easier...), but I just can't figure this out!
My schema, if it helps:
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "mysql"
url = env("MYSQL_URL_PRISMA2")
}
model Post {
content String #default("")
created_at DateTime #default(now())
post_id Int #default(autoincrement()) #id
published Boolean #default(false)
published_at DateTime?
title String #default("")
author User
}
model Profile {
bio String?
profile_id Int #default(autoincrement()) #id
user_id User
}
model Proposal {
email String #unique
name String?
proposal_id Int #default(autoincrement()) #id
created_at DateTime #default(now())
types Type[]
}
model Type {
cost Int?
name String?
model Model? #default(SUBSCRIPTION)
services Service[]
type_id Int #default(autoincrement()) #id
proposal_id Proposal
}
model Service {
service_id Int #default(autoincrement()) #id
service String?
type_id Type
}
model User {
email String #default("") #unique
name String #default("")
password String #default("")
role Role #default(USER)
user_id Int #default(autoincrement()) #id
posts Post[]
profiles Profile[]
}
enum Role {
USER ADMIN
}
enum Model {
SUBSCRIPTION PURCHASE CUSTOM
}
GraphQL types are categorized as either input types or output types. Input types are used for inputs like variable definitions or argument definitions. Output types are used for typing fields, which are what compose the actual response. Certain types, like scalars and enums, can be used as either an input or an output. However, with objects, there are output object types (sometimes referred to just object types or objects) and input object types.
Service is an output type, so it can't be used where an input type is expected (in this case, a variable definition). Examine the schema generated by Prisma to determine the appropriate type to use.
Thanks to some very needed direction from #xadm, I figured out the structure of the tag! For anyone who comes across this in the future:
mutation createOneProposal($input: ProposalCreateInput!){
createOneProposal(data:$input){
created_at
name
email
proposal_id
type{
cost
description
model
name
type_id
services{
service
cost
service_id
}
}
}
}

join 3 tables using graphql query

I want to join 3 tables just like we do in mysql based on primary and foreign keys.
Can I do such using graphql(http://graphql.org/)
My table structure along with graphql query is below. Thanks
query($companyId:String){
Data{
reach{
department {
departmentId
departmentName
description
}
companyDepartment(companyId:$companyId) {
primaryId
departmentId
companyId
createdDate
modifiedDate
modifiedBy
}
company(companyId:$companyId) {
companyId
companyName
}
}
}
}
You must break your mind and think in Graph way model :)
Type Company(node) <- CompanyDepartmentConection (name of connection edge) -> Type Department(node)
based on this very useful article, anyway i do for you eg. Schema
Concept
interface Node {
id: ID!
name: String
}
type Company implements Node {
id: ID!
name: String
departmentsConnection: CompanyDepartmentConnection
}
type CompanyDepartmentConnection {
pageInfo: PageInfo!
edges: [CompanyDepartmentEdge]
}
type CompanyDepartmentEdge {
cursor: String!
node: Company
linkedAt: DateTime
}

Resources