mongo (and mongoose) with typescript

2021-12-07

 | 

~2 min read

 | 

276 words

I was working on a project recently that was using Mongo as its data store. Unforuntately, because I was using the node driver for mongodb directly, I wasn’t getting the type safety I like.

Part of this was my poor design (guilty!). But part of this was also how the node driver is typed to interact with the database.

Take my fetchTodos for example:

src/server/todos/todos.service.ts
const todos = _db.collection("todo")

export const fetchTodos = async (
  query?: Filter<WithId<Document>>,
  options?: FindOptions<Document>,
): Promise<any | Todo[]> => {
  const found = query ? todos.find(query, options) : todos.find()
  const foundResults = await found.toArray()
  // TODO: Consider converting to a stream
  return foundResults
}

I would have liked to be able to narrow my query only to elements on the Todo model, but I couldn’t figure out how.

By leveraging Mongoose, however, I was able to simplify the logic and make sure that my queries were strongly typed.

src/server/todos/todos.service.ts
export const fetchTodos = async (
  query?: FilterQuery<Todo>,
): Promise<any | Todo[]> => {
  return await dal.findTodo(query)
}

An example of replacing Mongo’s native driver with Mongoose is in this pull request.

The other thing I like about this change set is that I split the ologic between my service and the data access layer (dal) more cleanly. As I add in user authentication, I think this separation of concerns will pay dividends.

It’s worth noting that I’m currently relying on a single connection. If I wanted to convert to multiple connections, I might have to switch from exporting the model to exporting schemas and leverage Mongoose’s createConnection instead of connect method.

Additional Resources



Hi there and thanks for reading! My name's Stephen. I live in Chicago with my wife, Kate, and dog, Finn. Want more? See about and get in touch!