Wybór odpowiedniej bazy danych jest jedną z kluczowych decyzji przy tworzeniu aplikacji w Next.js. W tym module poznamy najpopularniejsze opcje baz danych oraz ich zalety i wady w kontekście aplikacji Next.js.
Bazy danych możemy podzielić na dwie główne kategorie: SQL (relacyjne) i NoSQL (nierelacyjne). Każda z nich ma swoje charakterystyczne cechy, które wpływają na to, do jakich projektów najlepiej się nadają.
Bazy relacyjne organizują dane w tabelach z predefiniowaną strukturą i relacjami między nimi. Przykładami popularnych baz SQL są:
PostgreSQL
MySQL/MariaDB
SQLite
Bazy NoSQL oferują większą elastyczność struktury danych i zazwyczaj lepszą skalowalność poziomą. Główne typy to:
Dokumentowe (MongoDB, Firebase Firestore)
Key-Value (Redis, DynamoDB)
Wide-Column (Cassandra, HBase)
Grafowe (Neo4j, Amazon Neptune)
PostgreSQL w połączeniu z ORM Prisma to jedna z najpopularniejszych opcji dla aplikacji Next.js:
1// Przykład modelu Prisma dla PostgreSQL
2model User {
3 id Int @id @default(autoincrement())
4 email String @unique
5 name String?
6 posts Post[]
7 profile Profile?
8 createdAt DateTime @default(now())
9 updatedAt DateTime @updatedAt
10}
11
12model Post {
13 id Int @id @default(autoincrement())
14 title String
15 content String?
16 published Boolean @default(false)
17 author User @relation(fields: [authorId], references: [id])
18 authorId Int
19 createdAt DateTime @default(now())
20 updatedAt DateTime @updatedAt
21}
22
23model Profile {
24 id Int @id @default(autoincrement())
25 bio String
26 user User @relation(fields: [userId], references: [id])
27 userId Int @unique
28}Zalety:
Wady:
MongoDB to popularna nierelacyjna baza dokumentowa, często używana z Next.js:
1// Przykładowy model Mongoose dla MongoDB
2import mongoose from 'mongoose';
3
4const UserSchema = new mongoose.Schema({
5 name: {
6 type: String,
7 required: [true, 'Proszę podać imię'],
8 maxlength: [50, 'Imię nie może być dłuższe niż 50 znaków']
9 },
10 email: {
11 type: String,
12 required: [true, 'Proszę podać email'],
13 unique: true,
14 match: [
15 /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/,
16 'Proszę podać poprawny adres email'
17 ]
18 },
19 posts: [
20 {
21 type: mongoose.Schema.Types.ObjectId,
22 ref: 'Post'
23 }
24 ],
25 createdAt: {
26 type: Date,
27 default: Date.now
28 }
29});
30
31export default mongoose.models.User || mongoose.model('User', UserSchema);Zalety:
Wady:
Serverless databases to nowy trend idealnie pasujący do architektury Next.js:
1// Przykład użycia PlanetScale z Next.js
2import { PrismaClient } from '@prisma/client';
3
4const prisma = new PrismaClient();
5
6// W API route lub Server Component
7export async function getUsers() {
8 try {
9 const users = await prisma.user.findMany({
10 include: { posts: true }
11 });
12 return users;
13 } catch (error) {
14 console.error('Database error:', error);
15 throw new Error('Nie udało się pobrać użytkowników');
16 }
17}Popularne opcje:
Zalety:
Wady:
Nowością w ekosystemie Next.js są bazy danych działające na edge:
1// Przykład użycia Cloudflare D1 z Next.js
2// W API Route (Pages Router) lub Route Handler (App Router)
3import { D1Database } from '@cloudflare/workers-types';
4
5export const runtime = 'edge';
6
7export async function GET() {
8 const db = process.env.DB as unknown as D1Database;
9
10 const { results } = await db
11 .prepare('SELECT * FROM users WHERE status = ?')
12 .bind('active')
13 .all();
14
15 return Response.json({ users: results });
16}Zalety:
Wady:
1// lib/prisma.ts
2import { PrismaClient } from '@prisma/client';
3
4let prisma: PrismaClient;
5
6if (process.env.NODE_ENV === 'production') {
7 prisma = new PrismaClient();
8} else {
9 // Zapobieganie wielu instancjom w trybie dev
10 if (!global.prisma) {
11 global.prisma = new PrismaClient();
12 }
13 prisma = global.prisma;
14}
15
16export default prisma;
17
18// W Server Component (App Router)
19// app/users/page.tsx
20import prisma from '@/lib/prisma';
21
22export default async function UsersPage() {
23 const users = await prisma.user.findMany({
24 include: {
25 posts: true,
26 },
27 });
28
29 return (
30 <div>
31 <h1>Użytkownicy</h1>
32 <ul>
33 {users.map((user) => (
34 <li key={user.id}>
35 {user.name} - {user.posts.length} postów
36 </li>
37 ))}
38 </ul>
39 </div>
40 );
41}1// lib/mongodb.js
2import { MongoClient } from 'mongodb';
3
4const MONGODB_URI = process.env.MONGODB_URI;
5const MONGODB_DB = process.env.MONGODB_DB;
6
7if (!MONGODB_URI) {
8 throw new Error('Zdefiniuj MONGODB_URI w pliku .env.local');
9}
10
11if (!MONGODB_DB) {
12 throw new Error('Zdefiniuj MONGODB_DB w pliku .env.local');
13}
14
15let cachedClient = null;
16let cachedDb = null;
17
18export async function connectToDatabase() {
19 if (cachedClient && cachedDb) {
20 return { client: cachedClient, db: cachedDb };
21 }
22
23 const client = await MongoClient.connect(MONGODB_URI, {
24 useNewUrlParser: true,
25 useUnifiedTopology: true,
26 });
27
28 const db = await client.db(MONGODB_DB);
29
30 cachedClient = client;
31 cachedDb = db;
32
33 return { client, db };
34}
35
36// W API Route
37// pages/api/users.js
38import { connectToDatabase } from '@/lib/mongodb';
39
40export default async function handler(req, res) {
41 const { db } = await connectToDatabase();
42
43 const users = await db
44 .collection('users')
45 .find({})
46 .limit(20)
47 .toArray();
48
49 res.status(200).json(users);
50}Przy wyborze bazy danych dla Next.js warto kierować się następującymi zasadami:
Niezależnie od wyboru, Next.js oferuje świetne mechanizmy integracji z wszystkimi popularnymi bazami danych. W kolejnym module dowiemy się, jak skonfigurować ORM dla wybranego rozwiązania, aby jeszcze bardziej usprawnić pracę z bazą danych.