簡體   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