1

Hoping to get some help from someone who is familiar with Next.js. I am having an issue converting my express API routes to Next.js's internal API routes in pages which looks really promising. The issue is it doesn't seem to be working with my mongoose models and methods.

For example:

doesn't work:

 const doc = await UserModel.findOne({ email: '[email protected]' })

works:

const doc = await req.db
    .collection('users')
    .findOne({ email: '[email protected]' }) 

doesn't work:

const doc = await req.db
    .collection('users')
    .find()

Not sure if I am just doing it wrong or I have something set up incorrectly. Hoping for some help. My user model for reference:

const mongoose = require('mongoose')

const UserSchema = new mongoose.Schema({
  fullName: {
    type: String,
    required: true,
  },
  email: {
    type: String,
    required: true,
  },
  userName: {
    type: String,
    required: true,
  },
  password: {
    type: String,
    required: true,
  },
  posts: {
    type: [{ type: mongoose.Schema.Types.ObjectId, ref: 'foodPost' }],
  },
  saves: {
    type: [{ type: mongoose.Schema.Types.ObjectId, ref: 'foodPost' }],
  },
  photo: {
    type: String,
    default: 'https://via.placeholder.com/400',
  },
  followingCount: {
    type: Number,
    default: 0,
  },
  followerCount: {
    type: Number,
    default: 0,
  },
  following: {
    type: Array,
    default: [],
  },
  followers: {
    type: Array,
    default: [],
  },
  startDate: {
    type: Date,
    default: Date.now(),
  },
  notifications: {
    type: Array,
    default: [],
  },
})
export default mongoose.models.user || mongoose.model('user', UserSchema)

Had to change the export so that it would stop giving an overwrite error.

Thank you!

1 Answer 1

5

Try doing like so... (this post is a bit long, but if you follow the logic and import order, it should work):

Example project structure:

├── .next
|
├── database
|   └── index.js
|
├── models
|   ├── all.js
|   ├── index.js
|   └── teams.js
|
├── src
|   └── pages
|       ├── api
|       |   └── teams
|       |       └── names.js
|       └── index.js
|
└── next.config.json

database/index.js -- this will establish a connection to a mongo database using mongoose

const bluebird = require("bluebird");
const mongoose = require("mongoose");

// I use process.env.DATABASE as a flexible database name
// this can be set in an ".env" file or in your package.json scripts...
// like "dev" : "DATABASE=example-dev next dev"
const { DATABASE } = process.env;

const options = {
    useNewUrlParser: true, // avoids DeprecationWarning: current URL string parser is deprecated
    useCreateIndex: true, // avoids DeprecationWarning: collection.ensureIndex is deprecated.
    useFindAndModify: false, // avoids DeprecationWarning: collection.findAndModify is deprecated.
    useUnifiedTopology: true, // avoids DeprecationWarning: current Server Discovery and Monitoring engine is deprecated
};

// connects to a mongodb database
mongoose.connect(`mongodb://localhost/${DATABASE}`, options); 

// uses bluebird for mongoose promises
mongoose.Promise = bluebird; 

models/all.js -- this will require all the models to be registered to the database connection

require("./team");
require("./user");
...etc

models/index.js -- this will export the mongoose model instances (you don't need this file, as you can just import mongoose and retrieve the model(Team) from within a file, but I like to reduce repetitive imports)

const { model } = require("mongoose");

module.exports = {
    Team: model("Team"),
    User: model("User"),
    ...etc
};

models/team.js -- this will establish the schema as a mongoose model

const { Schema, model } = require("mongoose");

const teamSchema = new Schema({
    league: { type: String, required: true },
    team: { type: String, unique: true },
    name: { type: String, unique: true, lowercase: true },
});

module.exports = model("Team", teamSchema);

pages/api/teams/all.js -- import the model instance from within an API route...

import { Team } from "../../../models"; (index.js)
// alternatively: 
// import { model } from "mongoose";
// cost Team = model("Team");

/**
 * Retrieves all teams names.
 *
 * @function getAllTeamNames
 * @returns {object} - names
 * @throws {string}
 */
const getAllTeamNames = async (_, res) => {
    // aggregates all team "names" into a single array, like so:
    // [{ names: ["team1,"team2","team3","team4", ...etc] }]

    const teams = await Team.aggregate([
        { $group: { _id: null, names: { $addToSet: "$team" } } },
        { $unwind: "$names" },
        { $sort: { names: 1 } },
        { $group: { _id: null, names: { $push: "$names" } } },
        { $project: { _id: 0, names: 1 } },
    ]); 

    // returns "names" array (["team1,"team2","team3","team4", ...etc])
    res.status(200).json({ names: teams[0].names });
};

export default getAllTeamNames;

next.config.js -- this will establish a connection pool and register models before next loads

require("./database"); // establishes the mongo connection
require("./models/all"); // registers the models to the connection

...etc
Sign up to request clarification or add additional context in comments.

5 Comments

unfortunately, after trying this for the past day I couldn't get it working. Thanks anyway though!
Here's a working repo: github.com/mattcarlotta/nextjs-ssr-kit (it's a bit outdated as I haven't had time to update it, but it still works).
Thanks! I am going to try and play with this some more with the repo.
After tearing apart my code and going through your repo, I got it working! Thank you so much for the help, Matt. Crazy how much you have to do just to get it to play nice with Next js.
Glad I could help. When dealing with SSR, nothing comes easy.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.