# Set-up MongoDB in NextJS

{% hint style="success" %}
Deoarece scopul nostru este configurarea și utilizarea tehnologiilor cloud, nu învățarea unor noi limbaje de programare, majoritatea codului "boilerplate" și funcțiilor utile vor fi puse la dispoziție de mine și le puteți folosi în proiecte.
{% endhint %}

## 1. Identificarea link-ului de conectare pentru proiectul Next.js

Mergem în Atlas, la secțiunea **Clusters**, iar pe clusterul nostru apăsăm butonul **Connect**.

<figure><img src="/files/IpxEu2q7PNwxCDYYJMHe" alt=""><figcaption></figcaption></figure>

Apăsam pe **Drivers**:

<figure><img src="/files/xutWDvdBxcSwuQdP9GkP" alt="" width="375"><figcaption></figcaption></figure>

Ne asigurăm că la **Driver** avem selectat **Node.js**, la versiune avem selectată ultima versiune disponibilă a Node-ului, **SRV Connection String** este debifată, după care putem copia linkul.

<figure><img src="/files/Yhy85HJCLB0cDlh01g7l" alt="" width="375"><figcaption></figcaption></figure>

## 2. Configurarea fișierului cu chei de mediu

În fișierul de .env o să salvăm cheile secrete necesare ca proiectul nostru să poată comunica cu baza de date creată în cloud la pașii anteriori.

Deschidem fișierul de .env în care o să adăugăm **connection string-ul** primit de la <mark style="color:green;">**MongoDB Cloud**</mark> și numele colecției cu care urmează să lucrăm.

```dotenv
NODE_ENV = development

NEXT_ATLAS_URI = mongodb://<USERNAME>:<PASSWORD>@ac-rl5aqbk-shard-00-00.vj1fvtg.mongodb.net:27017,ac-rl5aqbk-shard-00-01.vj1fvtg.mongodb.net:27017,ac-rl5aqbk-shard-00-02.vj1fvtg.mongodb.net:27017/?ssl=true&replicaSet=atlas-dbd1op-shard-0&authSource=admin&appName=Cluster0
NEXT_ATLAS_DATABASE = CloudComputing
```

{% hint style="info" %}
**DNS (Domain Name System)** este mecanismul prin care un nume de domeniu este transformat în informații necesare conectării, cum ar fi adrese IP sau locația unui anumit serviciu în rețea. DNS nu conține doar adrese IP, ci mai multe tipuri de înregistrări, fiecare având un rol diferit.

**SRV (Service Record)** este un tip de înregistrare DNS folosit pentru descoperirea automată a instanțelor unui serviciu. În cazul unui URI de forma `mongodb+srv://`, driverul MongoDB interoghează DNS pentru a obține automat host-urile și porturile nodurilor clusterului.

Un **SRV** spune **unde este un serviciu, pe ce port rulează și ce prioritate are serverul respectiv**.
{% endhint %}

<figure><img src="/files/aFLJatlmX366xeZkwooc" alt="" width="228"><figcaption><p>Common DNS Record Types</p></figcaption></figure>

{% hint style="warning" %}
Atunci când actualizăm o variabilă de mediu, este recomandat să repornim serverul pentru a ne asigura că proiectul o să știe de modificările pe care le-am efectuat în cadrul fișierului de **.env**
{% endhint %}

## 3. Instalarea dependințelor

Pentru a putea lucra cu <mark style="color:green;">**MongoDB Cloud**</mark>, suntem nevoiți să adăugăm o noua librărie în proiectul nostru.

Ne mutăm în terminal și rulăm comanda:

```git
npm install mongodb
```

Dacă totul a mers în regulă, în cadrul fișierului package.json ar trebui să putem vedea librăria abia instalată în lista de dependențe.

<figure><img src="/files/kqW0l9lhyJKVVqyopNLN" alt="" width="191"><figcaption></figcaption></figure>

## 4. Crearea fișierelor de configurare

În cadrul proiectului, creăm un nou folder pe care îl numim **lib** și unde o să adăugăm fișiere de configurare pentru fiecare librărie sau serviciu pe care urmează să le folosim.

În cadrul folderului lib, o să adăugăm un fișier care se numește mongodb.js și care o să realizeze conexiunea între aplicația noastră și baza de date:

```javascript
// lib/mongodb.js

import { MongoClient, } from 'mongodb';

const uri = process.env.NEXT_ATLAS_URI;

let mongoClient = null;
let database = null;

if (!process.env.NEXT_ATLAS_URI) {
    throw new Error('Please add your Mongo URI to .env');
}

export async function connectToDatabase() {
    try {
        if (mongoClient && database) {
            return { mongoClient, database, };
        }
        if (process.env.NODE_ENV === 'development') {
            if (!global._mongoClient) {
                mongoClient = await (new MongoClient(uri)).connect();
                global._mongoClient = mongoClient;
            } else {
                mongoClient = global._mongoClient;
            }
        } else {
            mongoClient = await (new MongoClient(uri)).connect();
        }
        database = await mongoClient.db(process.env.NEXT_ATLAS_DATABASE);
        return { mongoClient, database, };
    } catch (e) {
        console.error(e);
    }
}

export async function getCollection(name) {
    const { database } = await connectToDatabase();
    return database.collection(name);
}

```

{% hint style="info" %}
`global._mongoClient` este folosit în principal ca soluție pentru problema specifică din **development**: hot reload-ul din Next.js poate reexecuta modulele și poate reseta variabilele locale, dar obiectul `global` rămâne mai stabil între reload-uri. Astfel evităm să creăm multe conexiuni MongoDB în timpul dezvoltării.
{% endhint %}

## 5. Configurarea API-ului NextJS

Următorul pas este dat de crearea **metodelor CRUD** în ceea ce privește manipularea datelor din cadrul **colecției de records**, așadar ne mutăm în cadrul folderului **api** și o să adăugăm un nou folder numit **records**.

<figure><img src="/files/ug7m4W0f1Ffe3inQoTze" alt=""><figcaption></figcaption></figure>

Structura e **app/api/records/route.js** pentru operațiile pe colecția întreagă (GET, POST) și **app/api/records/\[id]/route.js** pentru operațiile pe un singur document (GET, PUT, DELETE), unde **\[id]** e segmentul dinamic care devine parametru.

Separarea există pentru că Next.js rutează după căile din URL: **/api/records** fără id nu poate sta în același fișier cu **/api/records/:id** (URL-uri diferite = fișiere&#x20;diferite), iar convenția **\[param]** e singurul mod prin care Next recunoaște că partea aceea din URL e variabilă și o pasează în **params**.

```javascript
// /api/records/route.js

import { NextResponse } from 'next/server';
import { getCollection } from '@/lib/mongodb';

export async function GET() {
    const records = await getCollection('records');
    const all = await records.find({}).toArray();
    return NextResponse.json(all);
}

export async function POST(request) {
    const body = await request.json();
    const records = await getCollection('records');
    const { insertedId } = await records.insertOne(body);
    return NextResponse.json({ _id: insertedId, ...body }, { status: 201 });
}
```

```javascript
// /api/records/[id]/route.js

import { NextResponse } from 'next/server';
import { ObjectId } from 'mongodb';
import { getCollection } from '@/lib/mongodb';

function toObjectId(id) {
    if (!ObjectId.isValid(id)) return null;
    return new ObjectId(id);
}

async function resolveId(params) {
    const { id } = await params;
    const _id = toObjectId(id);
    if (!_id) return { _id: null, error: NextResponse.json({ error: 'Invalid id' }, { status: 400 }) };
    return { _id, error: null };
}

export async function GET(request, { params }) {
    const { _id, error } = await resolveId(params);
    if (error) return error;

    const records = await getCollection('records');
    const doc = await records.findOne({ _id });
    if (!doc) return NextResponse.json({ error: 'Not found' }, { status: 404 });
    return NextResponse.json(doc);
}

export async function PUT(request, { params }) {
    const { _id, error } = await resolveId(params);
    if (error) return error;

    const body = await request.json();
    delete body._id;

    const records = await getCollection('records');
    const updated = await records.findOneAndUpdate(
        { _id },
        { $set: body },
        { returnDocument: 'after' }
    );
    if (!updated) return NextResponse.json({ error: 'Not found' }, { status: 404 });
    return NextResponse.json(updated);
}

export async function DELETE(request, { params }) {
    const { _id, error } = await resolveId(params);
    if (error) return error;

    const records = await getCollection('records');
    const { deletedCount } = await records.deleteOne({ _id });
    if (deletedCount === 0) return NextResponse.json({ error: 'Not found' }, { status: 404 });
    return NextResponse.json({ deleted: _id.toString() });
}
```

{% hint style="info" %}
**Informații utile**

**`ObjectId` + `toObjectId`** — `ObjectId` e tipul binar (**12 bytes**, afișat ca **24 hex chars**) pe care MongoDB îl folosește implicit pentru `_id`. E generat astfel încât să fie unic fără coordonare între servere și sortabil după timpul creării. `toObjectId` întâi verifică cu `ObjectId.isValid(id)` că string-ul din URL e format valid, apoi îl convertește cu `new ObjectId(id)`. Fără validare, un `id` invalid ar arunca exception și ai returna `500` în loc de `400`.

**`NextResponse`** — o clasă din `next/server` care extinde `Response` web standard. Metoda `NextResponse.json(data, { status })`  face trei lucruri: serializează obiectul în JSON, setează `Content-Type: application/json` și aplică status code-ul cerut. Alternativ, putem folosi `new Response(JSON.stringify(...))`, dar ar fi mai verbos și am pierde helper-ele pentru cookies/redirect.

**`findOneAndUpdate(filter, update, options)`** — primește 3 argumente: `filter` (ce document caut, ex. `{ _id }`), `update` cu operatori Mongo (folosim `$set` ca să modificăm doar câmpurile trimise, fără să ștergem restul) și `options`. Singura opțiune care contează aici e `returnDocument: 'after'` — îi zice să returneze documentul după modificare, nu înainte. Așa putem trimite clientului direct versiunea nouă. Returnează `null` dacă nu a găsit match → `404`.

`delete body._id` previne ca clientul să încerce să rescrie `_id`-ul (Mongo blochează asta oricum, dar e curat să-l scoatem din start). `{ _id }` e shorthand pentru `{ _id: _id }`. `[id]` În numele folderului e convenția Next pentru un segment dinamic în URL.
{% endhint %}

## 6. Testarea API-urilor

Pentru testarea API-urilor, o să ne folosim de aplicația <mark style="color:orange;">**Postman**</mark>.

{% file src="/files/Wrc68hUokqUORHgvZpIX" %}
Colecție Postman
{% endfile %}

{% hint style="info" %} <mark style="color:blue;">**GET**</mark>
{% endhint %}

Pentru returnarea tuturor înregistrărilor din colecția records, o să ne folosim de path-ul **/api/records** și cu metoda **GET**:

<figure><img src="/files/ZXpH0ctSw1ezebw1p7QJ" alt=""><figcaption></figcaption></figure>

{% hint style="info" %} <mark style="color:green;">**POST**</mark>
{% endhint %}

Pentru crearea de noi entry-uri, o să schimbăm metoda la **POST**, o să selectăm că dorim să trimitem ceva în **body-ul** requestului, de tip **JSON**.

După trimiterea requestului, dacă totul a funcționat cum trebuie, o să primim un răspuns cu id-ul entry-ului pe care abia l-am creat.

<figure><img src="/files/fZEKHgA6mHtXiQktOHI9" alt=""><figcaption></figcaption></figure>

{% hint style="info" %} <mark style="color:yellow;">**UPDATE**</mark>
{% endhint %}

Pentru editarea unui entry, o să schimbăm metoda la PUT, și o să trimitem obiectul pe care vrem să îl edităm, alături de id-ul obiectului.

<figure><img src="/files/Rd0guVZfqAmc7DuHPZWI" alt=""><figcaption></figcaption></figure>

{% hint style="info" %} <mark style="color:red;">**DELETE**</mark>
{% endhint %}

Pentru ștergerea uni entry, la path-ul request-ului o să adăugăm și un query parameter, selectăm să nu trimitem body și schimbăm metoda la **DELETE**.

<figure><img src="/files/wYYBUNLr7WjHCjAntVZi" alt=""><figcaption></figcaption></figure>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://lungu-mihai-adrian.gitbook.io/cloud-computing-2026-simpre/seminar-1/set-up-mongodb-in-nextjs.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
