Basic Configuration
Next.config.js Configuration
The next.config.js file is used to customize the Next.js build configuration.
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: EnablesReact.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 check code style and 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 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 the tsconfig.json file.
// tsconfig.json
{
"compilerOptions": {
"baseUrl": "./src", // Set the source directory as the 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, which is 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...
};Use 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: The base 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 the project structure.
- Create the backend API.
- Implement OAuth 2.0 authorization.
- Generate and verify JWTs.
- Protect routes.
- Project structure.
Assume the project structure is as follows:
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());
passport.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.verifyToken, (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>
);
}



