Adventures with the CDK – AppSync - Part 2
By Adam Myers
Adventures with the CDK – AppSync
Part 2 - generate some code.
Schema changes
If you want to try the code out but would like to use your own schema you can make changes to the files in the sql-scripts folder and then go through the following steps to generate the code for the changes you've made. If you're happy to use the existing schema go straight to the "Deploying the app" section.
introspection
First things first we need to be able to gather the metadata from the database and use that to inform the code we're generating. I did take a look at the options of just using the AWS introspection options that are basically the underpinnings of the wizard we're trying to emulate. After a few hours I'd really not got as much info as I would have by querying the metadata of the datasource. Anyway I'm not sure generating the code on the fly as you deploy isn't an anti-pattern. If we generate from a local copy of the schema we would at least see what we've changed before we deploy it.
To generate the code we need a local copy of the database with the schema loaded into it. For this I'm using docker compose
and loading up the contents of the init.sql file in the sql-scripts folder. If you've made changes to the schema and
test-data folders in sql-scripts you'll need to run generate-init-sql.sh in the sql-scripts folder. Once that's done
you can start the local instance using
docker-compose up
Then just run the main file in the generate module and it'll create a couple of files in the infra module.
infra/src/main/java/com/pennyhill/gen/Resolvers.java
infra/src/main/resources/gen/schema.graphql
then back in the root directory run
npx graphql-schema-utilities -s "{infra/src/main/resources/gen/schema.graphql,infra/src/main/resources/*.graphql}" \
-o "infra/src/main/resources/merged/schema.graphql"
this merges any existing schema with the generated schema. Change the AppSync stack to pull the definition from the merged file
.definition(Definition.fromFile("src/main/resources/merged/schema.graphql"))
and the resolvers to load from both the generated and custom resolvers collections
getResolverProps().forEach(
resolverProps -> ds.createResolver(props.getPrefix() + resolverProps.getFieldName() + resolverProps.getTypeName(), resolverProps)
);
getCustomResolverProps().forEach(
resolverProps -> ds.createResolver(props.getPrefix() + resolverProps.getFieldName() + resolverProps.getTypeName(), resolverProps)
);
The generate module gets the metadata from the datasource and then uses some velocity templates to generate the files. If you're interested in the detail take a look at the code (see Useful links below)
Deploying the app
Now we have everything set up we just need to get it into our AWS account. Assuming your credentials are set up for the
cli in the normal way then run the deploy.sh file in the infra folder which will take a while and hopefully work :)
Once that's done run the load-test-data.sh script in the sql-scripts folder to put some values in the tables. Then you can go to the AppSync queries area to see some data.
Conclusion
Assuming it works that's great as long as you have a sql schema you can go from there with relatively little effort to an API. Also assuming that the SQL schema is a work in progress this can flex with those changes. What we've sacrificed for expediency is any kind of local development and testing experience. This is a bit of a feature of serverless development but particularly true of this since AppSync only supported by the pro version of localstack. So without a generous benefactor handing out the pro licences we're going to have to wait for each deployment to finish before we can test in the AWS account. Maybe there's a trade off because we're potentially not going to need to make many changes to the API, bit of a pain if that's not the case though.
Where's the tests?
There's not many points in the code that we can do unit tests. The API is just an, albeit complex, infrastructure definition. I think the only real way to test this is to check it's all working in the environment. Ideally we'd have automated testing and anything we'd generated would have test generated at the same time. If it's a backend for a frontend then you could run a full integration suite of tests using cypress or testcafe. Alternatively you could run junit tests against the environment using the CICD tooling of your choice.
Going round the houses or round the bend?
The feedback loop for small changes is daunting. Write your code then wait many minutes for the changes to deploy then see if it worked. That's nothing like the kind of feedback we're used to working on a local dev environment or using TDD. I guess it's the payment for getting up and running quickly but you could loose much of what you've gained by having longer dev times for future changes.
Final thoughts on AppSync
It seems like a nice option if you're fairly sure that all you have is a API exposing a database, for example a backend for a frontend. The extended feedback loop and lack of opportunity for local testing would be a pain for anything more complex. Though if your client/employer were supportive financially you could look into the pro localstack option.
What's next
Now we have an api we can connect to it using AWS Amplify you can go further and generate code for your UI (Web, IOS, Android).
Useful links
Github Project - the current state of the code AWS CDK Examples - a AWS managed repo of CDK examples Graphql CDK examples - AWS examples repo