Skip to content

✨ Production-ready Next.js 16+ full-stack template with TypeScript, MongoDB, Tailwind CSS v4, Zod-based schemas, auto-generated OpenAPI documentation, and Orval SDK with React Query hooks, dark mode support, custom UI components with Framer Motion, form validation with Zod, and complete developer experience. πŸš€

License

Notifications You must be signed in to change notification settings

YousifAbozid/template-nextjs-ts

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

80 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸš€ Next.js Full-Stack Template

A production-ready Next.js 16+ full-stack template with TypeScript, MongoDB, Tailwind CSS v4, and comprehensive tooling. Features auto-generated OpenAPI documentation, type-safe API client, React Query integration, dark mode support, custom UI components with Framer Motion, form validation with Zod, and complete developer experience.

✨ Features

🎯 Dynamic OpenAPI System

  • Zod-Based Schemas: Use Zod for validation with built-in OpenAPI metadata
  • Functional Registry: Declarative route registration with @asteasolutions/zod-to-openapi
  • Type-Safe SDK: Auto-generated React Query hooks with Orval
  • Zero Decorators: Clean, functional patterns without class decorators
  • Live Documentation: Interactive Swagger UI at /api/docs
  • Full Type Safety: End-to-end TypeScript with Zod inference

πŸ—ƒοΈ MongoDB Integration

  • Mongoose ODM: Full MongoDB integration with schema validation
  • Connection Caching: Optimized for serverless environments
  • Middleware Pattern: withDatabase wrapper for seamless DB connections
  • Model Organization: Clean separation of concerns with organized models

🎨 Frontend Components

  • Modern UI Library: Comprehensive component system with Tailwind CSS v4
  • Dark Mode Support: Built-in theme switching with CSS custom properties
  • Framer Motion: Smooth animations and page transitions
  • Form Validation: Type-safe forms with react-hook-form + Zod
  • State Management: React Query for server state, React hooks for client state

πŸ”§ Developer Experience

  • Hot Reload: Watch mode for automatic OpenAPI regeneration
  • Type Safety: Full TypeScript support with strict type checking
  • Route-Centric: Co-located types and endpoints for better maintainability
  • Modern Stack: Next.js 16+ App Router with ES modules

πŸš€ Quick Start

Prerequisites

  • Node.js 18+
  • MongoDB Atlas account or local MongoDB instance
  • npm or yarn

Installation

# Clone the template
git clone https://github.com/YousifAbozid/template-nextjs-ts.git
cd template-nextjs-ts

# Install dependencies
npm install

# Setup environment
cp .env.example .env
# Edit .env with your MongoDB connection string

# Generate API documentation and SDK
npm run api:generate
npm run api:sdk

# Start development server
npm run dev

Environment Variables

# .env
MONGODB_URI=mongodb+srv://username:password@cluster.mongodb.net/database
API_TITLE="Your API Title"
API_VERSION="1.0.0"
API_DESCRIPTION="Your API Description"

πŸ“ Project Structure

app/
β”œβ”€β”€ api/                    # 🟒 API Routes (You modify these)
β”‚   β”œβ”€β”€ users/
β”‚   β”‚   β”œβ”€β”€ route.ts       # API endpoints (GET, POST, etc.)
β”‚   β”‚   β”œβ”€β”€ schema.ts      # Zod schemas for validation + OpenAPI
β”‚   β”‚   └── openapi.ts     # OpenAPI route registration
β”‚   β”œβ”€β”€ health/            # Health check endpoint
β”‚   β”œβ”€β”€ docs/              # Swagger UI documentation
β”‚   └── openapi.json/      # OpenAPI specification endpoint
β”œβ”€β”€ components/            # 🟒 React components
β”‚   β”œβ”€β”€ ui/                # Base UI components (Button, Card, etc.)
β”‚   └── shared/            # Shared business components
β”œβ”€β”€ context/               # 🟒 React context providers
β”‚   β”œβ”€β”€ Providers.tsx      # App-level providers
β”‚   β”œβ”€β”€ ThemeProvider.tsx  # Dark/light mode
β”‚   └── ToastContext.tsx   # Toast notifications
β”œβ”€β”€ lib/                   # Shared utilities and business logic
β”‚   β”œβ”€β”€ api/
β”‚   β”‚   β”œβ”€β”€ database/      # 🟒 Database connection utilities
β”‚   β”‚   β”œβ”€β”€ middleware/    # 🟒 Custom middleware
β”‚   β”‚   β”œβ”€β”€ models/        # 🟒 Mongoose models
β”‚   β”‚   β”œβ”€β”€ openapi/       # 🟒 OpenAPI registry and helpers
β”‚   β”‚   β”œβ”€β”€ config.ts      # 🟑 API configuration
β”‚   β”‚   └── sdk-mutator.ts # 🟑 Orval custom fetch instance
β”‚   └── network/           # 🟒 React Query configuration
β”œβ”€β”€ layout.tsx             # Root layout with providers
β”œβ”€β”€ page.tsx               # Landing page with component showcase
└── globals.css            # Global styles with CSS custom properties

Legend: 🟒 Safe to modify | 🟑 Modify carefully | πŸ”΄ Auto-generated (don't touch)

🎯 Adding New API Routes

1. Create Route Structure

mkdir app/api/products
touch app/api/products/route.ts app/api/products/schema.ts app/api/products/openapi.ts

2. Define Schemas (schema.ts)

import { z } from 'zod';
import { extendZodWithOpenApi } from '@asteasolutions/zod-to-openapi';

extendZodWithOpenApi(z);

export const CreateProductRequestSchema = z
  .object({
    name: z
      .string()
      .min(1)
      .openapi({ description: 'Product name', example: 'Widget' }),
    price: z
      .number()
      .min(0)
      .openapi({ description: 'Price in USD', example: 29.99 }),
    category: z
      .string()
      .openapi({ description: 'Product category', example: 'Electronics' }),
    description: z
      .string()
      .optional()
      .openapi({ description: 'Product description' })
  })
  .openapi('CreateProductRequest');

export const ProductSchema = z
  .object({
    _id: z.string().openapi({ example: '507f1f77bcf86cd799439011' }),
    name: z.string(),
    price: z.number(),
    category: z.string(),
    description: z.string().optional(),
    createdAt: z.coerce.date(),
    updatedAt: z.coerce.date()
  })
  .openapi('Product');

export type CreateProductRequest = z.infer<typeof CreateProductRequestSchema>;
export type Product = z.infer<typeof ProductSchema>;

3. Register OpenAPI Routes (openapi.ts)

import {
  registry,
  createRouteConfig,
  createListResponse
} from '@/lib/api/openapi';
import { CreateProductRequestSchema, ProductSchema } from './schema';

// GET /api/products
registry.registerPath(
  createRouteConfig({
    method: 'get',
    path: '/api/products',
    tags: ['Products'],
    summary: 'Get all products',
    responses: {
      200: {
        description: 'List of products',
        content: {
          'application/json': {
            schema: createListResponse(ProductSchema)
          }
        }
      }
    }
  })
);

// POST /api/products
registry.registerPath(
  createRouteConfig({
    method: 'post',
    path: '/api/products',
    tags: ['Products'],
    summary: 'Create a new product',
    request: {
      body: {
        content: {
          'application/json': {
            schema: CreateProductRequestSchema
          }
        }
      }
    },
    responses: {
      201: {
        description: 'Product created successfully',
        content: {
          'application/json': {
            schema: z.object({
              success: z.literal(true),
              data: ProductSchema,
              message: z.string()
            })
          }
        }
      }
    }
  })
);

4. Create Route Handler (route.ts)

import { NextRequest, NextResponse } from 'next/server';
import { withDatabase } from '@/lib/api/middleware';
import { Product } from '@/lib/api/models';
import { CreateProductRequestSchema } from './schema';
import './openapi'; // ⚠️ CRITICAL: Import to register OpenAPI routes

export const GET = withDatabase(async () => {
  const products = await Product.find().sort({ createdAt: -1 });
  return NextResponse.json({
    success: true,
    data: products,
    count: products.length
  });
});

export const POST = withDatabase(async (req: NextRequest) => {
  try {
    const body = await req.json();

    // Validate with Zod
    const validationResult = CreateProductRequestSchema.safeParse(body);
    if (!validationResult.success) {
      return NextResponse.json(
        { success: false, error: validationResult.error.errors[0].message },
        { status: 400 }
      );
    }

    const product = new Product(validationResult.data);
    const savedProduct = await product.save();

    return NextResponse.json(
      {
        success: true,
        data: savedProduct,
        message: 'Product created successfully'
      },
      { status: 201 }
    );
  } catch (error) {
    console.error('Create product error:', error);
    return NextResponse.json(
      { success: false, error: 'Failed to create product' },
      { status: 500 }
    );
  }
});

5. Create Database Model (app/lib/api/models/Product.ts)

import mongoose, { Schema, Document } from 'mongoose';

export interface IProduct extends Document {
  name: string;
  price: number;
  category: string;
  description?: string;
  createdAt: Date;
  updatedAt: Date;
}

const ProductSchema: Schema<IProduct> = new Schema(
  {
    name: { type: String, required: true, trim: true },
    price: { type: Number, required: true, min: 0 },
    category: { type: String, required: true, trim: true },
    description: { type: String, trim: true }
  },
  { timestamps: true }
);

export const Product =
  mongoose.models.Product || mongoose.model<IProduct>('Product', ProductSchema);

6. Generate Documentation and SDK

npm run api:generate  # Generate OpenAPI spec
npm run api:sdk       # Generate React Query SDK

Result: Your new route automatically appears in:

  • OpenAPI spec at /api/openapi.json
  • Interactive docs at /api/docs
  • Generated React Query hooks in sdk/index.ts

🎨 Frontend Development

Using UI Components

import { Button } from '@/components/ui/Button';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/Card';
import { useToast } from '@/context/ToastContext';

export function ExampleComponent() {
  const { showToast } = useToast();

  return (
    <Card>
      <CardHeader>
        <CardTitle>Example Component</CardTitle>
      </CardHeader>
      <CardContent>
        <Button
          onClick={() => showToast('Success!', 'success')}
          variant="default"
        >
          Click me
        </Button>
      </CardContent>
    </Card>
  );
}

API Integration with Generated SDK

import { useGetApiUsers, usePostApiUsers } from 'sdk';

// Using auto-generated React Query hooks
export function UsersList() {
  const { data: response, isLoading } = useGetApiUsers();
  const createUser = usePostApiUsers();

  if (isLoading) return <div>Loading...</div>;

  return (
    <div>
      {response?.data?.map(user => (
        <div key={user._id}>{user.name}</div>
      ))}
      <button
        onClick={() => createUser.mutate({ data: { name: 'John', email: 'john@example.com' } })}
      >
        Add User
      </button>
    </div>
  );
}

πŸ”§ Available Scripts

npm run dev              # Start development server
npm run build            # Build for production (includes OpenAPI + SDK generation)
npm run start            # Start production server
npm run api:generate     # Generate OpenAPI spec from route definitions
npm run api:sdk          # Generate React Query SDK with Orval
npm run api:watch        # Watch mode: OpenAPI + SDK auto-regeneration
npm run api:dev          # Start dev server + watch OpenAPI + SDK
npm run type-check       # TypeScript type checking
npm run lint             # ESLint
npm run lint:fix         # ESLint with auto-fix
npm run format           # Prettier formatting
npm run test             # Run all checks (format, lint, type-check)

πŸ“– API Documentation

Once running, access your documentation and app:

πŸ”„ Auto-Generation Workflow

The system uses a two-step generation process:

  1. OpenAPI Generation: Imports all openapi.ts files to build the OpenAPI spec
  2. SDK Generation: Uses Orval to generate React Query hooks from the spec

How It Works:

  1. Define Zod schemas in schema.ts with .openapi() metadata
  2. Register routes in openapi.ts using registry.registerPath()
  3. Import './openapi' in route.ts to trigger registration
  4. Run npm run api:generate to build openapi.json
  5. Run npm run api:sdk to generate React Query hooks in sdk/

Generated Files (Auto-updated)

  • openapi.json - OpenAPI 3.0 specification (project root)
  • sdk/index.ts - Type-safe React Query hooks and types

πŸ› οΈ Tech Stack

Frontend

Backend

Developer Tools

πŸš€ Deployment

Vercel (Recommended)

# Install Vercel CLI
npm i -g vercel

# Deploy
vercel --prod

Docker

# Build image
docker build -t nextjs-backend .

# Run container
docker run -p 3000:3000 nextjs-backend

Manual Deployment

# Build for production
npm run build

# Start production server
npm start

πŸ“š Additional Resources

🀝 Contributing

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

πŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.

πŸ™ Acknowledgments


Built with ❀️ by Yousif Abozid

About

✨ Production-ready Next.js 16+ full-stack template with TypeScript, MongoDB, Tailwind CSS v4, Zod-based schemas, auto-generated OpenAPI documentation, and Orval SDK with React Query hooks, dark mode support, custom UI components with Framer Motion, form validation with Zod, and complete developer experience. πŸš€

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •