As part of my pltfrm work, I needed to build a GraphQL API, using AppSync.
AppSync looks … well, just odd. It seems to start off with some good ideas and then get bogged down in detail trying to solve new problems it has introduced.
Schema
At it’s heart is a Schema
: for my purposes, this is my schema.
type Query {
getWeather(timestamp: String!): WeatherReport
getHourlyWeather(timestamp: String!): HourlyWeatherReport
getInsult(id: Int): InsultData
}
type InsultData {
insultId: Int
insult: String
}
type IntervalMeasurement {
timestamp: String!
measurement: Float!
}
type WeatherReport {
FeelsLike: Float
Temperature: Float
Clouds: Float
Uvi: Float
WindSpeed: Float
DewPoint: Float
Pressure: Int
Weather: String
Humidity: Int
WindDeg: Int
Visibility: Int
Rain: [IntervalMeasurement!]!
}
type HourlyForecast {
clouds: Float
dewPoint: Float
dt: Int
feelsLike: Float
humidity: Int
pop: Int
pressure: Int
temp: Float
uvi: Float
visibility: Int
weather: [WeatherDescription!]!
windDeg: Float
windGust: Float
windSpeed: Float
}
type WeatherDescription {
id: Int
main: String
description: String
icon: String
}
type HourlyWeatherReport {
actualTime: String
forecastHours: [HourlyForecast!]
}
schema {
query: Query
}
That all looks reasonable sensible. I’m redefining my weather model, and for fun I added a stream of random insults - I asked ChatGPT for a list of 100 insults in JSON, and spent the next 10 minutes chuckling away.
Resolvers
You then get to define your resolvers
- defined in Data Sources
. My two weather queries come from two DynamoDB tables respectively, so the query can specify the PK of the table - a timestamp
and then the resolver is pretty straight forward.
resource "aws_appsync_resolver" "get_weather" {
api_id = aws_appsync_graphql_api.this.id
type = "Query"
field = "getWeather"
data_source = aws_appsync_datasource.weather_datasource.name
request_template = <<EOF
{
"version": "2017-02-28",
"operation": "GetItem",
"key": {
"timestamp": $util.dynamodb.toDynamoDBJson($ctx.args.timestamp)
}
}
EOF
response_template = <<EOF
#if($ctx.error)
$util.error($ctx.error.message, $ctx.error.type)
#else
$util.toJson($ctx.result)
#end
EOF
}
I added the third to work out how to integrate a Lambda, and it is pretty straightforward once you have the permissions sorted : just returning a Map to match the schema.
public Map<String, Object> handleRequest(final Map<String, Object> stringObjectMap, final Context context) {
final Map<String, Object> response = new HashMap<>();
response.put("insultId", insultIndex);
response.put("insult", this.insults.get(insultIndex));
return response;
}
Once I have an API key, that all works.
Weirdness # 1 : front end only ?
It really doesn’t look like they want you to use this from the back-end. I guess the main purpose of GraphQL is to make mobile and web applications faster by only sending the data you need; but I haven’t yet found a nice library for Java to try this from the back end.
Weirdness # 2 Authorisation and protection
For a REST API, you can specify e.g. scope for each endpoint, and control access. The ‘open’ nature of GraphQL makes that harder; as does the fact that the schema can reference itself, so that you could get deep loops of data.
This is a well known issue with GraphQL : they even say they’re avoiding talking about it
¯\_(ツ)_/¯