— reactjs, mongodb — 1 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).
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 itself2yarn add mongoose # install peer-dependency mongoose
Establish connection with Mongodb in entry.server.tsx
1// Establish MongoDB connection once server boots up2connect(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.
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
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.
1import { mongoose, prop } from "@typegoose/typegoose";2
3/**4 * Typegoose Model representing MenuItem5 */6export default class MenuItem {7 _id?: mongoose.Types.ObjectId;8 @prop()9 public name!: string;10
11 // type is optional here12 @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: 07 },8 {9 _id: ObjectId("61f389fd999bc8d7b954bb55"),10 name: 'Bread',11 price: 10000,12 __v: 013 }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
1import { getModelForClass } from "@typegoose/typegoose";2import MenuItem from "../models/menu-item";3
4const MenuItemModel = getModelForClass(MenuItem);5/**6 * Get Menu Items7 * @returns List of MenuItems8 */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
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: