Skip to content

Commit

Permalink
demoed a mongoose connection
Browse files Browse the repository at this point in the history
  • Loading branch information
eric-burel committed Jun 22, 2020
1 parent 07d03f5 commit d814311
Show file tree
Hide file tree
Showing 11 changed files with 323 additions and 8 deletions.
4 changes: 3 additions & 1 deletion .env.development.sample
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
NEXT_PUBLIC_GRAPHQL_URI="http://localhost:3001"
NEXT_PUBLIC_GRAPHQL_URI="http://localhost:3001"
MONGO_URI="mongodb://localhost:27017/vns"
APOLLO_SERVER_CORS_WHITELIST="http://localhost:3000"
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,14 @@ You can start editing the page by modifying `pages/index.js`. The page auto-upda

...To be completed.

## Other Next frameworks, and Next+Apollo boilerplates
## Other cool Next stuff

- [Official Apollo example from Next](https://github.com/zeit/next.js/tree/canary/examples/with-apollo)
- [Next Right Now](https://github.com/UnlyEd/next-right-now) (a complete Next Starter, relying on Prisma cloud solutions for the backend)
- [Blitz](https://blitzjs.com/) (fullstack Next without GraphQL)
- [Next react Graphql Apollo Bootstrap](https://github.com/Sebastp/Next-react-graphql-apollo_Boostrap)
- [Next advanced starter](https://github.com/borisowsky/next-advanced-starter)
- [Next advanced starter](https://github.com/borisowsky/next-advanced-starter),
- [Next - Mongo](https://github.com/hoangvvo/nextjs-mongodb-app) (no Express, no GraphQL, just Next and Mongo)

# About Next

Expand Down
6 changes: 6 additions & 0 deletions cypress/integration/vns/mongo.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
describe("connect to mongo", () => {
it("get the best restaurants in town", () => {
cy.visit("/vns/debug/mongo");
cy.get("li").should("have.length", 5);
});
});
16 changes: 16 additions & 0 deletions docs/features.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,22 @@ GraphQL Playground is available on `api/graphql`. All API routes of Next are loc

Open `api/debug/graphql-voyager` and explore your API visually.

## MongoDB

### Lambda safe connection

Relevant docs:

[Official tutorial without mongoose](https://developer.mongodb.com/how-to/nextjs-building-modern-applications)

[Best practice for Mongo in AWS Lambda](https://docs.atlas.mongodb.com/best-practices-connecting-to-aws-lambda/)

### Mongoose for schema modelling

### Conversion between GraphQL ID and Mongoose ID types

https://github.com/apollographql/apollo-server/issues/1633

## Code architecture and build

### Code in `src`
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
"i18next-http-backend": "^1.0.15",
"isomorphic-unfetch": "3.0.0",
"lodash": "^4.17.15",
"mongoose": "^5.9.19",
"next": "9.4.0",
"next-i18next": "5.0.0-beta.2",
"next-with-apollo": "5.0.1",
Expand All @@ -89,6 +90,7 @@
"@testing-library/react": "^10.2.0",
"@testing-library/react-hooks": "^3.3.0",
"@types/jest": "^25.2.3",
"@types/mongoose": "^5.7.27",
"@types/node": "^13.7.6",
"@types/react": "^16.9.23",
"@types/react-dom": "^16.9.5",
Expand Down
3 changes: 3 additions & 0 deletions src/lib/api/cors.ts → src/api/cors.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { parseEnvVariableArray } from "~/lib/utils";
import debug from "debug";
const debugCors = debug("vns:cors");

const corsWhitelist = parseEnvVariableArray(
process.env.APOLLO_SERVER_CORS_WHITELIST
Expand All @@ -9,6 +11,7 @@ const corsWhitelist = parseEnvVariableArray(
*/
const corsOptions = {
origin: function (origin, callback) {
debugCors("Origin is", origin, "Allowed origins are ", corsWhitelist);
if (!origin) {
// same origin
callback(null, true);
Expand Down
68 changes: 68 additions & 0 deletions src/api/middlewares/mongoConnection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/**
* This middleware relies on Express
*
* @see https://developer.mongodb.com/how-to/nextjs-building-modern-applications
* @see https://docs.atlas.mongodb.com/best-practices-connecting-to-aws-lambda/
* @see https://github.com/vercel/next.js/discussions/12229
*/

import { Request, Response } from "express";
import mongoose from "mongoose";
// Import mongoose models here
import "~/api/mongoose/models";

/*
async function closeDbConnection() {
try {
await mongoose.connection.close();
} catch (err) {
// Catch locally error
}
}
*/

import debug from "debug";
const debugMongo = debug("vns:mongo");

// trigger the initial connection on app startup
export const connectToDb = async () => {
if (![1, 2].includes(mongoose.connection.readyState)) {
debugMongo("Call mongoose connect");
return await mongoose.connect(process.env.MONGO_URI, {
useNewUrlParser: true,
});
}
debugMongo("Ran connectToDb, already connected or connecting to Mongo");
};

const mongoConnectionMiddleware = () => {
// init the first database connection on server startup
connectToDb();
// mongoose.set("useFindAndModify", false);

// then return a middleware that checks the connection on every call
return async (req: Request, res: Response, next) => {
try {
// To debug the number of connections in Mongo client: db.serverStatus().connections
await connectToDb();

// Do not forget to close connection on finish and close events
// NOTE: actually we don't need this. Db connection close should happen on lambda destruction instead.
// res.on("finish", closeDbConnection);
// res.on("close", closeDbConnection);
next();
} catch (err) {
res.status(500);
res.send("Could not connect to db");
}
};
};

export default mongoConnectionMiddleware;

// We need to add a converter between Mongoose ID and Apollo Server
// @see https://github.com/apollographql/apollo-server/issues/1633
const ObjectId = require("mongoose").Types.ObjectId;
ObjectId.prototype.valueOf = function () {
return this.toString();
};
2 changes: 2 additions & 0 deletions src/api/mongoose/models.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// Add your models here
import "mongoose";
25 changes: 23 additions & 2 deletions src/pages/api/graphql.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import express from "express";
import cors from "cors";
import corsOptions from "~/lib/api/cors";
import mongoose from "mongoose";
import corsOptions from "~/api/cors";
import { ApolloServer, gql } from "apollo-server-express";
import { makeExecutableSchema } from "graphql-tools";
import mongoConnection from "~/api/middlewares/mongoConnection";

/**
* Sample, naive, Apollo server. You can move this code in `src/server`
Expand All @@ -14,16 +16,32 @@ import { makeExecutableSchema } from "graphql-tools";
const typeDefs = gql`
type Query {
users: [User!]!
restaurants: [Restaurant]
}
type User {
name: String
}
type Restaurant {
_id: ID!
name: String
}
`;
const resolvers = {
Query: {
users() {
return [{ name: "Rick" }, { name: "Morty" }];
},
// Demo with mongoose
// Expected the database to be setup with the demo "restaurant" API from mongoose
async restaurants() {
const db = mongoose.connection;
const restaurants = await db
.collection("restaurants")
.find(null, null)
.limit(5)
.toArray();
return restaurants;
},
},
};
const schema = makeExecutableSchema({ typeDefs, resolvers });
Expand All @@ -47,8 +65,11 @@ const app = express();
app.set("trust proxy", true);

const gqlPath = "/api/graphql";
// setup cors
app.use(gqlPath, cors(corsOptions));
// You could init the db connection here too
// init the db
app.use(gqlPath, mongoConnection());

server.applyMiddleware({ app, path: "/api/graphql" });

export default app;
Expand Down
38 changes: 38 additions & 0 deletions src/pages/vns/debug/mongo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { useQuery } from "@apollo/react-hooks";
import gql from "graphql-tag";
import { withApollo } from "@vulcan/next-apollo";
const MongoDebugPage = () => {
const { data, loading, error } = useQuery(
gql`
query restaurants {
restaurants {
_id
name
}
}
`
);
if (loading) return <>Thinking about restaurants...</>; // NOTE: in TypeScript with React we can't return just a string
if (error)
return (
<div>
I could not remember any restaurant, sorry... Error: {error.message}
</div>
);

const { restaurants } = data;

return (
<div>
<h2>First 5 restaurants that comes to my mind:</h2>
<ul>
{restaurants.map(({ _id, name }) => (
<li key={_id}>{name}</li>
))}
</ul>
</div>
);
};
export default withApollo(MongoDebugPage, {
graphqlUri: "http://localhost:3000/api/graphql",
});
Loading

0 comments on commit d814311

Please sign in to comment.