Back to Blog
Tutorial
11 min read

Build a Blog with Next.js and MDX from Scratch

Build a Blog with Next.js and MDX from Scratch

File-based content, zero database, full control. A complete walkthrough of building a statically-generated blog with Next.js, MDX, and gray-matter.

When building a robust web application, maintaining a clean architecture is essential. One of the most important aspects is how you structure your folders and manage your secrets. Let's dive into the optimal setup.

Clean Architecture Setup

A well-organized codebase reduces cognitive load. Here is the folder structure I use for almost every production Next.js or Node application:

Folder Structure

src/
├── config/
│   ├── database.ts
│   ├── logger.ts
│   └── constants.ts
├── models/
│   ├── User.ts
│   ├── Category.ts
│   ├── Gear.ts
│   ├── RentalOrder.ts
│   ├── Payment.ts
│   └── Review.ts
├── routes/
│   ├── auth.routes.ts
│   ├── gear.routes.ts
│   ├── rental.routes.ts
│   ├── payment.routes.ts
│   ├── review.routes.ts
│   ├── provider.routes.ts
│   └── admin.routes.ts
├── controllers/
│   ├── auth.controller.ts
│   ├── gear.controller.ts
│   └── ...
├── services/
│   ├── auth.service.ts
│   ├── payment.service.ts
│   └── ...
├── middlewares/
│   ├── auth.middleware.ts
│   ├── role.middleware.ts
│   ├── validation.middleware.ts
│   └── error.middleware.ts
├── utils/
│   ├── bcrypt.util.ts
│   ├── jwt.util.ts
│   └── validation.util.ts
├── types/
│   └── index.ts
├── app.ts
└── server.ts

Environment Configuration

Handling your environment variables carefully ensures that your secrets are never exposed to the client side inadvertently. Here is the typical .env file you will need for this setup:

Environment Variables (.env)

# Server
PORT=5000
NODE_ENV=development

# Database
DATABASE_URL="postgresql://user:password@localhost:5432/gearup"

# JWT
JWT_SECRET="your-super-secret-jwt-key"
JWT_EXPIRES_IN="7d"

# Stripe
STRIPE_SECRET_KEY="sk_test_..."
STRIPE_WEBHOOK_SECRET="whsec_..."

# SSLCommerz
SSL_STORE_ID="test"
SSL_STORE_PASSWORD="test"

# Cloudinary (for images)
CLOUDINARY_CLOUD_NAME="your-cloud-name"
CLOUDINARY_API_KEY="your-api-key"
CLOUDINARY_API_SECRET="your-api-secret"

Remember to add .env to your .gitignore and provide a .env.example for your fellow developers. Keep shipping!