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

Posted

技术标签:

【中文标题】是否可以在 next.js api 中运行 Mongoose?【英文标题】:Is it possible to run Mongoose inside next.js api? 【发布时间】:2020-06-24 21:08:17 【问题描述】:

我正在为我姐姐建立一个网站,以便她可以出售她的艺术品。我正在使用 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

pages/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 时。它只会呈现页面一次,一旦刷新它就会破坏我相信架构和模型重做的事实。

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
;
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 驱动程序。像这样。

/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);
;

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

谢谢!

【问题讨论】:

如果不浏览所有代码就很难说,但我记得在使用 axios 时遇到了获取问题,切换到 isomorphic-unfetch 并且一切正常,如果你有时间github.com/syahmiyani/tpkprinting.now.sh. 【参考方案1】:

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

【讨论】:

这对我很有用。只需确保完全按照示例中的方式导出模型,而不是使用 mongoose 时的正常方式。 (这得到了我)【参考方案2】:

在这里,更完整的答案可能会有所帮助。以下是对我们有用的方法。

我建议使用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;

【讨论】:

优雅。问:例如有几个端点,一些 REST 和一个 graphql。在pages/api/graphql.ts 端点中,我们如何将处理程序与阿波罗服务器结合起来?或者我们根本就没有? apollo-server-micro 有自己的 .createHandler 方法。也许将阿波罗服务器用作apollo-server-express 的中间件? 这是最被低估的答案,在通过 google 和 *** 进行大量搜索后,它救了我。【参考方案3】:

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.js api 中运行 Mongoose?的主要内容,如果未能解决你的问题,请参考以下文章

是否可以使用 next.js 从远程文件系统获取页面

在 getServerSideProps() 中直接导入 next.js API 端点

Next JS 中的页面预渲染发生错误 [重复]

Next.js + API 在同一台服务器上

在开发模式 localhost 中从 Next.js 应用程序请求在不同端口上运行的 Graphql api

为啥在将 next.js 与环境变量一起使用时我的 API 密钥可见?