Basic Configuration
Next.config.js Configuration
The next.config.js file is used to customize Next.js build configurations.
Example Configuration
// next.config.js
const path = require('path');
module.exports = {
reactStrictMode: true,
swcMinify: true,
images: {
domains: ['example.com'],
},
experimental: {
appDir: true, // Enable the new App Router
serverComponents: true,
// Other experimental features...
},
// Define webpack aliases
webpack(config) {
config.resolve.alias = {
...config.resolve.alias,
'@components': path.resolve(__dirname, 'src/components'),
'@utils': path.resolve(__dirname, 'src/utils'),
};
return config;
},
// Other configuration options...
};Configuration Options
reactStrictMode: Enables React.StrictMode.swcMinify: Uses SWC for code minification.images: Configures image optimization options.experimental: Enables experimental features like the new App Router and Server Components.
TypeScript Configuration
Next.js supports TypeScript, configurable via the tsconfig.json file.
Example Configuration
// tsconfig.json
{
"compilerOptions": {
"target": "esnext",
"useDefineForClassFields": true,
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
}ESLint Configuration
ESLint is used to enforce code style and detect potential errors.
Example Configuration
// .eslintrc.json
{
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"prettier/@typescript-eslint",
"plugin:prettier/recommended"
],
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint"],
"rules": {
"prefer-const": "warn",
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/no-explicit-any": "off"
}
}Environment Variables Configuration
Next.js supports managing environment variables using .env files.
Example Configuration
// .env
NEXT_PUBLIC_API_URL=https://api.example.comUsing Environment Variables in Code
// app/page.js
console.log(process.env.NEXT_PUBLIC_API_URL);Absolute Imports and Module Path Aliases
Absolute imports allow importing modules directly from the project root without relative paths. Module path aliases enable defining shorthand aliases for specific directories.
Configure TypeScript
Define path aliases in tsconfig.json.
// tsconfig.json
{
"compilerOptions": {
"baseUrl": "./src", // Set source directory as base path
"paths": {
"@components/*": ["components/*"], // Define aliases
"@utils/*": ["utils/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
}Using Aliases
// app/page.js
import MyComponent from '@components/MyComponent'; // Using aliasSource Directory
By default, Next.js uses the pages directory as the source directory. You can change this via next.config.js.
Example Configuration
// next.config.js
module.exports = {
pageExtensions: ['page.tsx', 'page.ts'], // Specify source file extensions
// Other configuration options...
};Draft Mode
Draft mode allows previewing unpublished changes, useful for content editing.
Enable Draft Mode
Enable draft mode in next.config.js.
// next.config.js
module.exports = {
experimental: {
unstable_useDraftMode: true, // Enable draft mode
},
// Other configuration options...
};Using Draft Mode
Use the draftMode method in API routes.
// pages/api/draft.js
export default function handler(req, res) {
if (req.method === 'POST') {
res.unstable_setDraftMode({ enable: true });
res.redirect('/');
} else {
res.status(405).end('Method Not Allowed');
}
}Content Security Policy (CSP)
Content Security Policy is a security feature that helps defend against cross-site scripting (XSS) attacks.
Configure CSP
Configure CSP in next.config.js.
// next.config.js
const withCsp = require('next-content-security-policy');
module.exports = withCsp({
csp: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-inline'"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", "data:"],
connectSrc: ["'self'"],
frameSrc: ["'none'"],
objectSrc: ["'none'"],
mediaSrc: ["'self'"],
fontSrc: ["'self'"],
childSrc: ["'self'"],
formAction: ["'self'"],
baseUri: ["'self'"],
manifestSrc: ["'self'"],
workerSrc: ["'self'", "blob:"],
pluginTypes: ["application/pdf"],
},
},
// Other configuration options...
});Authentication
Technology Stack
- Next.js 14: Core framework for building the application.
- Node.js: Backend server.
- Express: Building backend APIs.
- jsonwebtoken: Generating and verifying JWTs.
- passport: OAuth 2.0 authentication middleware.
- passport-oauth2: OAuth 2.0 strategy.
Steps
- Set up project structure
- Create backend API
- Implement OAuth 2.0 authorization
- Generate and verify JWT
- Protect routes
- Project structure
Project Structure
Assume the following project structure:
my-app/
├── frontend/
│ ├── pages/
│ │ └── index.js
│ ├── public/
│ ├── src/
│ │ ├── api/
│ │ │ └── auth.js
│ │ └── utils/
│ │ └── jwt.js
│ ├── next.config.js
│ └── package.json
└── backend/
├── routes/
│ └── auth.js
├── controllers/
│ └── authController.js
├── passport/
│ └── oauth2.js
├── middleware/
│ └── authMiddleware.js
├── models/
│ └── User.js
├── app.js
└── package.jsonCreate Backend API
Install Dependencies
cd backend
npm install express passport passport-oauth2 jsonwebtoken bcryptjs dotenvCreate app.js
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const passport = require('passport');
const authRoutes = require('./routes/auth');
const app = express();
app.use(bodyParser.json());
app.use(cors());
app.use(passport.initialize());
app.use(authRoutes);
app.listen(3001, () => {
console.log('Server is running on port 3001');
});Implement OAuth 2.0 Authorization
Create passport/oauth2.js
const passport = require('passport');
const OAuth2Strategy = require('passport-oauth2').Strategy;
const User = require('../models/User');
passport.use(new OAuth2Strategy(
{
authorizationURL: 'http://localhost:3001/auth/authorize',
tokenURL: 'http://localhost:3001/auth/token',
clientID: 'your-client-id',
clientSecret: 'your-client-secret',
callbackURL: 'http://localhost:3000/callback',
},
async (accessToken, refreshToken, profile, done) => {
try {
let user = await User.findOne({ oauthId: profile.id });
if (!user) {
user = await User.create({ oauthId: profile.id, username: profile.username });
}
return done(null, user);
} catch (err) {
return done(err);
}
}
));Generate and Verify JWT
Create middleware/authMiddleware.js
const jwt = require('jsonwebtoken');
const secret = process.env.JWT_SECRET;
function generateToken(user) {
return jwt.sign({ id: user.id }, secret, { expiresIn: '1h' });
}
function verifyToken(token) {
return jwt.verify(token, secret);
}
module.exports = {
generateToken,
verifyToken,
};Protect Routes
Create routes/auth.js
const express = require('express');
const router = express.Router();
const authMiddleware = require('../middleware/authMiddleware');
const authController = require('../controllers/authController');
router.post('/login', authController.login);
router.post('/register', authController.register);
router.get('/protected', authMiddleware.protect, (req, res) => {
res.send('Protected route');
});
module.exports = router;Implement Controller Logic
Create controllers/authController.js
const bcrypt = require('bcryptjs');
const jwt = require('../middleware/authMiddleware');
const User = require('../models/User');
async function register(req, res) {
try {
const hashedPassword = await bcrypt.hash(req.body.password, 10);
const user = await User.create({
username: req.body.username,
password: hashedPassword,
});
const token = jwt.generateToken(user);
res.json({ token });
} catch (err) {
res.status(500).json({ error: err.message });
}
}
async function login(req, res) {
try {
const user = await User.findOne({ username: req.body.username });
if (!user || !(await bcrypt.compare(req.body.password, user.password))) {
return res.status(401).json({ error: 'Invalid credentials' });
}
const token = jwt.generateToken(user);
res.json({ token });
} catch (err) {
res.status(500).json({ error: err.message });
}
}
module.exports = {
register,
login,
};Frontend Implementation
Create src/api/auth.js
import axios from 'axios';
export async function login(data) {
const response = await axios.post('/api/auth/login', data);
return response.data;
}
export async function register(data) {
const response = await axios.post('/api/auth/register', data);
return response.data;
}Create pages/index.js
import { useState } from 'react';
import { login, register } from '../src/api/auth';
export default function Home() {
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const handleLogin = async () => {
try {
const result = await login({ username, password });
localStorage.setItem('token', result.token);
console.log('Logged in successfully');
} catch (error) {
console.error(error);
}
};
const handleRegister = async () => {
try {
const result = await register({ username, password });
localStorage.setItem('token', result.token);
console.log('Registered and logged in successfully');
} catch (error) {
console.error(error);
}
};
return (
<div>
<input type="text" value={username} onChange={(e) => setUsername(e.target.value)} placeholder="Username" />
<input type="password" value={password} onChange={(e) => setPassword(e.target.value)} placeholder="Password" />
<button onClick={handleLogin}>Login</button>
<button onClick={handleRegister}>Register</button>
</div>
);
}



