Skip to content
Chandan Kumar

Remix Run Tutorial: Mongodb connection with Mongoose and Typegoose

reactjs, mongodb1 min read

We will use Typegoose to create MongoDB models instead of using Mongoose directly.

"Typegoose is a "wrapper" for easily writing Mongoose models with TypeScript."

So I had written a previous blog post which was very rudimentary way of fetching MongoDB data into Remix app. This tutorial is an upgrade on that. We would use model and service to fetch the data and render it in the controller(I am referring Remix routes as controller).

Steps

  • Establish mongodb connection on server start
  • Create a Typegoose Model
  • Render data in a Remix Route.

Getting started

When remix server starts up we can connect to database. This allows us to use connection pool for a faster data pull from MongoDB. This can be used effectively if you are using something like Remix default server or Express Adapter.

To install Typegoose

1yarn add @typegoose/typegoose # install typegoose itself
2yarn add mongoose # install peer-dependency mongoose

Establish connection with Mongodb in entry.server.tsx

entry.server.tsx
1// Establish MongoDB connection once server boots up
2connect(process.env.MONGODB_URL as string)
3 .then(() => console.log({ mongoDb: "Connected" }))
4 .catch((err) => {
5 console.log({ mongoErr: err });
6 });

For above to work make sure that you have MONGODB_URL set in enviornment.

.env
1MONGODB_URL=mongodb://SOME_USER:PASSWORD@localhost/menu?authSource=admin

To load .env values in development mode you would want to load it first.

So to do that I have updated package.json with the following entry in scripts

package.json
1"dev": "node -r dotenv/config node_modules/.bin/remix dev",

In this example I am going to list out food menu items collection from mongodb. So will create a Typegoose model for it.

app/server/models/menu-item.ts
1import { mongoose, prop } from "@typegoose/typegoose";
2
3/**
4 * Typegoose Model representing MenuItem
5 */
6export default class MenuItem {
7 _id?: mongoose.Types.ObjectId;
8 @prop()
9 public name!: string;
10
11 // type is optional here
12 @prop({ type: Number })
13 public price?: number;
14}

My menuitems collection looks something like this

1[
2 {
3 _id: ObjectId("61f389e8999bc8d7b954bb52"),
4 name: 'Pizza',
5 price: 10000,
6 __v: 0
7 },
8 {
9 _id: ObjectId("61f389fd999bc8d7b954bb55"),
10 name: 'Bread',
11 price: 10000,
12 __v: 0
13 }
14]

While we can use MenuItem model directly in the remix route, I prefer to use a service layer so that my routes are free from logic to fetch data. Menu Service would look something like below

menu-item-service.ts
1import { getModelForClass } from "@typegoose/typegoose";
2import MenuItem from "../models/menu-item";
3
4const MenuItemModel = getModelForClass(MenuItem);
5/**
6 * Get Menu Items
7 * @returns List of MenuItems
8 */
9export async function getMenuItems(): Promise<MenuItem[]> {
10 const items = await MenuItemModel.find();
11 return items ? items : [];
12}

Finally to menu route to can now use this service method to populate menu items

routes/menus/index.tsx
1export let loader: LoaderFunction = async (): Promise<MenuItem[]> => {
2 return await getMenuItems();
3};
4
5export default function TodosIndex() {
6 let menuItems = useLoaderData();
7 return (
8 <div>
9 <div className="container mx-auto max-w-sm">
10 <h1 className="text-xl">Menu Items</h1>
11 <table>
12 <thead>
13 <th className="p-2">Name</th>
14 <th className="p-2">Price</th>
15 </thead>
16 <tbody>
17 {menuItems &&
18 menuItems.map((t: MenuItem, index: number) =>
19 renderMenuItem(t, index)
20 )}
21 </tbody>
22 </table>
23 </div>
24 </div>
25 );
26}
27<tr className="border" key={t._id ? t._id.toString() : index}>
28 <td className="p-2">{t.name}</td>
29 <td className="p-2">{t.price ? t.price / 100 : "NA"}</td>
30</tr>;

Similar article:

  1. Remix Tutorial: Firebase Authentication

Comments

Copyleft. WTH
Theme by LekoArts