Full Stack TypeScript: MongoDB, Node.js and Next.js

ben_lambert ·

This current iteration of benlambert.tech was started in October 2025 when I wanted a place to share my skills, my experiences and what I’ve learned over the years as a full stack developer. The first step in undertaking any new project like this is to choose a tech stack. I ultimately ended up starting with Next.js and building the rest of the stack around it.

Database

For me, MongoDB was the obvious choice for a database at the personal blog site scale. Mongoose js is an incredibly simple, well documented and easy to use ODM for MongoDB. The ODM API closely mimics the actual MongoDB API, which limits surprises while providing useful abstractions like application-level schemas that are easy to typecheck and seamlessly integrate into the wider TypeScript application. While many fantastic relational database ORMs for TypeScript also exist, I felt that at this scale it would be overkill to design a predefined rigid structure that only I will be creating, updating or deleting. The kind of data that I will be storing does not have a high level of interconnectedness, so the benefits of a relational database would be limited. Embedded sub-documents make more sense for my use case here instead of complex relational definitions. I tend to prefer PostgreSQL for most projects, so using MongoDB was a bit new to me, but it was a great developer experience to essentially “set it and forget it” and focus on building the application layer of the site.

API

The API that I chose to go with is the excellent Express.js framework on Node.js. I found it to be the perfect balance between reducing boilerplate and using vanilla Node.js modules. I knew that I wanted to build my API in TypeScript, so that limited my options to TypeScript runtime environments. That left me to choose between Node, Bun and Deno as the primary contenders. While I initially considered using Bun because of the raw performance gains, and Deno for the built-in standard Web Platform APIs, I decided on Node.js with the Express.js framework for ultimate flexibility and compatibility options. I wasn’t sure what my future API constraints or requirements would end up being so I went with the most widely used and well documented path to leave as many options on the table as possible. Another consideration that I made at the time was that I knew I would be experimenting with npm workspaces and a shared source file type checker. I knew that Node.js would be the most likely candidate to support a gradual lightweight monorepo-esque adoption out of the box with minimal extra configuration needed. The final consideration for me was that I have been using Node.js and Express for over 5 years now so it was the most familiar choice.

Frontend

For the frontend I chose a vanilla implementation of Next.js using the app router, standalone build output and cache components. While Next.js has arguably more complexity than something like vanilla React (a client-only, single-page app) or TanStack Start (client-first, isomorphic SSR by default with opt-in RSC), I wanted to leverage the immediate benefits Next.js provides by encouraging server-first design patterns. For my personal blog and my personal projects, I wanted to focus on SEO and discoverability. This is something that Next heavily leans into and abstracts away from the developer which was particularly appealing to me. Whether it’s Lighthouse, DuckDuckBot, redditbot, ia_archiver, Bingbot, LinkedInBot, etc. I can count on Vercel to keep track of how to render the site in a way that will give the bots accurate link previews and keep the site easily discoverable to automated aggregators. I did not choose a component library for frontend UI elements, instead preferring to roll my own and keep it as close to standard implementation as possible. I leaned heavily on tailwindcss and heroicons to craft the interface. A big part of why this project took so long to develop and deploy was that I wanted to meticulously craft the UI to be as performant and accessible as possible. I put a lot of time and effort into thinking about how to minimize the javascript running on the client and ways to prerender content on the server ahead of time, fully leveraging cache components to make the site blazingly fast. While this would have been easy to do without using a framework and only minimal javascript, leaning on HTML and CSS to do the heavy lifting, I wanted to keep my options open for future development on rich client only experiences within the site. Overall, the learning curve for React is already steep, and Next.js makes that even steeper, but the flexibility and benefits that Next offers is best in class. I had to be very careful and dig deep into the documentation for some aspects of the frontend application, but it has paid off immensely.

Common Abbreviations:

AbbreviationDefinition
APIApplication Programming Interface
ODMObject Document Mapper
ORMObject Relational Mapper
UIUser Interface
SEOSearch Engine Optimization
SSRServer-Side Rendering
RSCReact Server Components