Docs Menu
Docs Home
/ /

Integrate MongoDB with Nuxt and Vue

This tutorial guides you through building a Nuxt application that uses Vue and integrates with MongoDB. Nuxt is a Vue framework that provides file-based routing, server-side rendering, and built-in API routes through its Nitro server engine. Nuxt combined with Vue allows you to build complete applications without a separate back-end server.

The application in this tutorial contains the following layers:

  • Database layer: MongoDB provides data storage and retrieval

  • Server layer: Nuxt server routes handle API logic and database interactions

  • Presentation layer: Vue implements the user interface with reactive data binding

Nuxt builds on Vue to provide a full-stack framework with a Node.js runtime for server routes, making it a natural fit for the MongoDB Node.js driver. You can connect to MongoDB directly from your Nuxt server routes or server-side utilities and return documents to your Vue components without maintaining a separate back-end service or API layer.

MongoDB's flexible document structure maps easily to JavaScript objects. As a result, you can work seamlessly with MongoDB data in your Vue components, whether you are using the Options API or the Composition API. The documents you fetch from MongoDB can be passed straight into your components and templates, reducing the need for complex data transformation or an additional object-relational mapping (ORM) layer.

Nuxt's server-side rendering (SSR) and data-fetching utilities let you query MongoDB on the server before rendering pages. This lets you hydrate your Vue components with ready-to-use data, improving perceived performance and SEO for data-driven applications.

This tutorial shows you how to build a web application by using Nuxt and Vue. The application accesses sample restaurant data, queries the data, and displays the results on a locally hosted site. The tutorial also includes instructions on connecting to a MongoDB cluster hosted on MongoDB Atlas and accessing and displaying data from your database.

Tip

If you prefer to connect to MongoDB by using the Node.js driver without Nuxt, see the Get Started with the Node.js Driver guide.

Follow the steps in this section to install the project dependencies, create an Atlas cluster, and set up the application directories.

1

To create the Quick Start application, install the following software in your development environment:

Prerequisite

Notes

Use version 20 or later.

Code editor

This tutorial uses Visual Studio Code, but you can use the editor of your choice.

Terminal app and shell

For macOS or Linux users, use Terminal or a similar app. For Windows users, use PowerShell.

2

MongoDB Atlas is a fully managed cloud database service that hosts your MongoDB deployments. If you do not have a MongoDB deployment, you can create a MongoDB cluster for free (no credit card required) by completing the MongoDB Get Started tutorial.

Tip

Ensure that the sample_restaurants dataset is loaded into your cluster. This tutorial queries data from that dataset. To learn how to load the sample_restaurants dataset into your cluster, see the MongoDB Get Started tutorial.

To connect to your MongoDB cluster, you must use a connection URI. To learn how to retrieve your connection URI, see the Add your connection string section of the MongoDB Get Started tutorial.

Tip

Save your MongoDB Atlas connection string in a secure location. You will add it to your .env file in a later step.

3

Use the official Nuxt CLI to initialize a new Nuxt project with Vue. Open your terminal and navigate to your desired project directory.

Run the initialization command:

npm create nuxt@latest

The CLI prompts you to configure your project. Use the following responses:

  1. Prompt: Need to install the following packages: create-nuxt@latest-number. Ok to proceed?

    Response: Type y and press Enter.

  2. Prompt: Which template would you like to use?

    Response: Select minimal - Minimal setup for Nuxt 4 (recommended).

  3. Prompt: Where would you like to create your project?

    Response: Press Enter to accept the default directory or type a different path.

  4. Prompt: Which package manager would you like to use?

    Response: Select npm.

  5. Prompt: Initialize git repository?

    Response: Select Yes or No based on your preference.

  6. Prompt: Would you like to install any of the official modules?

    Response: Select No.

4

Navigate to your project directory and install the MongoDB driver, Lucide icons, Tailwind, and related dependencies by running the following commands:

npm install mongodb lucide-vue-next
npm install -D @types/node @nuxtjs/tailwindcss

You can safely ignore any warnings about deprecated packages from the Tailwind installation.

After setting up the project structure, follow the steps in this section to set up the back end.

1

Create a .env file at the root of your project to securely store your connection URI by running the following command in your project root:

touch .env

Open the .env file and add your connection URI:

MONGODB_URI=<Your Connection URI>

Note

Replace <Your Connection URI> with the connection URI you received from MongoDB Atlas.

2

Open the nuxt.config.ts file in your project root and replace its contents with the following code to load environment variables and configure the Tailwind CSS module:

nuxt.config.ts
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
compatibilityDate: '2025-07-15',
devtools: { enabled: true },
future:{
compatibilityVersion: 4,
},
runtimeConfig:{
// Server-side only config. Not exposed to the client
mongoURI: process.env.MONGODB_URI,
},
css:['@/assets/css/main.css'],
modules: ['@nuxtjs/tailwindcss'],
})

The nuxt.config.ts file loads environment variables from the .env file and configures Tailwind support for styling your Vue components.

3

Run the following commands in your project root to create a utility file to hold the MongoDB connection logic:

mkdir -p server/utils
touch server/utils/mongodb.ts

Open the server/utils/mongodb.ts file and add the following code:

server/utils/mongodb.ts
import { MongoClient, type Db } from "mongodb";
let cachedClient: MongoClient | null = null;
let cachedDb: Db | null = null;
export async function connectToDatabase(){
if (cachedClient && cachedDb){
return { client: cachedClient, db: cachedDb };
}
const config = useRuntimeConfig();
const uri = config.mongoURI;
if (!uri){
throw new Error("Please define the MONGO_URI environment variable inside .env");
}
const client = new MongoClient(uri);
await client.connect();
const db = client.db("sample_restaurants");
cachedClient = client;
cachedDb = db;
console.log("Connected to MongoDB Atlas");
return { client, db };
}

This code creates a reusable MongoDB client that connects to your Atlas cluster by using the connection URI stored in your environment variable. The client connects to the sample_restaurants database, which contains information about restaurants around New York City.

4

Nuxt server routes handle API requests and interact with the database. Run the following commands in your project root to create a server route for fetching restaurant data:

mkdir -p server/api/restaurants
touch server/api/restaurants/index.get.ts

Open the server/api/restaurants/index.get.ts file and add the following code:

server/api/restaurants/index.get.ts
import { connectToDatabase } from "~~/server/utils/mongodb";
export default defineEventHandler(async () =>{
const { db } = await connectToDatabase();
const collection = db.collection("restaurants");
const restaurants = await collection
.find({})
.limit(50)
.project({ name: 1, borough: 1, cuisine: 1, grades: 1})
.toArray();
return restaurants.map((doc) => ({
...doc,
_id: doc._id.toString(),
}))
})

This file defines a server route that queries the restaurants collection. It retrieves the first 50 restaurant documents and returns them as a JSON response.

5

Run the following command in your project root to create the new route file:

touch server/api/restaurants/browse.get.ts

Open the server/api/restaurants/browse.get.ts file and add the following code:

server/api/restaurants/browse.get.ts
import { connectToDatabase } from '~~/server/utils/mongodb'
export default defineEventHandler(async () => {
const { db } = await connectToDatabase();
const collection = db.collection("restaurants");
// Filter: Queens borough + name contains "Moon" (case-insensitive)
const query = {
borough: 'Queens',
name: { $regex: 'Moon', $options: 'i' },
}
const restaurants = await collection
.find(query)
.project({ name: 1, borough: 1, cuisine: 1, grades: 1 })
.toArray()
return restaurants.map((doc) => ({
...doc,
_id: doc._id.toString(),
}))
})

This file defines a server route that queries the sample_restaurants database for restaurants in Queens whose names contain the word Moon and returns the matching documents as a JSON response.

After setting up the back end, follow the steps in this section to set up the presentation layer for your application.

1

Run the following command in your project root to create a main CSS file for global styles:

mkdir -p app/assets/css
touch app/assets/css/main.css

Open the app/assets/css/main.css file and add the following code:

app/assets/css/main.css
@tailwind base;
@tailwind components;
@tailwind utilities;

This file imports Tailwind CSS directives that generate utility classes for styling your application. Tailwind provides a comprehensive set of pre-built classes for layout, spacing, colors, and typography without requiring custom CSS.

2

Run the following commands in your project root to create a Vue component for displaying restaurant data:

mkdir -p app/components
touch app/components/RestaurantList.vue

Open the app/components/RestaurantList.vue file and add the following code:

app/components/RestaurantList.vue
<script setup lang="ts">
import { computed } from 'vue'
import { MapPin, Utensils, ChevronRight } from 'lucide-vue-next'
// Type definitions
interface Grade {
grade?: string
score?: number
}
interface Restaurant {
_id: string
name: string
borough?: string
cuisine?: string
grades?: Grade[]
}
// Determine endpoint based on current route
const route = useRoute()
const isBrowse = computed(() => route.path === '/browse')
const endpoint = computed(() =>
isBrowse.value ? '/api/restaurants/browse' : '/api/restaurants'
)
// Fetch data using Nuxt's useFetch composable
// Automatically refetches when endpoint changes
const { data: restaurants } = await useFetch<Restaurant[]>(endpoint, {
watch: [endpoint],
default: () => [],
})
// Dynamic page content
const pageTitle = computed(() =>
isBrowse.value ? 'Hidden Gems in Queens' : 'All Restaurants'
)
const pageSubtitle = computed(() =>
isBrowse.value
? "Filtered by: Borough 'Queens' & Name contains 'Moon'"
: 'Explore our complete directory of local favorites'
)
// Deterministic gradient based on name length
const getGradient = (name: string) => {
const gradients = [
'from-orange-400 to-pink-500',
'from-blue-400 to-indigo-500',
'from-green-400 to-emerald-500',
'from-purple-400 to-fuchsia-500',
]
return gradients[name.length % gradients.length]
}
</script>
<template>
<div class="max-w-6xl mx-auto px-4 py-8">
<!-- Header -->
<div class="mb-8">
<h3 class="text-3xl font-bold text-gray-800">
{{ pageTitle }}
</h3>
<p class="text-gray-500 mt-2">
{{ pageSubtitle }}
</p>
</div>
<!-- Restaurant Grid -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<div
v-for="restaurant in restaurants"
:key="restaurant._id"
class="group bg-white rounded-2xl border border-gray-100 shadow-sm hover:shadow-xl hover:-translate-y-1 transition-all duration-300 overflow-hidden flex flex-col"
>
<!-- Gradient Header -->
<div
:class="[
'h-32 bg-gradient-to-br p-6 flex items-end relative',
getGradient(restaurant.name)
]"
>
<span class="absolute top-4 right-4 bg-white/20 backdrop-blur-md text-white text-xs font-bold px-2 py-1 rounded-full border border-white/30">
Grade {{ restaurant.grades?.[0]?.grade ?? 'N/A' }}
</span>
<h3 class="text-white text-xl font-bold drop-shadow-md line-clamp-2">
{{ restaurant.name }}
</h3>
</div>
<!-- Card Body -->
<div class="p-5 flex-1 flex flex-col">
<div class="flex items-center justify-between mb-4">
<span class="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-md text-xs font-medium bg-green-50 text-green-700 border border-green-100">
<Utensils class="w-3 h-3" />
{{ restaurant.cuisine ?? 'Unknown' }}
</span>
<div class="flex items-center text-gray-400 text-xs font-medium">
<MapPin class="w-3 h-3 mr-1" />
{{ restaurant.borough ?? 'Unknown' }}
</div>
</div>
<div class="mt-auto pt-4 border-t border-gray-50 flex items-center justify-between text-sm">
<span class="text-gray-400">
Score: {{ restaurant.grades?.[0]?.score ?? 0 }}
</span>
<button class="text-indigo-600 font-semibold group-hover:underline flex items-center gap-1">
View Menu <ChevronRight class="w-4 h-4" />
</button>
</div>
</div>
</div>
</div>
<!-- Empty State -->
<div v-if="restaurants?.length === 0" class="text-center py-12 text-gray-500">
<p>No restaurants found.</p>
<p class="text-sm mt-2">Check your MongoDB connection and ensure the sample_restaurants database is loaded.</p>
</div>
</div>
</template>

This Vue component fetches restaurant data from your Nuxt server route and displays it in a responsive grid layout. It dynamically adapts the API endpoint and page content based on the current route, showing either all restaurants or filtered results.

3

Run the following command in your project root to create a Vue component for the navigation bar:

touch app/components/Navbar.vue

Open the app/components/Navbar.vue file and add the following code:

app/components/Navbar.vue
<template>
<nav class="flex justify-between items-center mb-6 bg-white shadow-md px-6 py-4 rounded-lg">
<NuxtLink to="/">
<img
alt="MongoDB logo"
class="h-10"
src="https://d3cy9zhslanhfa.cloudfront.net/media/3800C044-6298-4575-A05D5C6B7623EE37/4B45D0EC-3482-4759-82DA37D8EA07D229/webimage-8A27671A-8A53-45DC-89D7BF8537F15A0D.png"
/>
</NuxtLink>
<div class="flex gap-4">
<NuxtLink
to="/"
class="px-4 py-2 rounded-lg text-sm font-medium transition-colors"
:class="$route.path === '/'
? 'bg-indigo-100 text-indigo-700'
: 'text-gray-600 hover:bg-gray-100'"
>
All Restaurants
</NuxtLink>
<NuxtLink
to="/browse"
class="px-4 py-2 rounded-lg text-sm font-medium transition-colors"
:class="$route.path === '/browse'
? 'bg-indigo-100 text-indigo-700'
: 'text-gray-600 hover:bg-gray-100'"
>
Browse Filtered
</NuxtLink>
</div>
</nav>
</template>

This Vue component renders the navbar above the restaurant results, and contains the All Restaurants and Filtered Restaurants links. These allow you to navigate between the routes.

4

Run the following command in your project root to create an index page for the application:

mkdir -p app/pages
touch app/pages/index.vue

Open the app/pages/index.vue file and add the following code:

app/pages/index.vue
<template>
<RestaurantList />
</template>

This file displays the results from the index.get.ts server route by using the RestaurantList and Navbar components.

5

Run the following command in your project root to create a new Vue page for displaying filtered restaurant data:

touch app/pages/browse.vue

Open the app/pages/browse.vue file and add the following code:

app/pages/browse.vue
<template>
<RestaurantList />
</template>

This file displays the results of a filtered query on the sample_restaurants database that returns all restaurants in Queens with names that contain the word Moon. It also uses the RestaurantList and Navbar components to construct the page.

6

Open the app/App.vue file and replace its contents with the following code to connect the components:

app/app.vue
<template>
<div class="w-full p-6 min-h-screen bg-gray-50">
<Navbar />
<NuxtPage />
</div>
</template>
7

Before continuing, ensure your file tree closely matches the structure below. You can safely ignore any additional project files.

your-project-directory/
├── .env <-- Environment variable file
├── app/
│ ├── app.vue <-- Main application component
│ ├── assets/
│ │ └── css/
│ │ └── main.css <-- Tailwind config
│ ├── components/
│ │ ├── Navbar.vue <-- All restaurants and filtered restaurants links
│ │ └── RestaurantList.vue <-- Container for restaurant data
│ └── pages/
│ ├── browse.vue <-- Filtered restaurants page
│ └── index.vue <-- All restaurants page
├── server/
│ ├── api/
│ │ └── restaurants/
│ │ ├── browse.get.ts <-- Filtered restaurants route
│ │ └── index.get.ts <-- All restaurants route
│ └── utils/
│ └── mongodb.ts <-- MongoDB connection utility
├── nuxt.config.ts <-- Nuxt configuration file
└── package.json

Perform the following steps to start the server and view the rendered restaurant data.

1

Run the following command in your project root to start the development server:

npm run dev

If successful, the command outputs the following information in the terminal:

Nuxt 4.2.2 (with Nitro 2.13.1, Vite 7.3.1 and Vue 3.5.27)
Local: http://localhost:3000/
Network: use --host to expose
Using default Tailwind CSS file
DevTools: press Shift + Option + D in the browser (v3.1.1)
Vite client built in 22ms
Vite server built in 19ms
Nuxt Nitro server built in 247ms
Vite server warmed up in 0ms
Vite client warmed up in 1ms
Connected to MongoDB Atlas
2

Open http://localhost:3000/ in your browser. The initial landing page displays the first 50 restaurants in the sample_restaurants database.

The landing app that displays the first 50 restaurants in the sample_restaurants database.
3

Click the Filtered Restaurants link in the navigation bar to view the filtered restaurant results. The page displays all restaurants in Queens with names that contain the word Moon.

The filtered restaurants page that displays all restaurants in Queens with names that contain the word Moon.

Congratulations on completing the Quick Start tutorial!

After you complete these steps, you have a Nuxt and Vue application that connects to your MongoDB deployment, runs queries on sample restaurant data, and renders the results in your browser.

To learn more about using MongoDB, Nuxt, and Vue, view the following resources:

Back

Meteor and Vue Integration

On this page