Skip to main content

Command Palette

Search for a command to run...

next.js

Updated
6 min readView as Markdown
G

Experienced DevOps Engineer with expertise in CI/CD automation, cloud infrastructure, Kubernetes, and GitOps. Provisioned a Jenkins server on AWS EC2 for automated deployments, integrating Terraform to provision VPCs and EKS clusters. Configured a Jump Server for secure Kubernetes access and implemented ArgoCD for GitOps-driven deployments. Integrated SonarQube for static code analysis and enforced quality gates in Jenkins pipelines. Built AWS ECR repositories and automated Docker image management. Ensured security by managing secrets in Jenkins Credentials Manager and implementing IAM policies for AWS resources. Configured Kubernetes Ingress via ArgoCD and deployed MongoDB with persistence strategies. Designed multi-branch Jenkins pipelines for different environments. Installed Prometheus and Grafana for monitoring with automated alerts. Optimized costs using AWS CloudWatch and Lambda for unused resource cleanup. Ensured end-to-end automation, security, and observability.

nextjs-13 -> code

npx create-next-app@13
# name, typescript, 

npm i bootstrap react-bootstrap nextjs-progressbar

npm run dev

folder

  • pages --> routing, (ssr,ssg,isr)

  • components --> ui

  • models --> interface

routing (pages folder)

  • pages/index.ts (default page) --> localhost:3000

  • pages/search.ts -->localhost:3000/search

    pages/api --> backend endpoint

    pages/_app.ts --> root page(similar to <App /> in react.js)

  •     // pages/_app.ts
    
        // <Head> --> for all title , meta, icon 
        // Inter --> local downloaded font from google
        // '@/styles/globals.css'; --> can only be used in this page only
    
        import 'bootstrap/dist/css/bootstrap.min.css';
        import '@/styles/globals.css';
        import { Inter } from '@next/font/google';
        import type { AppProps } from 'next/app';
        import Head from 'next/head';
        import { Container } from 'react-bootstrap';
        import styles from "@/styles/App.module.css";
        import NavBar from '@/components/NavBar';
        import NextNProgress from "nextjs-progressbar";
    
        const inter = Inter({ subsets: ['latin'] });
    
        export default function App({ Component, pageProps }: AppProps) {
          return (
            <div className={inter.className}>
              <Head>
                <title key="title">NextJS News App</title>
                <meta name="description" key="description" content="NextJS crash course by Coding in Flow" />
                <meta name="viewport" content="width=device-width, initial-scale=1" />
                <link rel="icon" href="/favicon.ico" />
              </Head>
              <NextNProgress />
              <NavBar />
              <Container className={styles.pageContainer}>
                <Component {...pageProps} />
              </Container>
            </div>
          );
        }
    

    .env.local --> root --> to keep key secret


ssr --> getServerSideProps

NewsArticleEntry --> single card

index -->NewsArticlesGrid --> map ->NewsArticleEntry

// models/NewArtical.ts
export interface NewsArticle {
    author: string,
    title: string,
    description: string,
    url: string,
    urlToImage?: string,
    publishedAt: string,
    content: string,
}

export interface NewsResponse {
    articles: NewsArticle[],   // ->[{author:,title:,...},{},{}] --> arry of NewsArticle object 
}
//pages/index.ts --> localhost:3000
//<Head> --> to overwrite the title of _app.ts page
import NewsArticlesGrid from '@/components/NewsArticlesGrid';
import { NewsArticle, NewsResponse } from '@/models/NewsArticles';
import { GetServerSideProps } from 'next';
import Head from 'next/head';
import { Alert } from 'react-bootstrap';

interface BreakingNewsPageProps {
  newsArticles: NewsArticle[],
}

//ssr ->getServerSideProps --> better seo,faster load
// return props , so that we can use it --> newsArticles
export const getServerSideProps: GetServerSideProps<BreakingNewsPageProps> = async () => {
  const response = await fetch("https://newsapi.org/v2/top-headlines?country=us&apiKey=" + process.env.NEWS_API_KEY);
  const newsResponse: NewsResponse = await response.json();
  return {
    props: { newsArticles: newsResponse.articles }
  }
  // let error go to 500 page
}

export default function BreakingNewsPage({ newsArticles }: BreakingNewsPageProps) {
  return (
    <>
      <Head>
        <title key="title">Breaking News - NextJS News App</title>
      </Head>
      <main>
        <h1>Breaking News</h1>
        <Alert>
          This page uses <strong>getServerSideProps</strong> to fetch data server-side on every request.
          This allows search engines to crawl the page content and <strong>improves SEO</strong>.
        </Alert>
        <NewsArticlesGrid articles={newsArticles} />
      </main>
    </>
  )
}
// components/NewsArticleEntry.ts

// ssr,isr,ssg --> cant be used in component 
//<Image /> --> lazy loading,height and width optimization
import { NewsArticle } from "@/models/NewsArticles";
import Image from "next/image";
import { Card } from "react-bootstrap";
import placeholderImage from "@/assets/images/newsarticle_placeholder.jpg";
import styles from "@/styles/NewsArticleEntry.module.css";

interface NewsArticleEntryProps {
    article: NewsArticle,
}

const NewsArticleEntry = ({ article: { title, description, url, urlToImage } }: NewsArticleEntryProps) => {

    const validImageUrl = (urlToImage?.startsWith("http://") || urlToImage?.startsWith("https://")) ? urlToImage : undefined;

    return (
        <a href={url}>
            <Card className="h-100">
                <Image
                    src={validImageUrl || placeholderImage}
                    width={500}
                    height={200}
                    alt="News article image"
                    className={`card-img-top ${styles.image}`}
                />
                <Card.Body>
                    <Card.Title>{title}</Card.Title>
                    <Card.Text>{description}</Card.Text>
                </Card.Body>
            </Card>
        </a>
    );
}

export default NewsArticleEntry;
// next.config.js --> to load image from this -> www.si.com
/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  images: {
    domains: ["www.si.com"],
    remotePatterns: [
      {
        protocol: "https",
        hostname: "**",
      },
      {
        protocol: "http",
        hostname: "**",
      },
    ]
  }
}

module.exports = nextConfig
// components/NewsArticlesGrid.ts

// responsive --> <Row xs={1} sm={2} xl={3} ></Row>
import { NewsArticle } from "@/models/NewsArticles";
import { Col, Row } from "react-bootstrap";
import NewsArticleEntry from "./NewsArticleEntry";

interface NewsArticlesGridProps {
    articles: NewsArticle[],
}

const NewsArticlesGrid = ({ articles }: NewsArticlesGridProps) => {
    return (
        <Row xs={1} sm={2} xl={3} className="g-4">
            {articles.map(article => (
                <Col key={article.url}>
                    <NewsArticleEntry article={article} />
                </Col>
            ))}
        </Row>
    );
}

export default NewsArticlesGrid;

csr

// pages/search.ts --> localhost:3000/search
//csr
//swr library can also be used
//<Head> --> to overwrite the title of _app.ts page
import NewsArticlesGrid from "@/components/NewsArticlesGrid";
import { NewsArticle } from "@/models/NewsArticles";
import Head from "next/head";
import { FormEvent, useState } from "react";
import { Alert, Button, Form, Spinner } from "react-bootstrap";

const SearchNewsPage = () => {
    const [searchResults, setSearchResults] = useState<NewsArticle[] | null>(null);
    const [searchResultsLoading, setSearchResultsLoading] = useState(false);
    const [searchResultsLoadingIsError, setSearchResultsLoadingIsError] = useState(false);

    async function handleSubmit(e: FormEvent<HTMLFormElement>) {
        e.preventDefault();
        const formData = new FormData(e.target as HTMLFormElement);
        const searchQuery = formData.get("searchQuery")?.toString().trim();

        if (searchQuery) {
            try {
                setSearchResults(null);
                setSearchResultsLoadingIsError(false);
                setSearchResultsLoading(true);
                const response = await fetch("/api/search-news?q=" + searchQuery);
                const articles: NewsArticle[] = await response.json();
                setSearchResults(articles);
            } catch (error) {
                console.error(error);
                setSearchResultsLoadingIsError(true);
            } finally {
                setSearchResultsLoading(false);
            }
        }
    }

    return (
        <>
            <Head>
                <title key="title">Search News - NextJS News App</title>
            </Head>
            <main>
                <h1>Search News</h1>
                <Alert>
                    This is page uses <strong>client-side data fetching</strong> to show fresh data for every search.
                    Requests are handled by our backend via <strong>API routes</strong>.
                </Alert>
                <Form onSubmit={handleSubmit}>
                    <Form.Group className="mb-3" controlId="search-input">
                        <Form.Label>Search query</Form.Label>
                        <Form.Control
                            name="searchQuery"
                            placeholder="E.g. politics, sports, ..."
                        />
                    </Form.Group>
                    <Button type="submit" className="mb-3" disabled={searchResultsLoading}>
                        Search
                    </Button>
                </Form>
                <div className="d-flex flex-column align-items-center">
                    {searchResultsLoading && <Spinner animation="border" />}
                    {searchResultsLoadingIsError && <p>Something went wrong. Please try again.</p>}
                    {searchResults?.length === 0 && <p>Nothing found. Try a different query!</p>}
                    {searchResults && <NewsArticlesGrid articles={searchResults} />}
                </div>
            </main>
        </>
    );
}

export default SearchNewsPage;
// pages/api/search-news.ts

import { NewsResponse } from '@/models/NewsArticles';
import type { NextApiRequest, NextApiResponse } from 'next';

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  const searchQuery = req.query.q?.toString();

  if (!searchQuery) {
    return res.status(400).json({ error: "Please provide a search query" });
  }

  const response = await fetch(`https://newsapi.org/v2/everything?q=${searchQuery}&apiKey=${process.env.NEWS_API_KEY}`);
  const newsResponse: NewsResponse = await response.json();

  res.status(200).json(newsResponse.articles);
}

dynamic routing ->[] , (ssg -> getStaticPaths , isr --> revalidate: 5 * 60,) --> hell faster than ssr

//pages/categories/[category].ts --> localhost:3000/category/mamam
// useRouter --> router.query.category?.toString(); --> mamam

import NewsArticlesGrid from "@/components/NewsArticlesGrid";
import { NewsArticle, NewsResponse } from "@/models/NewsArticles";
import { GetStaticPaths, GetStaticProps } from "next";
import Head from "next/head";
import { useRouter } from "next/router";
import { Alert } from "react-bootstrap";

interface CategoryNewsPageProps {
    newsArticles: NewsArticle[],
}

export const getStaticPaths: GetStaticPaths = async () => {
    const categorySlugs = [ // this could be coming from an API
        "business",
        "entertainment",
        "general",
        "health",
        "science",
        "sports",
        "technology",
    ];

    const paths = categorySlugs.map(slug => ({ params: { category: slug } }));

    return {
        paths,
        fallback: false,
    }
}

export const getStaticProps: GetStaticProps<CategoryNewsPageProps> = async ({ params }) => {
    const category = params?.category?.toString();
    const response = await fetch(`https://newsapi.org/v2/top-headlines?country=us&category=${category}&apiKey=${process.env.NEWS_API_KEY}`);
    const newsResponse: NewsResponse = await response.json();
    return {
        props: { newsArticles: newsResponse.articles },
        revalidate: 5 * 60,
    }
    // let error go to 500 page
}

const CategoryNewsPage = ({ newsArticles }: CategoryNewsPageProps) => {
    const router = useRouter();
    const categoryName = router.query.category?.toString();

    const title = "Category: " + categoryName;

    return (
        <>
            <Head>
                <title key="title">{`${title} - NextJS News App`}</title>
            </Head>
            <main>
                <h1>{title}</h1>
                <Alert>
                    This is page uses <strong>getStaticProps</strong> for very high page loading speed
                    and <strong>incremental static regeneration</strong> to show data not older than <strong>5 minutes</strong>.
                </Alert>
                <NewsArticlesGrid articles={newsArticles} />
            </main>
        </>
    );
}

export default CategoryNewsPage;

customize 404 page

const NotFoundPage = () => {
    return ( 
        <div>
            <h1>Not Found</h1>
            <p>Looks like this page does not exist!</p>
        </div>
     );
}

export default NotFoundPage;

customize 500 page

const ErrorPage = () => {
    return ( 
        <div>
            <h1>Error 😵</h1>
            <p>Looks like something went wrong. Please refresh the page or contact support!</p>
        </div>
     );
}

export default ErrorPage;

navbar

//components/NavBar.ts
// <Link> --> navigate between pages --> 
// as={Link} --> since i want link feature and nav ui of bootstrap

import Link from "next/link";
import { Container, Nav, Navbar, NavDropdown } from "react-bootstrap";

const NavBar = () => {
    return (
        <Navbar bg="dark" variant="dark" sticky="top" expand="sm" collapseOnSelect>
            <Container>
                <Navbar.Toggle aria-controls="main-navbar" />
                <Navbar.Collapse id="main-navbar">
                    <Nav>
                        <Nav.Link as={Link} href="/">Breaking</Nav.Link>
                        <Nav.Link as={Link} href="/search">Search</Nav.Link>
                        <NavDropdown title="Categories" id="categories-dropdown">
                            <NavDropdown.Item as={Link} href="/categories/business">Business</NavDropdown.Item>
                            <NavDropdown.Item as={Link} href="/categories/entertainment">Entertainment</NavDropdown.Item>
                            <NavDropdown.Item as={Link} href="/categories/general">General</NavDropdown.Item>
                            <NavDropdown.Item as={Link} href="/categories/health">Health</NavDropdown.Item>
                            <NavDropdown.Item as={Link} href="/categories/science">Science</NavDropdown.Item>
                            <NavDropdown.Item as={Link} href="/categories/sports">Sports</NavDropdown.Item>
                            <NavDropdown.Item as={Link} href="/categories/technology">Technology</NavDropdown.Item>
                        </NavDropdown>
                    </Nav>
                </Navbar.Collapse>
            </Container>
        </Navbar>
    );
}

export default NavBar;

nextjs-14

seo in next.js14

migtate 13 to 14 --> /page to /app

ecommerce-nextjs 14