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

Mon*_*ker 3 javascript mongodb reactjs next.js

我正在为我姐姐建立一个网站,以便她可以出售她的艺术作品。我正在使用 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
}
Run Code Online (Sandbox Code Playgroud)

页/ 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,
    },
  }
}
Run Code Online (Sandbox Code Playgroud)

我遇到的问题是,当我尝试在 api/get-artwork 中使用 mongoose 时。它只会渲染页面一次,一旦刷新它就会破坏我相信架构和模型重做的事实。

页面/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);
  }
});

};
Run Code Online (Sandbox Code Playgroud)

所以为了尝试解决这个问题,我决定使用本机 MongoDB 驱动程序。像这样。

/pages/api/get-artwork/

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);
};
Run Code Online (Sandbox Code Playgroud)

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

谢谢!

小智 11

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

  • 这对我来说非常有用。只需确保完全按照示例中的方式导出模型,而不是像使用 mongoose 时通常那样导出模型。(这让我着迷) (2认同)

Bal*_*ala 8

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);
    }
  });
};
Run Code Online (Sandbox Code Playgroud)

它对我来说很好用。


Blu*_*dis 7

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

我建议使用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;
Run Code Online (Sandbox Code Playgroud)

创建模型:

通常,这里的路径可能是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;
Run Code Online (Sandbox Code Playgroud)

实现下一个连接:

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

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

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

export function createHandler(...middleware) {

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

}
Run Code Online (Sandbox Code Playgroud)

在 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;
Run Code Online (Sandbox Code Playgroud)