We use cookies to enhance your experience on the site
CodeWorlds

Overview of Database Options for Next.js (SQL, NoSQL)

Choosing the right database is one of the key decisions when building a Next.js application. In this module, we will explore the most popular database options along with their advantages and disadvantages in the context of Next.js applications.

Types of Databases

Databases can be divided into two main categories: SQL (relational) and NoSQL (non-relational). Each has its own characteristic features that determine which projects they are best suited for.

SQL Databases (Relational)

Relational databases organize data in tables with a predefined structure and relationships between them. Examples of popular SQL databases include:

  1. PostgreSQL

    • Free, open-source
    • Advanced features (e.g., JSON types, full-text search)
    • Excellent performance and scalability
    • Excellent data integrity
  2. MySQL/MariaDB

    • Widely used, free database
    • Good performance for read operations
    • Lower resource requirements than PostgreSQL
  3. SQLite

    • Lightweight database (file) without a separate server
    • Ideal for prototyping and testing
    • Not suitable for production applications with high traffic

NoSQL Databases (Non-relational)

NoSQL databases offer greater flexibility in data structure and typically better horizontal scalability. The main types are:

  1. Dokumentowe (MongoDB, Firebase Firestore)

    • Store data as documents (JSON/BSON objects)
    • Flexible structure without a rigid schema
    • Great for rapid prototyping and unstructured data
  2. Key-Value (Redis, DynamoDB)

    • Store simple key-value pairs
    • Extremely fast read/write operations
    • Often used as cache
  3. Wide-Column (Cassandra, HBase)

    • Store data in columns instead of rows
    • High scalability and performance for huge datasets
  4. Grafowe (Neo4j, Amazon Neptune)

    • Specialize in relationships between data
    • Ideal for complex relationships and connection analysis

Popular Database Options for Next.js

1. PostgreSQL + Prisma

PostgreSQL combined with the Prisma ORM is one of the most popular options for Next.js applications:

1// Example Prisma model for 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}

Advantages:

  • Strong data integrity through relationships
  • High performance and reliability
  • Good scalability
  • Excellent TypeScript support thanks to Prisma

Disadvantages:

  • Requires a database server
  • Rigid data structure (schema)

2. MongoDB + Mongoose

MongoDB is a popular non-relational document database, often used with Next.js:

1// Example Mongoose model for MongoDB
2import mongoose from 'mongoose';
3
4const UserSchema = new mongoose.Schema({
5  name: {
6    type: String,
7    required: [true, 'Please provide a name'],
8    maxlength: [50, 'Name cannot be longer than 50 characters']
9  },
10  email: {
11    type: String,
12    required: [true, 'Please provide an email'],
13    unique: true,
14    match: [
15      /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/,
16      'Please provide a valid email address'
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:

  • Flexible data structure
  • Easy horizontal scalability
  • Data format similar to JSON
  • Good TypeScript support

Wady:

  • Weaker data integrity than in SQL databases
  • More difficult complex queries

3. Serverless Databases (PlanetScale, Neon, Supabase)

Serverless databases are a new trend that perfectly fits the Next.js architecture:

1// Example of using PlanetScale with Next.js
2import { PrismaClient } from '@prisma/client';
3
4const prisma = new PrismaClient();
5
6// In API route or 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('Failed to fetch users');
16  }
17}

Popular options:

  • PlanetScale - MySQL-compatible serverless database
  • Neon - PostgreSQL in a serverless model
  • Supabase - backend-as-a-service platform with PostgreSQL
  • Firebase - comprehensive solution with Firestore as a document database

Zalety:

  • No need to manage infrastructure
  • Pay-per-use (you only pay for what you use)
  • Automatic scaling
  • Often free tier for small projects

Wady:

  • Potentially higher costs at large scale
  • Less control over infrastructure
  • Dependency on an external provider

4. Edge Databases (Cloudflare D1, Fauna)

A novelty in the Next.js ecosystem are databases running on the edge:

1// Example of using Cloudflare D1 with Next.js
2// In API Route (Pages Router) or 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:

  • Minimal latency for global users
  • Integration with edge functions
  • Often lower costs

Wady:

  • Limited capabilities compared to traditional databases
  • Relatively new technology

Choosing a Database for Next.js - Factors to Consider

1. Data Structure

  • If you have a clearly defined data structure with relationships → PostgreSQL or MySQL
  • If the data structure is fluid and may change → MongoDB or Firestore

2. Scalability

  • For applications that need to handle millions of users → Serverless options (PlanetScale, Neon)
  • For applications with predictable traffic → Traditional solutions like PostgreSQL

3. Team and Experience

  • If the team has SQL experience → PostgreSQL/MySQL
  • If the team prefers a JavaScript-first approach → MongoDB

4. Hosting and Deployment

  • If you use Vercel for Next.js hosting → Integrates well with PlanetScale, Neon, Supabase
  • If you use Cloudflare Pages → Cloudflare D1 or Cloudflare KV

Integration Examples in Next.js

Example 1: PostgreSQL + Prisma in Next.js

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  // Preventing multiple instances in dev mode
10  if (!global.prisma) {
11    global.prisma = new PrismaClient();
12  }
13  prisma = global.prisma;
14}
15
16export default prisma;
17
18// In 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>Users</h1>
32      <ul>
33        {users.map((user) => (
34          <li key={user.id}>
35            {user.name} - {user.posts.length} posts
36          </li>
37        ))}
38      </ul>
39    </div>
40  );
41}

Example 2: MongoDB in Next.js (Pages Router)

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('Define MONGODB_URI in .env.local file');
9}
10
11if (!MONGODB_DB) {
12  throw new Error('Define MONGODB_DB in .env.local file');
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// In 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}

Summary

When choosing a database for Next.js, it is worth following these guidelines:

  1. Applications with clearly defined relationships: PostgreSQL + Prisma
  2. Rapid prototyping and flexible data structures: MongoDB or Firestore
  3. Serverless applications: PlanetScale, Neon, or Supabase
  4. Global applications with low latency: Edge Databases like Cloudflare D1 or FaunaDB

Regardless of the choice, Next.js offers excellent integration mechanisms with all popular databases. In the next module, we will learn how to configure an ORM for the chosen solution to further streamline working with the database.

Go to CodeWorlds