My team has been building a client facing application using Next.js and AWS Amplify recently. Being the only experienced UI developer on the team I got to advise what framework we should use. Using React was mandatory since our org is heavily bought in to React, and so it was really about choosing a React framework.
Given that the dev team is small, with only a single experienced UI developer (me!), and the project MVP deadline was very tight, I needed to choose a framework that wouldn’t require lots of time to set up. The options seemed to be pretty slim, mostly because I didn’t have time to spend configuring routing manually. I also wanted the framework to have good documentation I could point the other developers to when needed, and to be very popular so that search engine queries and AI assistant questions would yield good results. For this reason I went with Next.js, and honestly there weren’t really any other strong contenders. Some other things I liked about Next.js:
- Out the box I can use CSS modules and Tailwind. Since we were having to rely on Figma to code plugins this was really helpful.
- Amplify has a starter template for a Next.js project, and everything just worked.
- Although we aren’t using server components for anything dynamic, the static rendering and caching of layouts makes the app feel more snappy.
I’ve also been quite impressed with Amplify. One feature in particular that we have found very useful is pull request preview sites. These enable us to have a fresh site created for every pull request going into the main branch, enabling out QA engineer to start testing as soon as a pull request is open, and to help catch issues before they are merged to main. It’s worth noting that we have to separate amplify apps configured on the same git repository and main branch. The first is our dev environment and has auto build enabled meaning the dev site is updated for every merge to main. The second app is our production app and has auto build disabled. We manually run the build everytime we want to deploy to production.
One feature of Amplify I’ve been less impressed with is the ability to run end to end tests as part of the build pipeline. The amplify documentation describes a “deep” Cypress integration, however there are the following limitations:
- There appears to be either a bug or a feature that wasn’t incorporated in the Amplify Gen 2 UI in that the Cypress test artifacts do not show in the Amplify console for download. Currently we have to use the cli to get the relevant build job which includes a link to downloadable test artifacts.
- The documented setup results in the backend resources being deployed, then the Cypress tests running against a UI running on localhost within the build environment which uses those freshly updated and deployed backend resources. This means that if the tests fail, and the frontend deploy is cancelled, we are left with potentially incompatible backend and frontend resources deployed.
We had to add extra steps to the amplify.yml file to deploy a separate test environment, build the frontend against those resources, run the Cypress tests, and then if all tests pass finally deploy the real backend resources for the current branch and rebuild the frontend against those.
Some key parts of our enhanced build process:
Build section (of amplify.yml):
// create the test environment
aws amplify create-branch --app-id $AWS_APP_ID --branch-name $TEST_ENV_NAME --no-enable-auto-build
npx ampx pipeline-deploy --branch $TEST_ENV_NAME --app-id $AWS_APP_ID
Frontend section:
// this build will use the test backend resources (amplify.yml) generated by npx ampx pipeline-deploy in the build step. Make sure it gets replaced by a build that uses the correct backend resources later! (see postTest)
export NODE_ENV=test
npm run build
preTest / test/ postTest phases: Everything as outlined in the Amplify docs.
Extra steps in postTest:
// required because Cypress always returns zero exit code regardless of test failures!
FAILURES=$(jq -r '.stats.failures' cypress/report/mochawesome.json)
echo "Number of Cypress test failures: $FAILURES"
if [ "$FAILURES" -gt 0 ]; then
CYPRESS_EXIT_CODE=1
else
CYPRESS_EXIT_CODE=0
fi
aws amplify delete-branch --debug --app-id $AWS_APP_ID --branch-name $TEST_ENV_NAME
if [ "$CYPRESS_EXIT_CODE" -eq 0 ]; then
// Cypress tests pass - now you can deploy the real backend resources
npx ampx pipeline-deploy --branch $AWS_BRANCH --app-id $AWS_APP_ID;
export NODE_ENV=production
npm ci --cache .npm --prefer-offline
npm run build
fi
This works because the artifacts declaration in the frontent section only states WHERE the build should look for the frontent artifacts, they dont actually get uploaded to the CDN until after the postTest phase, so they can be overwritten with the correct build output in the postTest phase.
Why does this matter? Because Next.js will include in the final output values from amplify_outputs.json that contain URLs of the backend resources it should use (AppSync etc). ampliy_outputs.json is generated by npx ampx pipeline-deploy, and so we need to rebuild Next.js after the real backend resources for “main” have been deployed in the postTest phase.