≡ Menu

In the modern web ecosystem, Next.js has emerged as the go-to framework for developers who want to build fast, SEO-friendly, and scalable applications.

Whether you are a seasoned developer or just starting out, building a blog is a rite of passage that touches on all the core pillars of web development: data fetching, dynamic routing, and server-side logic.

In this tutorial, we’ll walk through creating a full-stack blog from scratch using Next.js 15, Prisma for database management, and Tailwind CSS for a sleek, modern design.

By the end of this guide, you will have a working application where you can view a list of posts, read individual articles, and even publish new content directly to your database.


1. Project Setup

Initialize your project with the latest features.

npx create-next-app@latest my-awesome-blog

Selection Guide:

  • TypeScript: Yes (Better for catching bugs in your post data).

  • Tailwind CSS: Yes (For styling).

  • App Router: Yes (The modern standard).


2. The Data Layer (Prisma)

Prisma acts as the bridge between your code and your database (PostgreSQL, MySQL, or SQLite).

  1. Install Prisma:

    npm install prisma @prisma/client
    npx prisma init
    
  2. Define the Schema: Open prisma/schema.prisma and add your blog model:

    model Post {
      id        String   @id @default(cuid())
      title     String
      slug      String   @unique
      content   String
      excerpt   String
      createdAt DateTime @default(now())
    }
    
  3. Push the Schema: Update your database with npx prisma db push.


3. Building the Blog Feed (Server Component)

In Next.js, we fetch data directly inside our components. This is fast because the data stays on the server and only the HTML is sent to the user.

File: src/app/page.tsx

import Link from 'next/link';
import { prisma } from '@/lib/prisma';

export default async function BlogHome() {
  const posts = await prisma.post.findMany({
    orderBy: { createdAt: 'desc' },
  });

  return (
    <main className="max-w-4xl mx-auto p-8">
      <h1 className="text-5xl font-extrabold mb-12">The Dev Blog</h1>
      <div className="space-y-8">
        {posts.map((post) => (
          <article key={post.id} className="group border-b pb-8">
            <Link href={`/blog/${post.slug}`}>
              <h2 className="text-2xl font-bold group-hover:text-blue-500 transition">
                {post.title}
              </h2>
              <p className="text-gray-600 mt-2">{post.excerpt}</p>
              <span className="text-sm text-gray-400">{post.createdAt.toDateString()}</span>
            </Link>
          </article>
        ))}
      </div>
    </main>
  );
}

4. The Individual Post Page (Dynamic Routing)

We use the folder syntax [slug] to handle any blog post URL (e.g., /blog/my-first-post).

File: src/app/blog/[slug]/page.tsx

import { prisma } from '@/lib/prisma';
import { notFound } from 'next/navigation';

export default async function PostPage({ params }: { params: { slug: string } }) {
  const post = await prisma.post.findUnique({
    where: { slug: params.slug },
  });

  if (!post) notFound();

  return (
    <article className="max-w-3xl mx-auto py-16 px-4">
      <h1 className="text-4xl font-bold mb-4">{post.title}</h1>
      <p className="text-gray-400 mb-8 italic">{post.createdAt.toDateString()}</p>
      {/* The 'prose' class handles all the markdown-style formatting */}
      <div className="prose lg:prose-xl">
        {post.content}
      </div>
    </article>
  );
}

5. Adding Content (Server Actions)

Instead of building a separate API, we use Server Actions to handle form submissions securely on the server.

File: src/app/admin/new/page.tsx

import { prisma } from '@/lib/prisma';
import { redirect } from 'next/navigation';

export default function NewPost() {
  async function handleSubmit(formData: FormData) {
    'use server';

    const title = formData.get('title') as string;
    const content = formData.get('content') as string;
    const slug = title.toLowerCase().replace(/ /g, '-');

    await prisma.post.create({
      data: {
        title,
        content,
        slug,
        excerpt: content.substring(0, 150) + '...',
      },
    });

    redirect('/');
  }

  return (
    <form action={handleSubmit} className="flex flex-col gap-4 max-w-xl mx-auto p-10">
      <input name="title" placeholder="Post Title" className="border p-2 rounded" required />
      <textarea name="content" placeholder="Write your post here..." className="border p-2 h-40 rounded" required />
      <button type="submit" className="bg-blue-600 text-white p-2 rounded hover:bg-blue-700">
        Publish Post
      </button>
    </form>
  );
}

Conclusion

Congratulations! You’ve just built a fully functional, full-stack blog application.

By leveraging Next.js Server Components, you’ve ensured that your blog is optimized for search engines and loads lightning-fast for your readers.

You’ve also learned how to use Server Actions to simplify your data mutations and Prisma to keep your database interactions clean and type-safe.

From here, you could take this project even further by adding user authentication with NextAuth, integrating a Markdown editor for more complex formatting, or implementing a comment section.

The foundation you’ve built today is the perfect springboard for any advanced web project.

Happy coding!

Useful links below:

Let me & my team build you a money making website/blog for your business https://bit.ly/tnrwebsite_service

Get Bluehost hosting for as little as $1.99/month (save 75%)…https://bit.ly/3C1fZd2

Best email marketing automation solution on the market! http://www.aweber.com/?373860

Build high converting sales funnels with a few simple clicks of your mouse! https://bit.ly/484YV29

Join my Patreon for one-on-one coaching and help with your coding…https://www.patreon.com/c/TyronneRatcliff

Buy me a coffee ☕️https://buymeacoffee.com/tyronneratcliff

{ 0 comments }

Welcome to the world of NestJS!

If you’re looking to build efficient, scalable, and maintainable server-side applications, you’ve come to the right place.

NestJS is a progressive Node.js framework heavily inspired by Angular, providing an out-of-the-box architecture for enterprise-grade backends.

In this tutorial, we’ll walk through the basics of setting up a NestJS project and creating your first API endpoint.

Why Choose NestJS?

  • TypeScript by Default: Brings static typing and improved code quality.

  • Modular Architecture: Keeps your application organized as it grows.

  • Powerful CLI: Speeds up development by generating boilerplate code.

  • Scalability: Designed specifically for large-scale applications.


Prerequisites

Make sure you have the following installed:

  1. Node.js (LTS version recommended)

  2. npm (comes with Node)


Step 1: Install the NestJS CLI

The CLI allows you to generate modules, controllers, and services with simple commands. Open your terminal and run:

npm i -g @nestjs/cli

Step 2: Create a New Project

Navigate to your desired folder and run:

nest new my-nest-app

Select npm as your package manager. Once finished, move into the project:

cd my-nest-app

Step 3: Run the Application

To start the development server with “watch mode” (which restarts whenever you save a file), run:

npm run start:dev

Your app is now running at http://localhost:3000. You should see “Hello World!” in your browser.


Step 4: Building a “To-Do” API

Let’s build a functional API. We will generate a Module, a Controller (to handle requests), and a Service (to handle logic).

1. Generate the Files

Run these commands in your terminal:

nest g module todos
nest g controller todos
nest g service todos

2. Implement the Service

Open src/todos/todos.service.ts and replace the code with this logic to manage a simple array:

import { Injectable } from '@nestjs/common';

interface Todo {
  id: number;
  title: string;
  completed: boolean;
}

@Injectable()
export class TodosService {
  private todos: Todo[] = [];
  private nextId = 1;

  findAll(): Todo[] {
    return this.todos;
  }

  create(title: string): Todo {
    const newTodo = { id: this.nextId++, title, completed: false };
    this.todos.push(newTodo);
    return newTodo;
  }

  findOne(id: number): Todo {
    return this.todos.find(todo => todo.id === id);
  }
}

3. Implement the Controller

Open src/todos/todos.controller.ts and update it to handle HTTP requests:

import { Controller, Get, Post, Body, Param } from '@nestjs/common';
import { TodosService } from './todos.service';

@Controller('todos')
export class TodosController {
  constructor(private readonly todosService: TodosService) {}

  @Get()
  getAll() {
    return this.todosService.findAll();
  }

  @Post()
  create(@Body('title') title: string) {
    return this.todosService.create(title);
  }

  @Get(':id')
  getOne(@Param('id') id: string) {
    return this.todosService.findOne(+id);
  }
}

Step 5: Test Your API

You can use a tool like Postman or Insomnia to test your new endpoints:

  1. POST http://localhost:3000/todos with a JSON body: {"title": "Learn NestJS"}.

  2. GET http://localhost:3000/todos to see your list.

Conclusion

You’ve just built a working REST API with NestJS!

From here, you can explore database integration with TypeORM, add Validation Pipes, or implement JWT Authentication.

Useful links below:

Let me & my team build you a money making website/blog for your business https://bit.ly/tnrwebsite_service

Get Bluehost hosting for as little as $1.99/month (save 75%)…https://bit.ly/3C1fZd2

Best email marketing automation solution on the market! http://www.aweber.com/?373860

Build high converting sales funnels with a few simple clicks of your mouse! https://bit.ly/484YV29

Join my Patreon for one-on-one coaching and help with your coding…https://www.patreon.com/c/TyronneRatcliff

Buy me a coffee ☕️https://buymeacoffee.com/tyronneratcliff

{ 0 comments }