Max Next – Section 9 – Project Time: API Routes


Courses

Updated Jul 15th, 2021

Continuing Events Project With Newsletter Registration Flow and Comments feature

Table of Contents

Chapter Details

Chapter 148. Module Introduction

Chapter 149. Starting Setup & A Challenge For You!

Off Screen added inputs folder for comments button and newsletter signup

Chapter 150. Adding a Newsletter Route

Add newsletter.js file into “api” folder and create a handler function. Make sure incoming request is a post request. Extract data from the req.body and add super simple validation for the email.

From inside the “newsletter-registration.js” file send a fetch request to your new API route

Chapter 151. Adding Comments API Routes

Add a dynamic “[eventId].js” file in a new “api/comments” folder. Create handler function. Check request method and do something for GET and something for POST requests.

Chapter 152. Connecting the Frontend To the Comments API Routes

From inside “new-comment.js” file grab the data from the form using refs and run simple front-end validation (which we shouldn’t trust).

In the “comments.js” file is where we send the request.

Leverage the useEffect hook to show comments via a “CommentList” component

Chapter 153. Setting Up A MongoDB Database

Instead of just logging the data, we want to store it more permanently. We could write the data to a file but it is more realistic to use a database.

Create DB cluster using free and default options. Install the “mongodb” package which is the official driver.

Chapter 154. Running MongoDB Queries From Inside API Routes

Run the insert() method to insert entries as documents. Using ES6 syntax we can import {MongoClient} from “mongodb” and use tot set up a connection with the “.connect()” method. Add a new db user with read and write access and get a connection string. Ensure your IP address is whitelisted.

Instead of giving connect a callback you can just use a .then() block which also gives us access to the client.

// inside of async handler function in newsletter.js

const client await MongoClient.connect(
  'yourConnectionString'
)
  const db = client.db()

  await db.collection('emails').insertOne({email: userEmail})

  client.close()

  res.status(201).json({message: 'Signed Up!'})
})

Chapter 155. Inserting Comments Into The Database

In the [eventId].js file, connect to the database by duplicating the code above (of course you could use helper function). If you update the connection string database name does this auto-create a new database for you? If so this is awesome. I know this is the case for the .collection() method.

Chapter 156. Getting Data From The Database

In the “[eventId].js” file work with the client get the comments with the “.find()” method.

// sorts by desc order so the latest comment is the first comment.

const documents = await db.collection("comments").find().sort({_id: -1}).toArray()

Chapter 157. Improvement: Getting Comments For A Specific Event

Here’s a little optional improvement you could add to the code.

At the moment, we’re always fetching all (!) comments – no matter for which event we sent a GET request.

We might just want to fetch the comments that belong to that specific event instead.

You can adjust the code to ensure that only the comments for a specific event (by event id) are fetched.

For this, you need to change the code in two places.

1) In helpers/db-util.js, add an extra, optional parameter (with a default value) to the getAllDocuments() parameter list:

export async function getAllDocuments(client, collection, sort) { ... }
becomes

export async function getAllDocuments(client, collection, sort, filter = {}) { ... }
The filter = {} parameter allows us to set a filter for finding documents. The default (an empty object: {}) ensures that NO filter is applied (i.e. we get ALL documents).

To use this filter, also change the find() method inside of the getAllDocuments() function:

const documents = await db
    .collection(collection)
    .find(filter) // this changed - we use the "filter" parameter!
    .sort(sort)
    .toArray();

2) In pages/api/comments/[eventId].js, pass a value for the filter argument:

Inside of the req.method === ‘GET’ block, change the way you call getAllDocuments().

const documents = await getAllDocuments(
    client,
    'comments',
    { _id: -1 },
);
should become

const documents = await getAllDocuments(
    client,
    'comments',
    { _id: -1 },
    { eventId: eventId } // this was added!
);

By adding these changes, you ensure that you only fetch the comments that really belong to a specific event.

Chapter 158. Adding Error Handling

When you run server-side code sometimes things do not go right. Sometimes the connection to the database fails or, even if the connection is successful, the insertion or update, or other method, could fail. We deal with this with try/catch blocks. In the newsletter.js file actually wants to use two try/catch blocks and will do so with the help of two helper functions, (connectDatabase and insertDocument).

A good watch.

Chapter 159. More Error Handling

Create a “db-util” file that contains re-usable versions of the functions above.

Chapter 160. A Final Note On MongoDB Connections

In this course, we always close our MongoDB connections via client.close().

This works and you can do that.

If you build an application where your MongoDB-related code will execute frequently (e.g. the API route will be hit frequently), you might want to take advantage of MongoDB’s “connection pooling” though.

For this, simply remove all client.close() calls from your code. The connection will then NOT be closed and will be re-used across requests.

Chapter 161. Module Summary

Chapter 162. Module Resources