A modern blog application built with Next.js 14, Hasura GraphQL Engine, TailwindCSS, and PostgreSQL.
- ✅ Next.js 14 with App Router for server-side rendering and optimal performance
- ✅ Hasura GraphQL Engine for instant GraphQL APIs
- ✅ PostgreSQL database with comprehensive blog schema
- ✅ Docker Compose for easy deployment
- ✅ TailwindCSS for modern, responsive UI
- ✅ Apollo Client for GraphQL queries with client-side caching
- ✅ GraphQL Code Generator for type-safe GraphQL operations
- ✅ NextAuth.js for authentication
- ✅ Redux Toolkit for state management
- ✅ TypeScript for type safety
- ✅ Database Migrations with Hasura and Prisma
- ✅ Prisma ORM for type-safe database access
- ✅ Nix for reproducible development environment
- ✅ SEO Optimization with Next.js metadata API
hasura-blog/
├── app/ # Next.js App Router pages
├── components/ # React components
├── lib/ # Utilities and configurations
│ ├── apollo-client.ts # Apollo Client setup
│ ├── apollo-provider.tsx # Apollo Provider wrapper
│ ├── auth.ts # NextAuth configuration
│ ├── graphql/ # GraphQL queries and mutations
│ └── redux/ # Redux store and slices
├── hasura/ # Hasura configuration
│ ├── config.yaml # Hasura CLI config
│ ├── metadata/ # Hasura metadata
│ ├── migrations/ # Database migrations
│ └── seeds/ # Seed data
├── types/ # TypeScript type definitions
├── public/ # Static assets
├── docker-compose.yml # Docker Compose configuration
└── Dockerfile # Next.js Dockerfile
- Nix package manager with flakes enabled
- Docker and Docker Compose
- Node.js 20+ (for local development)
- npm or yarn
If you don't have Nix installed:
# Install Nix
curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install
# Or use the official installer
sh <(curl -L https://nixos.org/nix/install) --daemonEnable flakes in your Nix configuration if not already enabled:
mkdir -p ~/.config/nix
echo "experimental-features = nix-command flakes" >> ~/.config/nix/nix.conf# Clone the repository
git clone https://github.com/npsg02/hasura-blog.git
cd hasura-blog
# Enter Nix shell (this will download and setup all dependencies)
nix develop
# Or use direnv (recommended)
# Install direnv first: nix-env -iA nixpkgs.direnv
echo "use flake" > .envrc
direnv allowcp .env.example .envEdit .env and update the values as needed. The default values work for local development.
# Start Docker services
docker-compose -f docker-compose.dev.yml up -d
# Install npm dependencies
npm install
# Generate Prisma Client
npm run prisma:generate
# Start Next.js development server
npm run devgit clone https://github.com/npsg02/hasura-blog.git
cd hasura-blogcp .env.example .envEdit .env and update the values as needed. The default values work for local development.
docker-compose up -dThis will start:
- PostgreSQL on port 5432
- Hasura GraphQL Engine on port 8080
- Next.js application on port 3000
The migrations are automatically applied when Hasura starts. You can verify by accessing the Hasura Console:
http://localhost:8080/console
Admin Secret: adminsecret (or the value you set in .env)
To populate the database with sample data:
# Connect to the Hasura container
docker-compose exec hasura bash
# Run the seed SQL file
psql $HASURA_GRAPHQL_DATABASE_URL -f /hasura-migrations/default/1699900000001_seed_data.sqlOpen your browser and navigate to:
http://localhost:3000
npm installdocker-compose up -d postgres hasuranpm run devAfter Hasura is running with your schema:
npm run codegenThis generates TypeScript types from your GraphQL schema.
The blog includes the following tables:
- users - User accounts with roles
- posts - Blog posts with content
- categories - Post categories
- tags - Post tags
- post_tags - Many-to-many relationship between posts and tags
- comments - Post comments with threading support
Access the GraphQL playground at:
http://localhost:8080/v1/graphql
Get all published posts:
query GetPosts {
posts(where: {status: {_eq: "published"}}, order_by: {published_at: desc}) {
id
title
slug
excerpt
author {
name
}
category {
name
}
}
}Get post by slug:
query GetPost($slug: String!) {
posts(where: {slug: {_eq: $slug}}) {
id
title
content
author {
name
bio
}
}
}Authentication is handled by NextAuth.js. The configuration supports:
- Credentials-based authentication
- JWT tokens
- Role-based access control
To add more providers (Google, GitHub, etc.), update lib/auth.ts.
Redux Toolkit is configured with two slices:
- userSlice - User state management
- uiSlice - UI state (theme, sidebar, notifications)
npm run dev- Start development servernpm run build- Build for productionnpm run start- Start production servernpm run lint- Run ESLintnpm run codegen- Generate GraphQL typesnpm run db:backup- Create database backupnpm run db:backup:compress- Create compressed database backup
Prisma is integrated as an alternative migration tool and ORM. You can use it alongside Hasura:
npm run prisma:generate# Push schema changes to database (without migrations)
npm run prisma:db:push
# Pull schema from database to Prisma schema
npm run prisma:db:pull
# Open Prisma Studio (database GUI)
npm run prisma:studio# Create and apply a new migration in development
npm run prisma:migrate:dev
# Apply migrations in production
npm run prisma:migrate:deploy
# Reset database (WARNING: This will delete all data)
npm run prisma:migrate:resetThe Prisma Client is generated in lib/generated/prisma and can be imported:
import { PrismaClient } from '@/lib/generated/prisma';
const prisma = new PrismaClient();
// Example: Get all published posts
const posts = await prisma.post.findMany({
where: { status: 'published' },
include: { author: true, category: true }
});Install Hasura CLI:
curl -L https://github.com/hasura/graphql-engine/raw/stable/cli/get.sh | bashOr if using Nix, it's already installed in the development environment.
Create a new migration:
cd hasura
hasura migrate create "migration_name" --database-name defaultApply migrations:
hasura migrate apply --database-name defaultExport metadata:
hasura metadata exportThe project includes a comprehensive database backup script (backup-db.sh) that supports both Docker and direct PostgreSQL connections.
Using npm scripts:
# Create a backup using Docker (default)
npm run db:backup
# Create a compressed backup
npm run db:backup:compress# Basic backup (Docker)
./backup-db.sh
# Backup with compression
./backup-db.sh --compress
# Backup to custom directory
./backup-db.sh --output /path/to/backups
# Backup using direct connection
./backup-db.sh --no-docker --host localhost --port 5432
# Backup specific database
./backup-db.sh --database mydb --user myuser
# Show all options
./backup-db.sh --help- Timestamp-based filenames for easy organization
- Compression support with gzip
- Docker and direct connection modes
- Custom backup directories
- Automatic backup directory creation
- Restore instructions after backup
- Error handling and validation
After creating a backup, the script provides restore instructions. For example:
# Restore uncompressed backup (Docker)
docker-compose exec -T postgres psql -U postgres -d hasura < backups/hasura_backup_20231110_120000.sql
# Restore compressed backup (Docker)
gunzip -c backups/hasura_backup_20231110_120000.sql.gz | docker-compose exec -T postgres psql -U postgres -d hasuraFor production environments, consider setting up automated backups using cron:
# Add to crontab (run daily at 2 AM)
0 2 * * * cd /path/to/hasura-blog && ./backup-db.sh --compress --output /backups/dailyDeploy to AWS with automated infrastructure provisioning using Terraform:
cd terraform
cp terraform.tfvars.example terraform.tfvars
# Edit terraform.tfvars with your configuration
# Set sensitive variables
export TF_VAR_db_password="your-password"
export TF_VAR_hasura_admin_secret="your-secret"
export TF_VAR_jwt_secret="your-jwt-secret"
export TF_VAR_nextauth_secret="your-nextauth-secret"
# Deploy
./deploy.shFor detailed instructions, see terraform/README.md and terraform/ARCHITECTURE.md.
Build the production image:
docker-compose -f docker-compose.yml buildMake sure to set strong values for production:
POSTGRES_PASSWORD- Strong database passwordHASURA_GRAPHQL_ADMIN_SECRET- Strong admin secretNEXTAUTH_SECRET- Strong NextAuth secretJWT_SECRET- Strong JWT secret (min 32 characters)
- Change all default passwords and secrets
- Use environment-specific configuration
- Enable HTTPS in production
- Configure CORS properly
- Set up proper database backups
- Use Hasura Cloud or self-hosted with proper security
Configure role-based permissions in Hasura Console:
- anonymous - Can read published posts
- user - Can create comments, read all posts
- author - Can create and edit their own posts
- admin - Full access to all operations
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the ISC License.
- PRISMA.md - Detailed Prisma integration guide
- NIX.md - Nix development environment guide
- DEPLOYMENT.md - Production deployment guide
- CONTRIBUTING.md - Contribution guidelines
For issues and questions:
- Open an issue on GitHub
- Check Hasura documentation: https://hasura.io/docs
- Check Next.js documentation: https://nextjs.org/docs
- Check Prisma documentation: https://www.prisma.io/docs