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.
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:
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);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}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}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}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_TOKENAdd 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}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});Before every production deployment, go through these checkpoints:
next build command finishes without errorsIn 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!