繁体   English   中英

是否可以在 next.js api 中运行 Mongoose?

[英]Is it possible to run Mongoose inside next.js api?

我正在为我姐姐建立一个网站,以便她可以出售她的艺术品。 我正在使用 Next.js 来设置所有内容。 该网站通过从数据库中抓取一个数组并通过它进行映射来呈现艺术品。

两个示例对象

  {
    id: 8,
    path: "images/IMG_0008.jpg",
    size: "9x12x.75",
    price: "55",
    sold: false
  }
  {
    id: 9,
    path: "images/IMG_0009.jpg",
    size: "9x12x.75",
    price: "55",
    sold: false
}

页面/Shop.js

import Card from "../Components/Card";
import fetch from 'node-fetch'
import Layout from "../components/Layout";

function createCard(work) {
  return (
    <Card
      key={work.id}
      id={work.id}
      path={work.path}
      size={work.size}
      price={work.price}
      sold={work.sold}
    />
  );
}

export default function Shop({artwork}) {
  return (
    <Layout title="Shop">
      <p>This is the Shop page</p>

        {artwork.map(createCard)}
    </Layout>
  );
}

export async function getStaticProps() {
  const res = await fetch('http://localhost:3000/api/get-artwork')
  const artwork = await res.json()


  return {
    props: {
      artwork,
    },
  }
}

我遇到的问题是当我尝试在 api/get-artwork 中使用 mongoose 时。 它只会呈现页面一次,一旦刷新,它就会中断我相信会重做 Schema 和 Model 的事实。

页面/api/get-artwork.js/

const mongoose = require("mongoose");



mongoose.connect('mongodb://localhost:27017/ArtDB', {
  useNewUrlParser: true,
  useUnifiedTopology: true,
  useFindAndModify: false
});

const itemsSchema = {
  id: String,
  description: String,
  path: String,
  size: String,
  price: Number,
  sold: Boolean
};
const Art = mongoose.model("Art", itemsSchema);

export default (req, res) => {
  Art.find({sold: false}, (err, foundItems)=> {
  if (err) {
    console.log(err);
  } else {
    console.log(foundItems);
    res.status(200).json(foundItems);
  }
});

};

因此,为了解决这个问题,我决定使用本机 MongoDB 驱动程序。 像这样。

/页面/API/获取艺术品/

const MongoClient = require('mongodb').MongoClient;
const assert = require('assert');

// Connection URL
const url = 'mongodb://localhost:27017';

// Database Name
const dbName = 'ArtDB';

// Create a new MongoClient
const client = new MongoClient(url, {useUnifiedTopology: true});

let foundDocuments = ["Please Refresh"];

const findDocuments = function(db, callback) {
  // Get the documents collection
  const collection = db.collection('arts');
  // Find some documents
  collection.find({}).toArray(function(err, arts) {
    assert.equal(err, null);
    foundDocuments = arts;
    callback(arts);
  });
}

// Use connect method to connect to the Server
client.connect(function(err) {
  assert.equal(null, err);

  const db = client.db(dbName);

  findDocuments(db, function() {
     client.close();
   });

});
export default (req, res) => {
  res.send(foundDocuments);
};

这在大多数情况下都有效,但偶尔不会返回数组。 我认为这是因为页面在 mongodb 部分完成之前加载? 所以我想我的问题是,无论是使用 mongoose 还是本机驱动程序,我如何才能 100% 确保每次都能正确加载艺术作品。

谢谢!

Next.js 团队有一组很好的示例代码,他们定期添加,其中之一是 Next.js 与 MongoDB 和 Mongoose。 检查一下, https://github.com/vercel/next.js/tree/canary/examples/with-mongodb-mongoose ,如果您仍在寻找解决方案,希望这会有所帮助。

pages/api/get-artwork.js/

const mongoose = require("mongoose");

mongoose.connect("mongodb://localhost:27017/ArtDB", {
  useNewUrlParser: true,
  useUnifiedTopology: true,
  useFindAndModify: false,
});

const itemsSchema = {
  id: String,
  description: String,
  path: String,
  size: String,
  price: Number,
  sold: Boolean,
};

let Art;

try {
  // Trying to get the existing model to avoid OverwriteModelError
  Art = mongoose.model("Art");
} catch {
  Art = mongoose.model("Art", itemsSchema);
}

export default (req, res) => {
  Art.find({ sold: false }, (err, foundItems) => {
    if (err) {
      console.log(err);
    } else {
      console.log(foundItems);
      res.status(200).json(foundItems);
    }
  });
};

它对我来说很好用。

更完整的答案在这里可能会有所帮助。 这就是对我们有用的东西。

我建议使用next-connect库使这更容易一点,而不是那么多余。

注意到 dev 中不必要的重新连接,所以我绑定到 Node.js 中的全局 this 属性。 也许这不是必需的,但这就是我注意到的。 可能与开发过程中的热重载有关。

这是一篇冗长的文章,但并不像看起来那么复杂,如果您有任何问题,请发表评论。

创建中间件助手:

import mongoose from 'mongoose';

// Get your connection string from .env.local

const MONGODB_CONN_STR = process.env.MONGODB_CONN_STR;

const databaseMiddleware = async (req, res, next) => {

  try {

    if (!global.mongoose) {

      global.mongoose = await mongoose.connect(MONGODB_CONN_STR, {
        useNewUrlParser: true,
        useUnifiedTopology: true,
        useFindAndModify: false,
      });

    }

  }
  catch (ex) {
    console.error(ex);
  }

  // You could extend the NextRequest interface 
  // with the mongoose instance as well if you wish.
  // req.mongoose = global.mongoose;

  return next();

};

export default databaseMiddleware;

创建模型:

通常,这里的路径可能是src/models/app.ts

import mongoose, { Schema } from 'mongoose';

const MODEL_NAME = 'App';

const schema = new Schema({
  name: String
});

const Model = mongoose.models[MODEL_NAME] || mongoose.model(MODEL_NAME, schema);

export default Model;

实现下一个连接:

通常我会把它放在像src/middleware/index.ts这样的路径中(如果不使用 Typescript,或者 index.js)。

注意:这里的...middleware只允许您(见下文)在创建处理程序时即时传递额外的中间件。

这非常有用,因为我们的处理程序创建者可以拥有诸如日志记录和其他有用的中间件之类的东西,因此它在每个页面/api 文件中都不是那么多余。

export function createHandler(...middleware) {

  return  nextConnect().use(databaseMiddleware, ...middleware);

}

在 Api 路由中使用:

放在一起,我们现在可以轻松使用我们的应用程序模型

import createHandler from 'path/to/above/createHandler';
import App from 'path/to/above/model/app';

// again you can pass in middleware here
// maybe you have some permissions middleware???
const handler = createHandler(); 

handler.get(async (req, res) => {
  
  // Do something with App
  const apps = await App.find().exec();

  res.json(apps);

});

export default handler;

当我们将 mongoose 与express.js一起使用时,我们连接到 mongodb 并运行一次代码。 但是在 next.js 每次我们需要连接到 mongodb 时,我们都必须运行连接代码。

连接到 mongodb

import mongoose from "mongoose";

const dbConnect = () => {
  if (mongoose.connection.readyState >= 1) {
    // if it is not ready yet return
    return;
  }

  mongoose
    .connect(process.env.DB_LOCAL_URI, {
      //****** since  mongoose 6, we dont need those******
      //   useNewUrlParser: true,
      //   useUnifiedTopology: true,
      //   useFindAndModify: false,
      //   useCreateIndex: true,
    })
    .catch((err) => console.log(err))
    .then((con) => console.log("connected to db"));
};

export default dbConnect;

接下来在里面使用 api

在运行处理程序代码之前,您需要先连接

import dbConnect from "../../../config/dbConnect";

dbConnect();

... then write handler code

简洁的 OO 方法

受@Blujedis 的启发,这就是我最终得到的答案。

/* /api/util/handler.js */

import nextConnect from "next-connect";
import mongoose from "mongoose";

export class Handler {

    static dbConnection = null

    static dbMiddleware = async (req, res, next) => {
        try {
            Handler.dbConnection ||= await mongoose.connect(process.env.MONGO_URL)
            next()
        } catch (err) {
            console.error(err);
            next(err)
        }
    }

    constructor(...middleware) {
        return nextConnect().use(Handler.dbMiddleware, ...middleware);
    }
}

示例处理程序:

/* /api/people.js */

import { Handler } from "./util/handler"
import { Person } from "./models/person"

export default
    new Handler()
        .get(async (req, res) => {
            const people = await Person.find({})
            return res.status(200).json(people)
        })

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM