Ошибка Mongoose: “Схема не зарегистрирована для модели \”User\”.\nИспользуйте mongoose.model(name, schema)” в API-маршруте Next.js, несмотря на импорт модели

Вопрос или проблема

Я работаю над маршрутом API Next.js, который извлекает блоги и заполняет поле user_id данными из модели User. Этот маршрут работал вчера, но начал выдавать ошибку:

Ошибка

{
    "success": false,
    "message": "Не удалось получить блоги по какой-то причине",
    "error": "Схема не была зарегистрирована для модели \"User\".\nИспользуйте mongoose.model(name, schema)"
}

Модель User

import mongoose, { Document, Schema } from 'mongoose';

export interface IUser extends Document {
  name?: string;
  username: string;
  email: string;
  password: string;
  img?: string;
  bio?: string;
  role?: 'admin' | 'moderator' | 'user';
  created_at?: Date;
  isVerified?: boolean;
  verifyCode?: string;
  verifyCodeExpiry?: Date;
}

const userSchema: Schema<IUser> = new mongoose.Schema({
  name: { type: String, maxlength: 100 },
  username: { type: String, maxlength: 50, unique: true, required: true },
  email: { type: String, maxlength: 100, unique: true, required: true },
  password: { type: String, maxlength: 255, required: true },
  img: { type: String, maxlength: 255 },
  bio: { type: String },
  role: { type: String, enum: ['admin', 'moderator', 'user'], default: 'user' },
  created_at: { type: Date, default: Date.now },
  isVerified: { type: Boolean, default: false },
  verifyCode: { type: String, maxlength: 255 },
  verifyCodeExpiry: { type: Date },
});

export default mongoose.models.User || mongoose.model<IUser>('User', userSchema);

Я проверил, что:

Модель User импортирована, но не используется в Blog для заполнения user_id. Подключение к базе данных устанавливается через dbConnect() перед любыми операциями с базой данных.

getblogs/route.ts

import { NextResponse, NextRequest } from 'next/server';
import { sortbyQuerySchema } from '@/schemas/blogSchema';
import dbConnect from '@/lib/db';
import Blog from '@/models/Blog.models';
import User from '@/models/User.models';

export async function GET(req: NextRequest) {
  try {
    await dbConnect();

    // Извлечение и валидация параметров запроса
    const { searchParams } = new URL(req.url);
    const queryParam = {
      sortby: searchParams.get('sortby') // например, sortby=likes
    };

    const parsedVal = sortbyQuerySchema.safeParse(queryParam);

    if (!parsedVal.success) {
      const sortbyErrors = parsedVal.error.format().sortby?._errors || [];
      return NextResponse.json(
        {
          success: false,
          error: parsedVal.error.message,
          message: sortbyErrors.length > 0 ? sortbyErrors.join(', ') : 'Неверные параметры запроса'
        },
        { status: 400 }
      );
    }

    const { sortby } = parsedVal.data;
    let blogs;

    switch (sortby) {
      case 'comments':
        blogs = await Blog.find({ comments_count: { $exists: true } }).sort({ comments_count: -1 }).populate({ path: 'user_id', select: 'name username'});
        return NextResponse.json({ success: true, message: 'Блоги отсортированы по комментариям', data: blogs }, { status: 200 });

      default:
        return NextResponse.json({ success: false, message: 'Неверный параметр sortby' }, { status: 400 });
    }
  } catch (error:any) {
    console.error('Ошибка получения блогов', error);
    return NextResponse.json(
      {
        success: false,
        message: "Не удалось получить блоги по какой-то причине",
        error: error.message || error
      },
      { status: 500 }
    );
  }
}

JSX компонент

'use client'
import React, { useEffect, useState } from 'react'
import TrailerPost from './TrailerPost'
import axios from 'axios'


interface User {
    _id: string;
    name: string;
    username: string;
}
interface blogs {
    category_id: string;
    comments_count: number;
    content: string;
    img: string;
    likes: number;
    published_at: string;
    title: string;
    user_id: User;
    __v: number;
    _id: string;
}

const TrailerPostPage = () => {

    const [blogs, setBlogs] = useState<[blogs] | []>([])

    useEffect(()=>{
        const data = axios.get('/api/blogs/getblogs?sortby=latest');

        data.then((response)=>{
            setBlogs(response.data.data);
            console.log(response.data.data)
        }).catch((error: any)=>{
            console.log(error)
        })

    },[])

  return (
      <div className="flex flex-col gap-10 pt-3">
        {
            blogs && blogs.map && blogs?.map((blog, index)=>{
                return (
                    <TrailerPost key={index} blog={blog} />
                )
            })
        }
      </div>
  )
}

export default TrailerPostPage

db.ts

import mongoose, { ConnectOptions } from 'mongoose';

const MONGODB_URI: string = process.env.MONGODB_URI || '';

if (!MONGODB_URI) {
  throw new Error('Пожалуйста, определите переменную окружения MONGODB_URI в .env.local');
}
let isConnected = false;
async function dbConnect(): Promise<mongoose.Connection> {
  if (isConnected) {
    return mongoose.connection;
  }

  const opts: ConnectOptions = {
    bufferCommands: false,
  };

try {
    const connection = await mongoose.connect(MONGODB_URI, opts);
    isConnected = true;
    return connection.connection;
} catch (error: any) {
  console.error('Ошибка подключения к базе данных:', error);
  throw error;
}
}

export default dbConnect;

Ожидаемое поведение:

{
    "success": true,
    "message": "Блоги отсортированы по комментариям",
    "data": [
        {
            "category_id": "605c72e9c23b6c001f9450e5",
            "_id": "671a936f0b782bb795652180",
            "user_id": {
                    "_id": "2o8634v2rt8r6rgr8r98",
                    "name": "Алан",
                    "username": "alanbjy"
             },
            "title": "Это заголовок блога для этого блога 345",
            "content": "Это содержание для этого блога44545",
            "likes": 0,
            "comments_count": 0,
            "published_at": "2024-10-24T18:35:27.475Z",
            "__v": 0
        }
    ]
}

Предоставленный мной код работал хорошо вчера.
Вопросы:

  1. Существует ли проблема с кэшированием или одиночным экземпляром при импортах моделей Mongoose в Next.js?
  2. Почему модель внезапно стала незарегистрированной, если она ранее работала без проблем?
  3. Есть ли известные решения или лучшие практики для регистрации моделей в маршрутах API Next.js?

Среда
next: 15.0.0-canary.174
mongoose: 8.7.2
node: v20.6.1

Ответ или решение

Ошибка Mongoose "Schema hasn’t been registered for model \"User\". Use mongoose.model(name, schema)" может возникать по нескольким причинам, особенно в контексте использования Next.js вместе с MongoDB через Mongoose. Ваша ситуация, в которой программа работала ранее без проблем, а потом suddenly выдала это сообщение об ошибке, может быть особенно запутанной. Рассмотрим основные возможные причины возникновения этой проблемы и предложим решения.

1. Проблемы с кэшированием и управлением импортами

При использовании Mongoose в Next.js важно помнить, что каждая функция API в Next.js обрабатывается в отдельном контексте, что может привести к проблемам с кэшированием.

Решение:

Убедитесь, что модели импортируются и регистрируются только один раз. Вы можете использовать условие:

export default mongoose.models.User || mongoose.model<IUser>('User', userSchema);

Это гарантирует, что модель не будет повторно зарегистрирована, если уже существует.

2. Неявные зависимости

Если ваш код работает с асинхронными функциями и соединениями с базой данных, существует вероятность, что модель могла быть импортирована до того, как произошло её создание. Проверьте порядок выполнения загрузки моделей и подключений к базе данных.

Решение:

Убедитесь, что dbConnect вызывается перед обращением к моделям. Один из вариантов — вызывать dbConnect() непосредственно перед тем, как использовать модель User.

Пример кода:

await dbConnect();
// Затем использовать User здесь

3. Обновления пакетов

Иногда обновление зависимостей может привести к изменениям в поведении. Посмотрите, если есть какие-либо изменения в API или известные проблемы в версиях, которые вы используете.

Решение:

Проверьте историю изменений (changelog) Mongoose и Next.js на предмет об известных проблем, связанных с регистрацией моделей. Если такое обновление было проведено недавно, можете попробовать откатиться на предыдущую версию для выполнения проверки.

4. Проверка окружения

Убедитесь, что ваше окружение правильно настроено, особенно переменные окружения, которые могут влиять на подключение к БД. Если у вас остались неиспользуемые или устаревшие переменные, это может привести к проблемам с подключением.

5. Обработка ошибок

В некоторых случаях, причиной проблемы может быть момент, когда сам Mongoose ставит тайм-аут на соединение. Проверьте корректность подключения и обработайте возможные исключения.

Пример обработки:

try {
  await dbConnect(); 
} catch (error) {
  console.error('Connection error:', error);
}

Заключение

Разобравшись в потенциальных причинах, вы можете реализовать предложенные решения для устранения проблемы. Используйте best practices для организации кода в Next.js, что поможет избежать возникновения подобных ошибок в будущем. Это могут быть:

  • Централизованный импорт моделей в одно месте.
  • Изолированное управление подключением к БД.
  • Однократная регистрация моделей для предотвращения конфликтов.

Не забывайте также делать резервные копии и работать с системой контроля версий при внесении значительных изменений в код. Это позволит вам легко откатиться к предыдущим рабочим версиям.

Оцените материал
Добавить комментарий

Капча загружается...