Next.js Discord

Discord Forum

"Use Client" and mongoose

Answered
Tramp ant posted this in #help-forum
Open in Discord
Tramp antOP
Hi! I'm a beginner to next.js.

I'm making a page to display a project with information from a MongoDB database. A module i'm using requires the page to have "Use Client" at the top. But then when I add import { getProject } from "@/lib/data" and issue npm run dev, the program hangs on ○ Compiling /projects/[slug] ... and never compiles. When I remove "Use Client" from the top, the issue resolves, but I cannot use the module I need. Could someone help me figure out how to successfully import getProject with "Use Client" at the top?


Here is the code:
webpage
"use client"
import { getProject } from "@/lib/data"
const ProjectsPage = async ( {params} ) => {
    const { slug } = params;
    return (
        <div></div>
    );
}

export default ProjectsPage

@/lib/data.js
import { Project } from "./models"
import { User } from "./models"

import { connectToDb } from "./utils"


export const getProjects = async() => {
    try {
        connectToDb();
        const projects = await Project.find();
        return projects;
    }
    catch (err){
        console.log(err);
        throw new Error("Failed to get Projects!");
    }
};

export const getProject = async(slug) => {
    try {
        connectToDb();
        const project = [];//await Project.find({slug: slug})
        return project;
    }
    catch (err){
        console.log(err);
        throw new Error("Failed to get Project!");
    }
};
...


./utils.js
const {default : mongoose} = require("mongoose");

// Store connection in case running in dev mode.
const connection = {};


export const connectToDb = async() => {
    try {
        if (connection.isConnected){
            console.log("Using existing connection");
            return;
        }
        const db = await mongoose.connect(process.env.MONGO);
        connection.isConnected = db.connections[0].readyState;
      } catch (error) {
        console.log(error);
        throw new Error(error);
      }
}


@/lib/models
import mongoose from "mongoose";
...
const projectSchema = new mongoose.Schema({
    title: {
        type: String,
        required: true
    },
    desc: {
        type: String,
        required: true
    },
    img: {
        type: String,
    },
    userId: {
        type: String,
        required: true
    },
    slug: {
        type: String,
        required: true,
        unique: true
    }

},
{ timestamps: true}
);
...
export const Project = mongoose.models.Project || mongoose.model("Project", projectSchema);


package.json
"dependencies": {
    "aws4": "^1.12.0",
    "gist-react": "^1.0.2",
    "mongoose": "^8.0.0",
    "next": "14.1.0",
    "react": "^18",
    "react-dom": "^18",
    "react-router-dom": "^6.22.0"
  },
Answered by Komondor
it's because you're trying to do server stuff (read from database) in a client component. The option above will work. What will also work is doing the server stuff (read from database) in a server component (such as page.tsx), then within that component render a client component and pass the data you read from the database into the client component
View full answer

7 Replies

West African Lion
I would recommend using server actions when fetching from mongoDB, here is an example export async function getQuestions(params: GetQuestionsParams){ try{ connectToDatabase() const { searchQuery, filter, page = 1, pageSize = 20 } = params const skipAmount = (page - 1) * pageSize const query: FilterQuery<typeof Question> = {} if(searchQuery){ query.$or = [ { title: { $regex: new RegExp(searchQuery, "i" )}}, { content: { $regex: new RegExp(searchQuery, "i" )}} ] } let sortOptions = {} switch(filter){ case "newest": sortOptions = { createdAt: -1 } break; case "frequent": sortOptions = { views: -1 } break; case "unanswered": query.answers = { $size: 0 } break; default: sortOptions = { createdAt: -1 } } const questions = await Question.find(query) .populate({path: "tags", model: Tag}) .populate({path: "author", model: User}) .skip(skipAmount) .limit(pageSize) .sort(sortOptions) const totalQuestions = await Question.countDocuments(query) const isNext = totalQuestions > skipAmount + questions.length return { questions, isNext } } catch(e){ console.log(e) throw new Error("Error getting questions") } }
this is "use server" and when you import it you wont need "use client"
if you have client side interaction you should make it as a component and then import it.
here is the fetch,
result = await getQuestions({
      searchQuery: searchParams.q,
      filter: searchParams.filter,
      page: searchParams.page ? +searchParams.page : 1,
    });
Komondor
it's because you're trying to do server stuff (read from database) in a client component. The option above will work. What will also work is doing the server stuff (read from database) in a server component (such as page.tsx), then within that component render a client component and pass the data you read from the database into the client component
Answer
Tramp antOP
Wow tysm you two!!! This really helped!!! I tried to identify the problem for so long. I'm so glad I asked here. Tysm!!
@Komondor it's because you're trying to do server stuff (read from database) in a client component. The option above will work. What will also work is doing the server stuff (read from database) in a server component (such as page.tsx), then within that component render a client component and pass the data you read from the database into the client component
Tramp antOP
I think this is the one I wound up implementing. I added a client component to my components folder and imported it for use with the server code. I'll next figure out how to pass parameters to the client component, and if that works out, i'll be set to move on.