Thoughts on Next.js and AWS Amplify a Few Months In

My team has been building a client facing application using Next.js and AWS Amplify recently. Being the most 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 light on frontend development experience 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:

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:

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.