Create a Next.js App with a MySQL Database That Builds and Deploys with Vercel

Deploy your Next.js and MySQL app with Vercel in a serverless environment.

In this guide, we will walk you through creating and deploying a Next.js app with the most popular open source database in the world, MySQL, on Vercel.

Next.js from Vercel is a production-ready framework that can help you create fast React apps. By using it along with MySQL, you can create a fast, modern web app that interacts with customer data in a performant manner.

We demonstrate the set up via an example app, that displays a paginated, gallery view of robot profiles, with individual profiles just a click away. The finished app can be found at https://next-mysql.now.sh.

Step 1: Populating Your MySQL Database

To use this guide, you will need to setup a remote MySQL database. Many cloud providers offer this service, such as Amazon Web Services, Google Cloud and Microsoft Azure. Most of them offer a free trial.

Note: Please read the trial terms and conditions carefully.

Once you have your remote MySQL database setup, you should make a note of your database credentials:

  • Database name
  • Database hostname
  • Database username
  • Database password

Using these credentials, you can connect to your database and insert your data into a new table, named profiles.

For this example, a profile consists of the following fields:

  • address string
  • avatar string
  • email string
  • id number
  • name string

For brevity, we do not cover inserting records into a MySQL database. More information on doing this can be found in the MySQL documentation.

Step 2: Set Up Your Project

Now that the database is populated, you can create a project directory and cd into it:

mkdir next-mysql && cd next-mysql

Creating and entering into the /next-mysql directory.

Next, initialize the project:

npm init

Initializing the project, this creates a package.json file.

Note: During the initializing process, npm will ask questions about your project. Answer these how you wish; there are no prerequisites for this example.

When this is complete, add serverless-mysql and sql-template-strings as dependencies:

npm i serverless-mysql sql-template-strings

Adding serverless-mysql and sql-template-strings as dependencies to the project.

Adding serverless-mysql to the project will allow you to make connections to your MySQL database. In addition to this, it also manages connections, ensuring you do not 'max out' the available connections.

Managing MySQL connections is an essential part of using it successfully in a serverless environment. This is because Serverless Functions will create multiple database connections as traffic increases. Therefore, all connections can be consumed quickly unless managed correctly - this is all handled for you by serverless-mysql.

Note: Using sql-template-strings is strongly recommended to prevent attacks via SQL Injection by using parameterized queries.

Next, add a build script to your package.json file:

{
  ...
  "scripts": {
    "build": "next build"
  }
}

Adding a build script to your package.json file.

Now, add your database credentials from step 1 to the project as Secrets using the Vercel CLI to keep them secure:

vercel secrets add MYSQL_HOST $database-hostname && vercel secrets add MYSQL_USER $database-username && vercel secrets add MYSQL_DATABASE $database-name && vercel secrets add MYSQL_PASSWORD $database-password

Adding secrets to the project.

Step 3: Create Your Reusable Database Connection

To ensure all your MySQL connections are managed by serverless-mysql, you should create a helper function to form the connection each time.

Create a /lib directory with a db.js file inside:

mkdir lib

Creating a /lib directory.

Add the following code to db.js:

const mysql = require('serverless-mysql')

const db = mysql({
  config: {
    host: process.env.MYSQL_HOST,
    database: process.env.MYSQL_DATABASE,
    user: process.env.MYSQL_USER,
    password: process.env.MYSQL_PASSWORD,
  },
})

exports.query = async (query) => {
  try {
    const results = await db.query(query)
    await db.end()
    return results
  } catch (error) {
    return { error }
  }
}

An example db.js file for your project.

Your db.js file performs the following functions:

  • Creates a connection to your MySQL database using credentials defined as Secrets
  • Exports a function that ensures connections are closed once the query has resolved
Note: The most important line is await db.end(). This prevents your app from exhausting all available connections.

Now you have a reusable database connection, perfectly suited for a serverless environment.

Step 4: Creating Your Node.js API

The next step is to create your API. Start off by creating an /api directory with a /profiles directory inside:

mkdir api && mkdir api/profiles

Creating an /api directory with a /profiles directory inside it.

Inside your /profiles directory create an index.js file with the following code:

const db = require('../../lib/db')
const escape = require('sql-template-strings')

module.exports = async (req, res) => {
  let page = parseInt(req.query.page) || 1
  const limit = parseInt(req.query.limit) || 9
  if (page < 1) page = 1
  const profiles = await db.query(escape`
      SELECT *
      FROM profiles
      ORDER BY id
      LIMIT ${(page - 1) * limit}, ${limit}
    `)
  const count = await db.query(escape`
      SELECT COUNT(*)
      AS profilesCount
      FROM profiles
    `)
  const { profilesCount } = count[0]
  const pageCount = Math.ceil(profilesCount / limit)
  res.status(200).json({ profiles, pageCount, page })
}

An example index.js file for your project.

Your index.js file performs the following functions:

  • Parses the request query parameters
  • Uses the query parameters to determine which profiles are required
  • Requests only the required profiles from the database
  • Queries the database to get the total records
  • Uses the records count to calculate pagination
  • Sends the retrieved profiles and pagination details as a response
Note: In the code snippet above, you probably noticed that we used req.query, res.status() and res.json() . These property and methods are automatically added for you when you use@vercel/node. Read more about this in the @vercel/node Runtime documentation page.

That is all the API code required to successfully use pagination in a serverless environment.

Next, create a profile.js file in your /profiles directory containing the code below:

const db = require('../../lib/db')
const escape = require('sql-template-strings')

module.exports = async (req, res) => {
  const [profile] = await db.query(escape`
    SELECT *
    FROM profiles
    WHERE id = ${req.query.id}
  `)
  res.status(200).json({ profile })
}

An example profile.js file for your project.

Your profile.js file performs the following functions:

  • Parses the request query parameter
  • Uses the query parameter to select a single profile from the database
  • Sends the retrieved profile as a response

You now have an API that will give you either all profiles or just a single one, dependent on the Route. You now need to create the app interface to display them.

Step 5: Creating Your Next.js Client

To add Next.js to your project, you should install the following dependencies:

npm i next react react-dom

Adding multiple dependencies to the project.

Next, create a /pages directory like so:

mkdir pages

Creating a /pages directory.

Now you should create an index.js file inside your /pages directory with the following code:

import Link from 'next/link'

function HomePage({ profiles, page, pageCount }) {
  return (
    <>
      <ul>
        {profiles.map((p) => (
          <li className="profile" key={p.id}>
            <Link href={`/profile?id=${p.id}`}>
              <a>
                <img src={p.avatar} />
                <span>{p.name}</span>
              </a>
            </Link>
          </li>
        ))}
      </ul>
      <nav>
        {page > 1 && (
          <Link href={`/?page=${page - 1}&limit=9`}>
            <a>Previous</a>
          </Link>
        )}
        {page < pageCount && (
          <Link href={`/?page=${page + 1}&limit=9`}>
            <a className="next">Next</a>
          </Link>
        )}
      </nav>
    </>
  )
}

export async function getServerSideProps({ req, query }) {
  const protocol = req.headers['x-forwarded-proto']
  const host = req.headers['x-forwarded-host']
  const page = query.page || 1
  const limit = query.limit || 9

  const res = await fetch(
    `${protocol}://${host}/api/profiles?page=${page}&limit=${limit}`
  )
  const data = await res.json()

  return { props: data }
}

export default HomePage

An example pages/index.js file for your project.

Your pages/index.js file performs the following functions:

  • Makes a request to the API for profiles using query parameters
  • Receives the profiles and pagination data, making them available as props
  • Lists the profiles in a gallery view
  • Uses the pagination data to create navigation buttons

The next page you should create in the /pages directory is profile.js, this will render a more detailed view of an individual profile:

import Link from 'next/link'

function ProfilePage({ profile }) {
  return (
    <>
      <div>
        <img src={profile.avatar} />
        <h1>{profile.name}</h1>
        <p>{profile.address}</p>
        <p>{profile.email}</p>
        <Link href="/">
          <a>← Back to profiles</a>
        </Link>
      </div>
    </>
  )
}

export async function getServerSideProps({ req, query }) {
  const protocol = req.headers['x-forwarded-proto']
  const host = req.headers['x-forwarded-host']

  const res = await fetch(`${protocol}://${host}/api/profiles/${query.id}`)
  const data = await res.json()

  return { props: data }
}

export default ProfilePage

An example pages/profile.js file for your project.

Your pages/profile.js file performs the following functions:

  • Makes a request to the API for a single profile using a query parameter
  • Receives the profile data, making it available as a prop
  • Displays the profile with an option to go back to the gallery

You now have a complete app with both an API and interface, the next section will show you how to deploy it seamlessly with Vercel.

Step 6: Deploy Your Project with Vercel

Getting your project ready to deploy with Vercel is easy – all that's required is the inclusion of Environment Variables.

Create a vercel.json file with the following configuration to make the environment variables available to your Serverless Functions:

{
  "env": {
    "MYSQL_HOST": "@mysql_host",
    "MYSQL_USER": "@mysql_user",
    "MYSQL_PASSWORD": "@mysql_password",
    "MYSQL_DATABASE": "@mysql_database"
  }
}

An example vercel.json file for your project.

Finally, deploy the app with Vercel.

To deploy your Next.js + MySQL app with a Vercel for Git Integration, make sure it has been pushed to a Git repository.

Import the project into Vercel using your Git Integration of choice:

After your project has been imported, all subsequent pushes to branches will generate Preview Deployments, and all changes made to the Production Branch (commonly "main") will result in a Production Deployment.



Written By
Written by mcsdevmcsdev
Written by furffurf
Last Edited on July 17th 2020