Files
school_compare/nextjs-app/next.config.js
T
Tudor Sitaru 62eeee5f7c
Build and Push Docker Images / Build Backend (FastAPI) (push) Successful in 1m1s
Build and Push Docker Images / Build Frontend (Next.js) (push) Successful in 53s
Build and Push Docker Images / Build Pipeline (Meltano + dbt + Airflow) (push) Successful in 2m4s
Build and Push Docker Images / Trigger Portainer Update (push) Successful in 1s
perf: cache aggressively and trim client bundle
Frontend
- Dynamic-import Chart.js components on detail/compare views so Chart.js
  no longer ships in initial JS.
- Drop force-dynamic on home, compare, rankings so internal data fetches
  reuse Next.js's per-call revalidate cache.
- Switch /school/[slug] to ISR with a 7-day revalidate window (school
  data updates annually).
- Preconnect to analytics + postcodes.io; remove redundant defer on the
  Umami Script tag (afterInteractive already covers it).
- Bump images.minimumCacheTTL to 1 year.
- Extract HowItWorks and Editorial sections as server components passed
  to HomeView via slot props so their JSX stays out of the client bundle.

Backend
- Add GZipMiddleware (min 512 bytes).
- Add CacheAndETagMiddleware: per-path Cache-Control with long s-maxage
  + stale-while-revalidate, ETag generation, and 304 on If-None-Match.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-02 13:46:45 +01:00

95 lines
2.2 KiB
JavaScript

/** @type {import('next').NextConfig} */
const nextConfig = {
// Enable standalone output for Docker
output: 'standalone',
// API Proxy to FastAPI backend
async rewrites() {
const apiUrl = process.env.FASTAPI_URL || 'http://localhost:8000/api';
const backendUrl = apiUrl.replace(/\/api$/, '');
return [
{
source: '/api/:path*',
destination: `${apiUrl}/:path*`,
},
{
source: '/sitemap.xml',
destination: `${backendUrl}/sitemap.xml`,
},
];
},
// Image optimization
images: {
remotePatterns: [
{ protocol: 'https', hostname: '*.tile.openstreetmap.org' },
{ protocol: 'https', hostname: 'tile.openstreetmap.org' },
{ protocol: 'https', hostname: 'cdnjs.cloudflare.com' },
],
formats: ['image/avif', 'image/webp'],
minimumCacheTTL: 31536000,
},
// Performance optimizations
compiler: {
// Remove console logs in production
removeConsole: process.env.NODE_ENV === 'production',
},
// Compression
compress: true,
// React strict mode for better error detection
reactStrictMode: true,
// Power optimizations
poweredByHeader: false,
// Production source maps (disable for smaller bundles)
productionBrowserSourceMaps: false,
// Experimental features for performance
experimental: {
// Optimize package imports
optimizePackageImports: ['chart.js', 'react-chartjs-2', 'leaflet'],
},
// Headers for caching and security
async headers() {
return [
{
source: '/:path*',
headers: [
{
key: 'X-DNS-Prefetch-Control',
value: 'on',
},
{
key: 'X-Frame-Options',
value: 'SAMEORIGIN',
},
{
key: 'X-Content-Type-Options',
value: 'nosniff',
},
{
key: 'Referrer-Policy',
value: 'origin-when-cross-origin',
},
],
},
{
source: '/favicon.svg',
headers: [
{
key: 'Cache-Control',
value: 'public, max-age=31536000, immutable',
},
],
},
];
},
};
module.exports = nextConfig;