Overview
GitHub Actions provides powerful CI/CD capabilities for Next.js applications. It allows you to automate testing, building, and deployment processes directly from your GitHub repository.
CI/CD Benefits
Automated testing - Continuous deployment - Quality gates - Rollback
capabilities
GitHub Actions Features
Workflow automation - Matrix builds - Environment management - Integration
with Vercel
Basic Workflow Setup
Simple CI Workflow
# .github/workflows/ci.yml
name : CI
on :
push :
branches : [ main , develop ]
pull_request :
branches : [ main ]
jobs :
test :
runs-on : ubuntu-latest
steps :
- name : Checkout code
uses : actions/checkout@v4
- name : Setup Node.js
uses : actions/setup-node@v4
with :
node-version : "18"
cache : "npm"
- name : Install dependencies
run : npm ci
- name : Run linting
run : npm run lint
- name : Run type checking
run : npm run type-check
- name : Run tests
run : npm run test
- name : Build application
run : npm run build
Advanced CI Workflow
# .github/workflows/advanced-ci.yml
name : Advanced CI
on :
push :
branches : [ main , develop ]
pull_request :
branches : [ main ]
env :
NODE_VERSION : "18"
PNPM_VERSION : "8"
jobs :
lint-and-typecheck :
name : Lint and Type Check
runs-on : ubuntu-latest
steps :
- name : Checkout code
uses : actions/checkout@v4
- name : Setup Node.js
uses : actions/setup-node@v4
with :
node-version : ${{ env.NODE_VERSION }}
cache : "npm"
- name : Install dependencies
run : npm ci
- name : Run ESLint
run : npm run lint
- name : Run TypeScript check
run : npm run type-check
- name : Check formatting
run : npm run format:check
test :
name : Test
runs-on : ubuntu-latest
strategy :
matrix :
node-version : [ 16 , 18 , 20 ]
steps :
- name : Checkout code
uses : actions/checkout@v4
- name : Setup Node.js ${{ matrix.node-version }}
uses : actions/setup-node@v4
with :
node-version : ${{ matrix.node-version }}
cache : "npm"
- name : Install dependencies
run : npm ci
- name : Run tests
run : npm run test:ci
- name : Upload coverage reports
uses : codecov/codecov-action@v3
with :
file : ./coverage/lcov.info
build :
name : Build
runs-on : ubuntu-latest
needs : [ lint-and-typecheck , test ]
steps :
- name : Checkout code
uses : actions/checkout@v4
- name : Setup Node.js
uses : actions/setup-node@v4
with :
node-version : ${{ env.NODE_VERSION }}
cache : "npm"
- name : Install dependencies
run : npm ci
- name : Build application
run : npm run build
- name : Upload build artifacts
uses : actions/upload-artifact@v3
with :
name : build-files
path : .next/
Deployment Workflows
Vercel Deployment
# .github/workflows/deploy-vercel.yml
name : Deploy to Vercel
on :
push :
branches : [ main ]
pull_request :
branches : [ main ]
jobs :
deploy :
runs-on : ubuntu-latest
steps :
- name : Checkout code
uses : actions/checkout@v4
- name : Deploy to Vercel
uses : amondnet/vercel-action@v25
with :
vercel-token : ${{ secrets.VERCEL_TOKEN }}
vercel-org-id : ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id : ${{ secrets.VERCEL_PROJECT_ID }}
vercel-args : "--prod"
Multi-Environment Deployment
# .github/workflows/deploy-multi-env.yml
name : Deploy Multi-Environment
on :
push :
branches : [ main , develop ]
pull_request :
branches : [ main ]
jobs :
deploy-staging :
if : github.ref == 'refs/heads/develop'
runs-on : ubuntu-latest
steps :
- name : Checkout code
uses : actions/checkout@v4
- name : Deploy to Staging
uses : amondnet/vercel-action@v25
with :
vercel-token : ${{ secrets.VERCEL_TOKEN }}
vercel-org-id : ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id : ${{ secrets.VERCEL_PROJECT_ID }}
vercel-args : "--target preview"
deploy-production :
if : github.ref == 'refs/heads/main'
runs-on : ubuntu-latest
needs : [ test , build ]
steps :
- name : Checkout code
uses : actions/checkout@v4
- name : Deploy to Production
uses : amondnet/vercel-action@v25
with :
vercel-token : ${{ secrets.VERCEL_TOKEN }}
vercel-org-id : ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id : ${{ secrets.VERCEL_PROJECT_ID }}
vercel-args : "--prod"
- name : Notify deployment
uses : 8398a7/action-slack@v3
with :
status : ${{ job.status }}
channel : "#deployments"
webhook_url : ${{ secrets.SLACK_WEBHOOK }}
Testing Workflows
Comprehensive Testing
# .github/workflows/test.yml
name : Test
on :
push :
branches : [ main , develop ]
pull_request :
branches : [ main ]
jobs :
unit-tests :
name : Unit Tests
runs-on : ubuntu-latest
steps :
- name : Checkout code
uses : actions/checkout@v4
- name : Setup Node.js
uses : actions/setup-node@v4
with :
node-version : "18"
cache : "npm"
- name : Install dependencies
run : npm ci
- name : Run unit tests
run : npm run test:unit
- name : Upload coverage
uses : codecov/codecov-action@v3
integration-tests :
name : Integration Tests
runs-on : ubuntu-latest
services :
postgres :
image : postgres:15
env :
POSTGRES_PASSWORD : postgres
POSTGRES_DB : test_db
options : > -
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports :
- 5432:5432
steps :
- name : Checkout code
uses : actions/checkout@v4
- name : Setup Node.js
uses : actions/setup-node@v4
with :
node-version : "18"
cache : "npm"
- name : Install dependencies
run : npm ci
- name : Run integration tests
run : npm run test:integration
env :
DATABASE_URL : postgresql://postgres:postgres@localhost:5432/test_db
e2e-tests :
name : E2E Tests
runs-on : ubuntu-latest
steps :
- name : Checkout code
uses : actions/checkout@v4
- name : Setup Node.js
uses : actions/setup-node@v4
with :
node-version : "18"
cache : "npm"
- name : Install dependencies
run : npm ci
- name : Build application
run : npm run build
- name : Start application
run : npm start &
- name : Wait for application
run : npx wait-on http://localhost:3000
- name : Run E2E tests
run : npm run test:e2e
Security Workflows
Security Scanning
# .github/workflows/security.yml
name : Security
on :
push :
branches : [ main ]
pull_request :
branches : [ main ]
schedule :
- cron : "0 2 * * 1" # Weekly on Monday at 2 AM
jobs :
dependency-scan :
name : Dependency Scan
runs-on : ubuntu-latest
steps :
- name : Checkout code
uses : actions/checkout@v4
- name : Run security audit
run : npm audit --audit-level moderate
- name : Check for vulnerabilities
uses : actions/dependency-review-action@v3
if : github.event_name == 'pull_request'
code-scan :
name : Code Scan
runs-on : ubuntu-latest
steps :
- name : Checkout code
uses : actions/checkout@v4
- name : Run CodeQL Analysis
uses : github/codeql-action/init@v2
with :
languages : javascript
- name : Perform CodeQL Analysis
uses : github/codeql-action/analyze@v2
# .github/workflows/performance.yml
name : Performance
on :
push :
branches : [ main ]
pull_request :
branches : [ main ]
jobs :
lighthouse :
name : Lighthouse CI
runs-on : ubuntu-latest
steps :
- name : Checkout code
uses : actions/checkout@v4
- name : Setup Node.js
uses : actions/setup-node@v4
with :
node-version : "18"
cache : "npm"
- name : Install dependencies
run : npm ci
- name : Build application
run : npm run build
- name : Start application
run : npm start &
- name : Wait for application
run : npx wait-on http://localhost:3000
- name : Run Lighthouse CI
run : npm run lighthouse:ci
- name : Upload Lighthouse results
uses : actions/upload-artifact@v3
with :
name : lighthouse-results
path : .lighthouseci/
Advanced Workflows
Matrix Builds
# .github/workflows/matrix.yml
name : Matrix Build
on :
push :
branches : [ main ]
pull_request :
branches : [ main ]
jobs :
test-matrix :
name : Test Matrix
runs-on : ubuntu-latest
strategy :
matrix :
node-version : [ 16 , 18 , 20 ]
os : [ ubuntu-latest , windows-latest , macos-latest ]
steps :
- name : Checkout code
uses : actions/checkout@v4
- name : Setup Node.js ${{ matrix.node-version }}
uses : actions/setup-node@v4
with :
node-version : ${{ matrix.node-version }}
cache : "npm"
- name : Install dependencies
run : npm ci
- name : Run tests
run : npm run test
Conditional Workflows
# .github/workflows/conditional.yml
name : Conditional Workflow
on :
push :
branches : [ main ]
pull_request :
branches : [ main ]
jobs :
check-changes :
name : Check Changes
runs-on : ubuntu-latest
outputs :
frontend : ${{ steps.changes.outputs.frontend }}
backend : ${{ steps.changes.outputs.backend }}
steps :
- name : Checkout code
uses : actions/checkout@v4
with :
fetch-depth : 0
- name : Check for changes
uses : dorny/paths-filter@v2
id : changes
with :
filters : |
frontend:
- 'src/**'
- 'app/**'
- 'components/**'
backend:
- 'api/**'
- 'lib/**'
- 'server/**'
test-frontend :
name : Test Frontend
runs-on : ubuntu-latest
needs : check-changes
if : needs.check-changes.outputs.frontend == 'true'
steps :
- name : Checkout code
uses : actions/checkout@v4
- name : Setup Node.js
uses : actions/setup-node@v4
with :
node-version : "18"
cache : "npm"
- name : Install dependencies
run : npm ci
- name : Run frontend tests
run : npm run test:frontend
test-backend :
name : Test Backend
runs-on : ubuntu-latest
needs : check-changes
if : needs.check-changes.outputs.backend == 'true'
steps :
- name : Checkout code
uses : actions/checkout@v4
- name : Setup Node.js
uses : actions/setup-node@v4
with :
node-version : "18"
cache : "npm"
- name : Install dependencies
run : npm ci
- name : Run backend tests
run : npm run test:backend
Best Practices
Workflow Best Practices
Use Matrix Builds
Test across multiple Node.js versions and operating systems
Implement Quality Gates
Require tests to pass before deployment
Use Caching
Cache dependencies and build artifacts for faster builds
Security First
Scan for vulnerabilities and security issues
Build Optimization
Use npm ci for faster installs - Cache node_modules - Use build artifacts
Parallel job execution
Test Optimization
Run tests in parallel - Use test coverage - Implement test matrices - Use
conditional workflows
Common Patterns
Release Workflow
# .github/workflows/release.yml
name : Release
on :
push :
tags :
- "v*"
jobs :
release :
name : Create Release
runs-on : ubuntu-latest
steps :
- name : Checkout code
uses : actions/checkout@v4
- name : Setup Node.js
uses : actions/setup-node@v4
with :
node-version : "18"
cache : "npm"
- name : Install dependencies
run : npm ci
- name : Build application
run : npm run build
- name : Create release
uses : actions/create-release@v1
env :
GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }}
with :
tag_name : ${{ github.ref }}
release_name : Release ${{ github.ref }}
draft : false
prerelease : false
Notification Workflow
# .github/workflows/notify.yml
name : Notify
on :
workflow_run :
workflows : [ "CI" , "Deploy" ]
types : [ completed ]
jobs :
notify :
name : Notify Team
runs-on : ubuntu-latest
if : github.event.workflow_run.conclusion != 'success'
steps :
- name : Notify Slack
uses : 8398a7/action-slack@v3
with :
status : ${{ github.event.workflow_run.conclusion }}
channel : "#alerts"
webhook_url : ${{ secrets.SLACK_WEBHOOK }}
Troubleshooting
Problem : Workflows failing unexpectedly
Solution : Check logs, verify environment variables, ensure all dependencies are available
Problem : Builds taking too long Solution : Use caching, optimize
dependencies, run jobs in parallel
Problem : Deployments failing Solution : Check secrets, verify
environment variables, ensure proper permissions
Problem : Tests failing in CI but passing locally
Solution : Check environment differences, ensure proper test setup, verify database connections
Key Takeaway : GitHub Actions provides powerful CI/CD capabilities for
Next.js applications. Use it to automate testing, building, and deployment
while implementing quality gates and security checks.
Overview
Vercel is a cloud platform designed specifically for Next.js applications. It provides automatic deployments, global CDN, serverless functions, and many other features that make it ideal for modern web applications.
Vercel Benefits
Automatic deployments - Global CDN - Serverless functions - Edge computing
Key Features
Zero-config deployments - Preview deployments - Analytics and monitoring -
Environment management
Getting Started with Vercel
Initial Setup
Install Vercel CLI:
Login to Vercel:
Deploy your project:
Project Configuration
Create a vercel.json
file in your project root:
{
"framework" : "nextjs" ,
"buildCommand" : "npm run build" ,
"outputDirectory" : ".next" ,
"installCommand" : "npm install" ,
"devCommand" : "npm run dev" ,
"regions" : [ "iad1" ],
"functions" : {
"app/api/**/*.ts" : {
"maxDuration" : 30
}
}
}
Environment Variables
Setting Environment Variables
Via Vercel Dashboard:
Go to your project settings
Navigate to Environment Variables
Add your variables
Via CLI:
vercel env add DATABASE_URL
vercel env add JWT_SECRET
Via vercel.json:
{
"env" : {
"DATABASE_URL" : "@database-url" ,
"JWT_SECRET" : "@jwt-secret"
}
}
Environment-Specific Variables
# Production
vercel env add DATABASE_URL production
# Preview
vercel env add DATABASE_URL preview
# Development
vercel env add DATABASE_URL development
Deployment Strategies
Automatic Deployments
Vercel automatically deploys when you push to your connected Git repository:
# Connect to Git repository
vercel --prod
# Deploy specific branch
vercel --prod --target production
Preview Deployments
Every pull request gets a preview deployment:
# Deploy preview
vercel --target preview
# Deploy specific branch
vercel --target preview --branch feature-branch
Manual Deployments
# Deploy to production
vercel --prod
# Deploy with specific environment
vercel --prod --env production
Vercel-Specific Features
Edge Functions
Create edge functions for global performance:
// app/api/edge/route.ts
import { NextRequest , NextResponse } from "next/server" ;
export const config = {
runtime: "edge" ,
};
export default function handler ( request : NextRequest ) {
return NextResponse . json ({
message: "Hello from the edge!" ,
region: process . env . VERCEL_REGION ,
});
}
Middleware
Use Vercel’s middleware for request handling:
// middleware.ts
import { NextResponse } from "next/server" ;
import type { NextRequest } from "next/server" ;
export function middleware ( request : NextRequest ) {
// Add custom headers
const response = NextResponse . next ();
response . headers . set ( "x-custom-header" , "custom-value" );
return response ;
}
export const config = {
matcher: "/api/:path*" ,
};
Image Optimization
Vercel provides automatic image optimization:
// app/page.tsx
import Image from "next/image" ;
export default function Home () {
return (
< div >
< Image
src = "/hero-image.jpg"
alt = "Hero image"
width = { 800 }
height = { 600 }
priority
placeholder = "blur"
blurDataURL = "data:image/jpeg;base64,..."
/>
</ div >
);
}
Build Optimization
// vercel.json
{
"buildCommand" : "npm run build" ,
"outputDirectory" : ".next" ,
"installCommand" : "npm ci" ,
"framework" : "nextjs" ,
"functions" : {
"app/api/**/*.ts" : {
"maxDuration" : 30
}
}
}
Caching Strategies
// app/api/cached-data/route.ts
import { NextRequest , NextResponse } from "next/server" ;
export async function GET ( request : NextRequest ) {
const data = await fetchExpensiveData ();
return NextResponse . json ( data , {
headers: {
"Cache-Control" : "public, s-maxage=3600, stale-while-revalidate=86400" ,
},
});
}
Static Generation
// app/blog/[slug]/page.tsx
import { generateStaticParams } from "next" ;
export async function generateStaticParams () {
const posts = await fetch ( "https://api.example.com/posts" ). then (( res ) =>
res . json ()
);
return posts . map (( post : any ) => ({
slug: post . slug ,
}));
}
export default function BlogPost ({ params } : { params : { slug : string } }) {
return < div > Blog post : {params. slug } </ div > ;
}
Database Integration
Vercel Postgres
// lib/db.ts
import { sql } from "@vercel/postgres" ;
export async function getUsers () {
try {
const { rows } = await sql `SELECT * FROM users` ;
return rows ;
} catch ( error ) {
console . error ( "Database error:" , error );
throw error ;
}
}
Connection Pooling
// lib/db.ts
import { Pool } from "pg" ;
const pool = new Pool ({
connectionString: process . env . DATABASE_URL ,
ssl: {
rejectUnauthorized: false ,
},
});
export async function query ( text : string , params ?: any []) {
const client = await pool . connect ();
try {
const result = await client . query ( text , params );
return result ;
} finally {
client . release ();
}
}
Monitoring and Analytics
Vercel Analytics
// app/layout.tsx
import { Analytics } from "@vercel/analytics/react" ;
export default function RootLayout ({
children ,
} : {
children : React . ReactNode ;
}) {
return (
< html lang = "en" >
< body >
{ children }
< Analytics />
</ body >
</ html >
);
}
Custom Analytics
// lib/analytics.ts
export function trackEvent ( event : string , properties ?: Record < string , any >) {
if ( typeof window !== "undefined" ) {
// Send to analytics service
fetch ( "/api/analytics" , {
method: "POST" ,
headers: { "Content-Type" : "application/json" },
body: JSON . stringify ({ event , properties }),
});
}
}
Security Best Practices
Environment Variables
// lib/env.ts
import { z } from "zod" ;
const envSchema = z . object ({
DATABASE_URL: z . string (). url (),
JWT_SECRET: z . string (). min ( 32 ),
NEXTAUTH_SECRET: z . string (). min ( 32 ),
NEXTAUTH_URL: z . string (). url (),
});
export const env = envSchema . parse ( process . env );
CORS Configuration
// app/api/cors/route.ts
import { NextRequest , NextResponse } from "next/server" ;
export async function GET ( request : NextRequest ) {
const response = NextResponse . json ({ message: "Hello" });
response . headers . set ( "Access-Control-Allow-Origin" , "https://yourdomain.com" );
response . headers . set (
"Access-Control-Allow-Methods" ,
"GET, POST, PUT, DELETE"
);
response . headers . set (
"Access-Control-Allow-Headers" ,
"Content-Type, Authorization"
);
return response ;
}
Advanced Configuration
Custom Domains
// vercel.json
{
"domains" : [ "yourdomain.com" , "www.yourdomain.com" ],
"redirects" : [
{
"source" : "/old-path" ,
"destination" : "/new-path" ,
"permanent" : true
}
]
}
// vercel.json
{
"headers" : [
{
"source" : "/api/(.*)" ,
"headers" : [
{
"key" : "Access-Control-Allow-Origin" ,
"value" : "https://yourdomain.com"
},
{
"key" : "Access-Control-Allow-Methods" ,
"value" : "GET, POST, PUT, DELETE, OPTIONS"
}
]
}
]
}
Rewrites and Redirects
// vercel.json
{
"rewrites" : [
{
"source" : "/api/proxy/(.*)" ,
"destination" : "https://external-api.com/$1"
}
],
"redirects" : [
{
"source" : "/old-page" ,
"destination" : "/new-page" ,
"permanent" : true
}
]
}
Troubleshooting
Common Issues
Problem : Build failing on Vercel
Solution : Check build logs, ensure all dependencies are in package.json, verify environment variables
Problem : Environment variables not available Solution : Ensure
variables are set in Vercel dashboard and match the environment
(production/preview/development)
Problem : API routes timing out Solution : Increase maxDuration in
vercel.json, optimize database queries, use edge functions for simple
operations
Problem : CORS errors in production
Solution : Configure proper CORS headers in API routes or vercel.json
Debugging
// app/api/debug/route.ts
import { NextRequest , NextResponse } from "next/server" ;
export async function GET ( request : NextRequest ) {
return NextResponse . json ({
environment: process . env . NODE_ENV ,
region: process . env . VERCEL_REGION ,
url: request . url ,
headers: Object . fromEntries ( request . headers . entries ()),
});
}
Best Practices
Deployment Best Practices
Environment Management
Use different environments for development, staging, and production
Security
Never commit secrets to version control, use Vercel’s environment variables
Performance
Optimize images, use static generation, implement proper caching
Monitoring
Set up analytics and monitoring to track performance and errors
Optimization Tips
Build Optimization
Use npm ci for faster installs - Optimize bundle size - Use dynamic
imports - Implement code splitting
Runtime Optimization
Use edge functions for simple operations - Implement proper caching -
Optimize database queries - Use CDN for static assets
Key Takeaway : Vercel provides a powerful platform for deploying Next.js
applications with automatic deployments, global CDN, and serverless functions.
Use its features to optimize performance and simplify deployment workflows.