We use cookies to enhance your experience on the site
CodeWorlds

Project: Full deployment of a Next.js application to production

Welcome to the final project of the deployment module! In Quantum Metropolis 2150, every system goes through a rigorous process of preparing for production - from building and optimization, through SEO configuration, all the way to monitoring and testing. In this project, we will combine all the skills gained throughout the module to go through the full deployment cycle of a Next.js application.

Project goal

Your task is to prepare a complete Next.js application for a production deployment. The project covers all the key aspects we discussed in this module:

  1. Building and optimization (from the build and analyze lesson)
  2. SEO and metadata (from the dynamic metadata lesson)
  3. Open Graph and social sharing (from the OG lesson)
  4. Sitemap and robots.txt (from the sitemaps lesson)
  5. Web Analytics (from the analytics lesson)
  6. Deploying to hosting platforms (from the Vercel/Netlify lesson)
  7. Environment variables and production configuration (from the env vars lesson)
  8. CI/CD (from the automation lesson)
  9. Monitoring (from the monitoring lesson)
  10. PWA and Service Workers (from the PWA lesson)
  11. Preview Deployments and staging (from the environments lesson)

Step 1: Configuring next.config.js

Start with a full Next.js configuration with production optimizations:

1// next.config.ts
2import type { NextConfig } from 'next';
3import withBundleAnalyzer from '@next/bundle-analyzer';
4
5const config: NextConfig = {
6  // Image optimization
7  images: {
8    formats: ['image/avif', 'image/webp'],
9    remotePatterns: [
10      {
11        protocol: 'https',
12        hostname: 'cdn.quantum-city.com',
13        pathname: '/images/**',
14      },
15    ],
16    minimumCacheTTL: 60 * 60 * 24 * 30, // 30 days cache
17  },
18
19  // Compression
20  compress: true,
21
22  // Bundle optimization
23  experimental: {
24    optimizePackageImports: ['lucide-react', '@quantum/ui'],
25  },
26
27  // Security headers
28  async headers() {
29    return [
30      {
31        source: '/:path*',
32        headers: [
33          { key: 'X-Frame-Options', value: 'SAMEORIGIN' },
34          { key: 'X-Content-Type-Options', value: 'nosniff' },
35          { key: 'X-XSS-Protection', value: '1; mode=block' },
36          { key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
37          {
38            key: 'Strict-Transport-Security',
39            value: 'max-age=63072000; includeSubDomains; preload',
40          },
41          {
42            key: 'Permissions-Policy',
43            value: 'camera=(), microphone=(), geolocation=()',
44          },
45        ],
46      },
47    ];
48  },
49
50  // Redirects
51  async redirects() {
52    return [
53      {
54        source: '/old-page',
55        destination: '/new-page',
56        permanent: true, // 301 redirect
57      },
58    ];
59  },
60};
61
62const withAnalyzer = withBundleAnalyzer({
63  enabled: process.env.ANALYZE === 'true',
64});
65
66export default withAnalyzer(config);

Step 2: SEO and dynamic metadata

Configure metadata for each page using the Metadata API:

1// app/layout.tsx
2import type { Metadata } from 'next';
3
4export const metadata: Metadata = {
5  metadataBase: new URL('https://quantum-city.com'),
6  title: {
7    default: 'Quantum City - Metropolis Portal',
8    template: '%s | Quantum City',
9  },
10  description: 'The official portal of Quantum Metropolis 2150',
11  openGraph: {
12    type: 'website',
13    locale: 'pl_PL',
14    url: 'https://quantum-city.com',
15    siteName: 'Quantum City',
16    images: [
17      {
18        url: '/og-default.png',
19        width: 1200,
20        height: 630,
21        alt: 'Quantum City',
22      },
23    ],
24  },
25  twitter: {
26    card: 'summary_large_image',
27    creator: '@QuantumCity',
28  },
29  robots: {
30    index: true,
31    follow: true,
32    googleBot: {
33      index: true,
34      follow: true,
35      'max-video-preview': -1,
36      'max-image-preview': 'large',
37      'max-snippet': -1,
38    },
39  },
40};
41
42// app/products/[id]/page.tsx - dynamic metadata
43import type { Metadata } from 'next';
44
45type Props = { params: Promise<{ id: string }> };
46
47export async function generateMetadata({ params }: Props): Promise<Metadata> {
48  const product = await fetch(
49    `https://api.quantum-city.com/products/${(await params).id}`
50  ).then(r => r.json());
51
52  return {
53    title: product.name,
54    description: product.description,
55    openGraph: {
56      title: product.name,
57      description: product.description,
58      images: [{ url: product.imageUrl, width: 800, height: 600 }],
59    },
60  };
61}

Step 3: Sitemap and robots.txt

Generate a dynamic sitemap:

1// app/sitemap.ts
2import { MetadataRoute } from 'next';
3
4export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
5  const baseUrl = 'https://quantum-city.com';
6
7  // Fetch dynamic pages
8  const products = await fetch('https://api.quantum-city.com/products')
9    .then(r => r.json());
10
11  const posts = await fetch('https://api.quantum-city.com/blog')
12    .then(r => r.json());
13
14  const productPages = products.map((product: any) => ({
15    url: `${baseUrl}/products/${product.slug}`,
16    lastModified: new Date(product.updatedAt),
17    changeFrequency: 'weekly' as const,
18    priority: 0.8,
19  }));
20
21  const blogPages = posts.map((post: any) => ({
22    url: `${baseUrl}/blog/${post.slug}`,
23    lastModified: new Date(post.updatedAt),
24    changeFrequency: 'monthly' as const,
25    priority: 0.6,
26  }));
27
28  return [
29    { url: baseUrl, lastModified: new Date(), changeFrequency: 'daily', priority: 1 },
30    { url: `${baseUrl}/products`, lastModified: new Date(), changeFrequency: 'daily', priority: 0.9 },
31    ...productPages,
32    ...blogPages,
33  ];
34}
35
36// app/robots.ts
37import { MetadataRoute } from 'next';
38
39export default function robots(): MetadataRoute.Robots {
40  return {
41    rules: [
42      {
43        userAgent: '*',
44        allow: '/',
45        disallow: ['/admin/', '/api/', '/dashboard/'],
46      },
47    ],
48    sitemap: 'https://quantum-city.com/sitemap.xml',
49  };
50}

Step 4: Web Analytics and monitoring

Integrate an analytics and error-monitoring system:

1// lib/analytics.ts
2export function trackEvent(name: string, properties?: Record<string, any>) {
3  if (typeof window === 'undefined') return;
4
5  // Custom analytics
6  fetch('/api/analytics', {
7    method: 'POST',
8    headers: { 'Content-Type': 'application/json' },
9    body: JSON.stringify({ event: name, properties, timestamp: Date.now() }),
10  }).catch(console.error);
11}
12
13// lib/monitoring.ts
14export function initErrorMonitoring() {
15  if (typeof window === 'undefined') return;
16
17  window.addEventListener('error', (event) => {
18    fetch('/api/errors', {
19      method: 'POST',
20      headers: { 'Content-Type': 'application/json' },
21      body: JSON.stringify({
22        message: event.message,
23        filename: event.filename,
24        lineno: event.lineno,
25        stack: event.error?.stack,
26        url: window.location.href,
27        timestamp: new Date().toISOString(),
28      }),
29    }).catch(console.error);
30  });
31}

Step 5: CI/CD Pipeline

Configure an automatic deployment pipeline:

1# .github/workflows/deploy.yml
2name: Deploy to Production
3
4on:
5  push:
6    branches: [main]
7
8jobs:
9  test:
10    runs-on: ubuntu-latest
11    steps:
12      - uses: actions/checkout@v4
13      - uses: actions/setup-node@v4
14        with:
15          node-version: '20'
16          cache: 'npm'
17      - run: npm ci
18      - run: npm run lint
19      - run: npm run type-check
20      - run: npm test
21
22  e2e:
23    needs: test
24    runs-on: ubuntu-latest
25    steps:
26      - uses: actions/checkout@v4
27      - uses: actions/setup-node@v4
28        with:
29          node-version: '20'
30      - run: npm ci
31      - run: npm run build
32      - name: Cypress E2E Tests
33        uses: cypress-io/github-action@v6
34        with:
35          start: npm start
36          wait-on: 'http://localhost:3000'
37          browser: chrome
38
39  deploy:
40    needs: [test, e2e]
41    runs-on: ubuntu-latest
42    steps:
43      - uses: actions/checkout@v4
44      - name: Deploy to Vercel
45        run: npx vercel --prod --token=VERCEL_TOKEN

Step 6: PWA and offline support

Add Progressive Web App functionality:

1// app/manifest.ts
2import { MetadataRoute } from 'next';
3
4export default function manifest(): MetadataRoute.Manifest {
5  return {
6    name: 'Quantum City Portal',
7    short_name: 'QC Portal',
8    description: 'The official portal of Quantum Metropolis 2150',
9    start_url: '/',
10    display: 'standalone',
11    background_color: '#0a0a1a',
12    theme_color: '#64ffda',
13    icons: [
14      { src: '/icon-192.png', sizes: '192x192', type: 'image/png' },
15      { src: '/icon-512.png', sizes: '512x512', type: 'image/png' },
16    ],
17  };
18}

Step 7: E2E tests with Cypress

Write tests covering critical user paths:

1// cypress/e2e/production-readiness.cy.ts
2describe('Production Readiness Check', () => {
3  it('home page loads in under 3 seconds', () => {
4    cy.visit('/', { timeout: 3000 });
5    cy.get('h1').should('be.visible');
6  });
7
8  it('SEO meta tags are correct', () => {
9    cy.visit('/');
10    cy.get('head title').should('contain', 'Quantum City');
11    cy.get('meta[name="description"]').should('have.attr', 'content');
12  });
13
14  it('security headers are set', () => {
15    cy.request('/').then((response) => {
16      expect(response.headers).to.have.property('x-frame-options');
17      expect(response.headers).to.have.property('x-content-type-options');
18    });
19  });
20});

Step 8: Pre-deployment checklist

Before every production deployment, go through these checkpoints:

Performance

  • The
    next build
    command finishes without errors
  • JS bundle size below 200KB (gzipped)
  • LCP below 2.5 seconds, CLS below 0.1
  • Images optimized (WebP/AVIF)

SEO

  • Every page has a unique title and description
  • Open Graph tags are correct
  • Sitemap.xml and robots.txt are generated

Security

  • Security headers (HSTS, CSP, X-Frame-Options)
  • CSRF protection and rate limiting
  • Input validation with Zod

Monitoring

  • Error tracking configured
  • Web Vitals monitored
  • Alerts for critical errors

CI/CD

  • Unit and E2E tests pass
  • Linting and type-checking with no errors
  • Automatic deploy after merge to main

Summary

In this project, you combined all the key skills for deploying a Next.js application to production. Each of these elements is essential for creating a professional, performant, and secure web application.

Remember - in Quantum Metropolis 2150 there is no room for half-measures. Every system must be fully optimized, secured, and monitored. Now you are ready to deploy your Next.js applications at the highest level!

Go to CodeWorlds