Quick GraphQL
Posted on September 2, 2022
Tags: codeetc
1 Summary
GraphQL is nothing special, it is REST but with strict requirements of how to write requests.
- Frontend: POST request with a specific format OR Use Apollo Client to generate those POST requests
- Backend: Use a library like apollo server or golang gqlgen
- The backend is really just a gateway that sits infront of your true backend or microservices
- DO NOT TURN give graphql server capabilities to your microservices, simply use the graphql backend as a gateway that sends request to your microservice.
1.1 Example request
Below we make a graphQL request
fetch("https://graphqlzero.almansi.me/api", {
"method": "POST",
"headers": { "content-type": "application/json" },
"body": JSON.stringify({
query: `{
user(id: 1) {
id
name
}
}`
}).then(res => res.json()).then(console.log)
})// { "data": { "user": { ... } } }
import ApolloClient, { gql } from 'apollo-boost';
const client = new ApolloClient({
uri: 'https://graphqlzero.almansi.me/api'
;
}).query({ query: gql`
client {
user(id: 1) {
id
name
}
}
`}).then(console.log);
2 Query and Mutations
- Two types of requests, query and mutations
- mutation example: graphql backend has an inmemory list of users that is empty so to populate the list, a client sends a POST request with body specified with mutation
3 Downsides
- GraphQL server has the N+1 problems
- No officially supported GraphQL Backend for languages other than JS (community libs can be dropped)
- But you can use Apollo Server as a gateway
- Entire schema is public, must manage and restrict what is/isnt callable
4 Example Appsync graphql schema
typical schema is
type
keyword in graphQL corresponds to Records/Structs/Product-typesinput
keyword in graphQL corresponds to input argument interfaces in JSenum
keyword in graphQL corresponds to Union/Sum-types
type TheseAreProductTypes {
bleh: ..
}
input ThereAreInputArgTypes {
varg: ..
}
enum ThereAreUnionTypes {
bool
number
}
- The schema transforms
Query
into a keyword, meaningtype Query{..}
isnt a Product-type but a special type- The same can be said for
type Mutation{..}
andtype Subscription{..}
which were both lifted into a special type due to theschema{..}
- The same can be said for
schema {
query: Query
mutation: Mutation
subscription: Subscription
}
type Query{...
}
type Mutation{...
}
type Subscription{...
}
4.1 Example
- Below is an example of a real graphql schema
schema {
query: Query
mutation: Mutation
subscription: Subscription
}
type ModelPostConnection @aws_api_key @aws_iam {
items: [Post]!
nextToken: String
startedAt: AWSTimestamp
}
type ModelTestimonialConnection @aws_api_key @aws_iam {
items: [Testimonial]!
nextToken: String
startedAt: AWSTimestamp
}
type Mutation {
createPost(condition: ModelPostConditionInput, input: CreatePostInput!): Post @aws_api_key @aws_iam
createTestimonial(condition: ModelTestimonialConditionInput, input: CreateTestimonialInput!): Testimonial @aws_api_key @aws_iam
deletePost(condition: ModelPostConditionInput, input: DeletePostInput!): Post @aws_api_key @aws_iam
deleteTestimonial(condition: ModelTestimonialConditionInput, input: DeleteTestimonialInput!): Testimonial @aws_api_key @aws_iam
updatePost(condition: ModelPostConditionInput, input: UpdatePostInput!): Post @aws_api_key @aws_iam
updateTestimonial(condition: ModelTestimonialConditionInput, input: UpdateTestimonialInput!): Testimonial @aws_api_key @aws_iam
}
type Post @aws_api_key @aws_iam {
_deleted: Boolean
_lastChangedAt: AWSTimestamp!
_version: Int!
body: String
createdAt: AWSDateTime!
description: String
id: ID!
readTime: Int
title: String
updatedAt: AWSDateTime!
}
type Query {
getPost(id: ID!): Post @aws_api_key @aws_iam
getTestimonial(id: ID!): Testimonial @aws_api_key @aws_iam
listPosts(filter: ModelPostFilterInput, limit: Int, nextToken: String): ModelPostConnection @aws_api_key @aws_iam
listTestimonials(filter: ModelTestimonialFilterInput, limit: Int, nextToken: String): ModelTestimonialConnection @aws_api_key @aws_iam
syncPosts(filter: ModelPostFilterInput, lastSync: AWSTimestamp, limit: Int, nextToken: String): ModelPostConnection @aws_api_key @aws_iam
syncTestimonials(filter: ModelTestimonialFilterInput, lastSync: AWSTimestamp, limit: Int, nextToken: String): ModelTestimonialConnection @aws_api_key @aws_iam
}
type Subscription {
onCreatePost(filter: ModelSubscriptionPostFilterInput): Post @aws_api_key @aws_iam @aws_subscribe(mutations : ["createPost"])
onCreateTestimonial(filter: ModelSubscriptionTestimonialFilterInput): Testimonial @aws_api_key @aws_iam @aws_subscribe(mutations : ["createTestimonial"])
onDeletePost(filter: ModelSubscriptionPostFilterInput): Post @aws_api_key @aws_iam @aws_subscribe(mutations : ["deletePost"])
onDeleteTestimonial(filter: ModelSubscriptionTestimonialFilterInput): Testimonial @aws_api_key @aws_iam @aws_subscribe(mutations : ["deleteTestimonial"])
onUpdatePost(filter: ModelSubscriptionPostFilterInput): Post @aws_api_key @aws_iam @aws_subscribe(mutations : ["updatePost"])
onUpdateTestimonial(filter: ModelSubscriptionTestimonialFilterInput): Testimonial @aws_api_key @aws_iam @aws_subscribe(mutations : ["updateTestimonial"])
}
type Testimonial @aws_api_key @aws_iam {
_deleted: Boolean
_lastChangedAt: AWSTimestamp!
_version: Int!
avatar: String
createdAt: AWSDateTime!
id: ID!
name: String
testimony: String
updatedAt: AWSDateTime!
}
enum ModelAttributeTypes {
_null
binary
binarySet
bool
list
map
number
numberSet
string
stringSet
}
enum ModelSortDirection {
ASC
DESC
}
input CreatePostInput {
_version: Int
body: String
description: String
id: ID
readTime: Int
title: String
}
input CreateTestimonialInput {
_version: Int
avatar: String
id: ID
name: String
testimony: String
}
input DeletePostInput {
_version: Int
id: ID!
}
input DeleteTestimonialInput {
_version: Int
id: ID!
}
input ModelBooleanInput {
attributeExists: Boolean
attributeType: ModelAttributeTypes
eq: Boolean
ne: Boolean
}
input ModelFloatInput {
attributeExists: Boolean
attributeType: ModelAttributeTypes
between: [Float]
eq: Float
ge: Float
gt: Float
le: Float
lt: Float
ne: Float
}
input ModelIDInput {
attributeExists: Boolean
attributeType: ModelAttributeTypes
beginsWith: ID
between: [ID]
contains: ID
eq: ID
ge: ID
gt: ID
le: ID
lt: ID
ne: ID
notContains: ID
size: ModelSizeInput
}
input ModelIntInput {
attributeExists: Boolean
attributeType: ModelAttributeTypes
between: [Int]
eq: Int
ge: Int
gt: Int
le: Int
lt: Int
ne: Int
}
input ModelPostConditionInput {
and: [ModelPostConditionInput]
body: ModelStringInput
description: ModelStringInput
not: ModelPostConditionInput
or: [ModelPostConditionInput]
readTime: ModelIntInput
title: ModelStringInput
}
input ModelPostFilterInput {
and: [ModelPostFilterInput]
body: ModelStringInput
description: ModelStringInput
id: ModelIDInput
not: ModelPostFilterInput
or: [ModelPostFilterInput]
readTime: ModelIntInput
title: ModelStringInput
}
input ModelSizeInput {
between: [Int]
eq: Int
ge: Int
gt: Int
le: Int
lt: Int
ne: Int
}
input ModelStringInput {
attributeExists: Boolean
attributeType: ModelAttributeTypes
beginsWith: String
between: [String]
contains: String
eq: String
ge: String
gt: String
le: String
lt: String
ne: String
notContains: String
size: ModelSizeInput
}
input ModelSubscriptionBooleanInput {
eq: Boolean
ne: Boolean
}
input ModelSubscriptionFloatInput {
between: [Float]
eq: Float
ge: Float
gt: Float
in: [Float]
le: Float
lt: Float
ne: Float
notIn: [Float]
}
input ModelSubscriptionIDInput {
beginsWith: ID
between: [ID]
contains: ID
eq: ID
ge: ID
gt: ID
in: [ID]
le: ID
lt: ID
ne: ID
notContains: ID
notIn: [ID]
}
input ModelSubscriptionIntInput {
between: [Int]
eq: Int
ge: Int
gt: Int
in: [Int]
le: Int
lt: Int
ne: Int
notIn: [Int]
}
input ModelSubscriptionPostFilterInput {
and: [ModelSubscriptionPostFilterInput]
body: ModelSubscriptionStringInput
description: ModelSubscriptionStringInput
id: ModelSubscriptionIDInput
or: [ModelSubscriptionPostFilterInput]
readTime: ModelSubscriptionIntInput
title: ModelSubscriptionStringInput
}
input ModelSubscriptionStringInput {
beginsWith: String
between: [String]
contains: String
eq: String
ge: String
gt: String
in: [String]
le: String
lt: String
ne: String
notContains: String
notIn: [String]
}
input ModelSubscriptionTestimonialFilterInput {
and: [ModelSubscriptionTestimonialFilterInput]
avatar: ModelSubscriptionStringInput
id: ModelSubscriptionIDInput
name: ModelSubscriptionStringInput
or: [ModelSubscriptionTestimonialFilterInput]
testimony: ModelSubscriptionStringInput
}
input ModelTestimonialConditionInput {
and: [ModelTestimonialConditionInput]
avatar: ModelStringInput
name: ModelStringInput
not: ModelTestimonialConditionInput
or: [ModelTestimonialConditionInput]
testimony: ModelStringInput
}
input ModelTestimonialFilterInput {
and: [ModelTestimonialFilterInput]
avatar: ModelStringInput
id: ModelIDInput
name: ModelStringInput
not: ModelTestimonialFilterInput
or: [ModelTestimonialFilterInput]
testimony: ModelStringInput
}
input UpdatePostInput {
_version: Int
body: String
description: String
id: ID!
readTime: Int
title: String
}
input UpdateTestimonialInput {
_version: Int
avatar: String
id: ID!
name: String
testimony: String
}