Next.js is a really exciting framework that makes it simple to build React applications with excellent features that you’d expect in production. I won’t go into much detail here, as the Next.js documentation does that brilliantly, but one of the key things that stood out is the easy development experience.
By using create-next-app you can immediately get set up with an interactive dev experience that auto-reloads code changes in your browser as you make changes. Next.js also has support for building web APIs without needing any separate API server (e.g, ASP.NET Core, Go’s net/http package). It can serve your frontend, and handle your API requests all in one.
But what if your backend API needs to request data from a database? Well, this is easy enough to do via packages like
serverless-mysql that let you initiate connections to a MySQL database from NodeJS. Add the dependency, run your queries in response to an API call in Next.js and then send the results back to the frontend (usually as JSON). Easy.
Something is missing…
Next.js itself is great. From a dev perspective it’s easy to get set up instantly with an environment and start coding.
But when it comes to building an app that requires a backend database, things get a bit more tricky as a developer.
In production, you’ve likely got some kind of managed database offering (or a DBA) that is responsible for maintaining your database. You get given some connection details and off you go.
But as a dev who wants to get started actually making changes, you might start to ask yourselves these kinds of questions:
- Should I install the database engine on my machine?
- Should I run it in a Docker container?
- If I run it in Docker, how do I persist my data in the case of container restarts?
- Should I connect to a database instance I share with the rest of the development team?
- What if I need production-like data? Restoring a backup can take a long time…
If you’ve set up Next.js but you’ve hit this roadblock, you might be frustrated with how good the development experience was up to this point. Suddenly, you have to worry about maintaining a stateful database alongside your dev environment, and that’s not insignificant.
Levelling the playing field
Getting a database environment should be as easy as scaffolding an entire Next.js application. So we set about making that reality.
Spawn is a new solution we’re working on that aims to make it really easy to include databases in your dev and CI environments. It removes the uncertainty around how to get a database environment and instead encourages you to spin them up and down as much as you like, without any concern over infrastructure.
To really put this into perspective, let’s see how Spawn fits in a Next.js application.
If you’d like to check out the code I’ll be discussing, you can review the GitHub repository here.
Hooking things up
For the sake of brevity, I followed the tutorial on setting up a Next.js app with a MySQL Database That Builds and Deploys with Vercel. This is an excellent tutorial which I encourage you to check out, but we won’t be concerning ourselves with the deployment side for now.
The real question is how do we spin up that database and hook it up with the API that Next.js makes available to us?
Well, Spawn is entirely commandline driven. As such, it’s possible to configure an npm script that runs just before the
dev script provided by next.js. This
predev script does one thing: create a Spawn data container and update the
.env.local file with the connection details to be consumed by our API.
Here’s a snippet of the
if containersExist; then
echo “Spawn data container already provisioned”
echo “Provisioning Spawn data container for dev”
spawnctl create data-container --image $DATA_IMAGE \
--name $CONTAINER_NAME -q
echo “Spawn data container successfully provisioned”
fiecho “Updating data container connection details in .env.local”db_conn_json=$(spawnctl get data-container $CONTAINER_NAME -o json)DB_HOST=$(echo $db_conn_json | jq .host)
DB_PORT=$(echo $db_conn_json | jq .port)
DB_USERNAME=$(echo $db_conn_json | jq .user)
DB_PASSWORD=$(echo $db_conn_json | jq .password)cat << EOF > .env.local
As you can see at the end of this, we write the database connection details to the
.env.local file. This presumes that I already have a data image available (which can be created from SQL Scripts, or the latest backup from production if you care about production-like data) and then creates a data container I can use which is a copy of that data image. This data container is created instantly — regardless of the size of the data image.
There’s absolutely nothing else for me to do here. Instead of needing to install Docker or a database engine on my machine and start it up, wait for connections to be accepted, then figure out the correct connection details to pass through, all I have to do is ask Spawn to give me a dev database and a few seconds later I’ve got the connection details.
This makes it much easier to bootstrap an entire dev environment including the database.
The best bit? All of this is committed to source control. After installing the
spawnctl, I can checkout the repository on my machine, run
yarn dev and then I’ve got my Next.js application up and running, backed by an API which can communicate with my database instantly. Onboarding a new dev is as simple as asking them to clone your repo.
Spawn has a bunch of other benefits, like being able to save the state of my database as I work in case I accidentally mess up and delete some data, and I can even make as many save points as I like to create a history of revisions. Equally, each dev in my team gets their own isolated data container for use in development, so I don’t have to worry about sharing a single instance and overwriting others’ changes.
If you’d like to take a more detailed look at the code to set this up, you can view it in the GitHub repo.
Get Spawn now
We’re currently accepting beta users for Spawn, so if this workflow looks like something you’d be interested in you can sign up to get access here.