joplin-notes/joplin notion extension/Web Development/React/next/Coding 7(RESTful Route handlers).md
2026-11-04 00:33:23 +05:30

11 KiB

Coding 7(RESTful Route handlers)

  • Unlike page routes, route handlers allow us to create RESTful endpoints, giving us full control over the response.
  • We dont have to create and configure a separate server for that
  • For making external API requests
  • For securing private keys

In src/app/flashboard/route.js

export async function GET() {
  return new Response("flash board api data");
}

when visited to page localhost:3000/flashboard then it will represent this response

If there is page.jsx too like in src/app/flashboard/page.js then in this case page.jsx will take precedence over route.js and page.jsx content will be displayed. To avoid this make sure that page.jsx and route.jsx is never in same folder/route. So make sure to have different folder (say api) under flashboard folder and add route.jsx to that

src/app
	flashboard
		api
			route.jsx
		page.jsx

 

GET Request

in src/app/comments/data.js

export const comments = [
  {
    id: 1,
    text: "This is my first comment",
  },
  {
    id: 2,
    text: "This is my second comment",
  },
  {
    id: 3,
    text: "This is my third comment",
  },
];

in src/app/comments/route.js

import { comments } from "./data";

export async function GET() {
  return Response.json(comments);
}

 

POST Request

in src/app/comments/route.js

export async function POST(request) {
  const comment = await request.json();

  const newcomment = {
    id: comments.length + 1,
    text: comment,
  };

  comments.push(newcomment);

  return new Response(JSON.stringify(newcomment), {
    headers: {
      "Content-Type": "application/json",
    },
    status: 201,
  });
}

Every POST route handler recieved a request in form of object  

Dynamic Route Handler

  • for dynamic routes like localhost:3000/comments/:id

Structure

src/app
	comments
	[id]
		route.js
	route.js

in src/app/comments/[id]/route.js

import { comments } from "../data";

export async function GET(request, context) {
  const { params } = context;

  const comment = comments.find(
    (comment) => comment.id === parseInt(params.id)
  );

  return Response.json(comments);
}

Here we have other parameter which is a object that contains params  

Handling PATCH request

in src/app/comments/[id]/route.js

export async function PATCH(request, { params }) {
  const body = await request.json();

  const { text } = body;

  console.log("text: ", text);

  const index = comments.findIndex(
    (comment) => comment.id === parseInt(params.id)
  );

  console.log("index : ", index);

  comments[index]["text"] = text;

  return Response.json(comments[index]);
}

 

Handling DELETE request

in src/app/comments/[id]/route.js

export async function DELETE(request, { params }) {
  const index = comments.findIndex(
    (comment) => comment.id === parseInt(params.id)
  );

  const deletedContext = comments[index];

  comments.splice(index, 1);

  return Response.json(deletedContext);
}

 

URL Query parameters

  • To handle and parse query parameters like localhost:3001/comments?s=first we need to parameter in GET method.
  • The paramater is of type NextRequest and contains nextUrl and this contains searchParams field too. searchParams field contains get method through which we can get values of query parameters

In src/app/comments/route.js

export async function GET(request) {
  const searchParams = request.nextUrl.searchParams;

  const query = searchParams.get("s");

  // if query is not null then only filter else print all comments
  const filteredComments = query
    ? comments.filter((comment) => comment.text.includes(query))
    : comments;

  return Response.json(filteredComments);
}

 

Redirects in URL

  • Just like useRouter. useRouter is for client side and redirect is for server side. to navigate to /comments in client side
      "use client";
      import { useRouter } from "next/navigation";
    
      const router = useRouter()
      router.push('/comments')
    
    

to navigate /comments in server side

  import {redirect} from "next/navigation";
  redirect('/comments');
  

In src/app/comments/[id]/route.js

export async function GET(request, context) {
  const { params } = context;
  // if id is invalid then redirects
  if (parseInt(params.id) > comments.length) {
    redirect("/comments");
  }

  const comment = comments.find(
    (comment) => comment.id === parseInt(params.id)
  );

  return Response.json(comment);
}
  • When using this it sends status code as 307 which means for redirect  

Headers in Route Handlers

  • HTTP headers represent the metadata associated with an API request and response

  • Request Headers

    • Sent by client to server containing info about request so server process it accordingly
    • User-Agent indicates browser and OS
    • Accept : indicates content-type that client will understand and expects server to send it in 'Content-Type' in response headers
    • Authorization : fr authenticate itself to server. Eg : Bearer token
  • Reponse Headers

    • Sent by server to client containing info abour server and data sent in reponse
    • Content-Type : indicates media type of response. eg : 'application/json', 'text/plain', 'text/html'.
  • Headers Class can be used to create object after passing req.headers in its constructor

  • there is a method named headers which can be imported from 'next/headers'

In src/app/flashboard/api/route.js

import { headers } from "next/headers";

export async function GET(request) {
  const reqHeaders = new Headers(request.headers);
  const headerList = headers();

  console.log(reqHeaders.get("Authorization"));
  console.log(headerList.get("Authorization"));

  return new Response("<h1> flash board api data </h1>", {
    headers: {
      // "Content-Type": "text/html",
      "Content-Type": "text/plain",
    },
  });
}

 

Cookies in route handlers

  • Cookies are piece of data server sends of web browser
  • Browser may store cookie and send it back to same server with later requests
  • Purposes
    • session management like logins and shopping carts
    • Personalization like user preferences and themes
    • Tracking like recording and anakyzing user behavior
  • It can be defined by adding Set-Cookie field in headers and value if the form of <name>=<value>
  • There r 2 ways. one is through standard way ie using request.cookies.get('<cookie_name>') and other is using cookies method from 'next/headers' module. this method will return an object which has get and set methods

In src/app/flashboard/api/route.js

import { headers, cookies } from "next/headers";

export async function GET(request) {
  const reqHeaders = new Headers(request.headers);
  const headerList = headers();

  cookies().set("resultsPerPage", "20");
  const theme = request.cookies.get("theme");
  const resultsPerPage = request.cookies.get("resultsPerPage");

  console.log(reqHeaders.get("Authorization"));
  console.log(headerList.get("Authorization"));

  console.log(theme);
  console.log("resultsperpage: ", cookies().get("resultsPerPage"));
  // console.log(resultsPerPage);

  return new Response("<h1> flash board api data </h1>", {
    headers: {
      // "Content-Type": "text/html",
      "Content-Type": "text/plain",
      "Set-Cookie": "theme=dark",
    },
  });
}

 

Caching in Route Handlers

  • Route Handlers are cached by default when using GET method with response object in Nextjs

In src/app/time/route.js

export async function GET() {
  const time = new Date().toLocaleTimeString();

  return Response.json({ time });
}

In this one it will show try to cache the time so after the first request when we send this request localhost:3000/time result will be cached and will not be updated after furthur load

  • How to opt out of caching
    • dynamic mode in Segment Config Option
    • using Request object with GET method
    • employing dynamic functions like headers() and cookies()
    • using any HTTP method other than GET
// by default dynamic is 'auto'
export const dynamic = "force-dynamic";

export async function GET() {
  const time = new Date().toLocaleTimeString();

  return Response.json({ time });
}

 

Middleware

  • Middleware in Next.js is powerful feature for intercepting and controlling flow of requests and responses within applications
  • Does all of this at global level which significantly ehance feature like redirection, URL rewrites, authentication, headers and cookies management and more.
  • Middleware allows to specify paths where it will be active
    • Custom matcher config (like switch case)
    • Conditional statements (if/else type)

In src/middleware.js Custom matcher config

import { NextResponse } from "next/server";

export function middleware(request) {
  return NextResponse.redirect(new URL("/", request.url));
}

export const config = {
  matcher: "/profile",
};

this will redirect every request whole path is /profile to /

  • this redirection can be done in profile route itself but point of using middleware is change it without profile route is reached. Maybe that route has more code to be exeuted and we dont wanna temper with it so using middleware solved this issue or maybe that route dont exist or requires authentication

Conditional statements

import { NextResponse } from "next/server";

export function middleware(request) {
  if (request.nextUrl.pathname === "/profile") {
    return NextResponse.redirect(new URL("/hello", request.url));
  }
}

import { NextResponse } from "next/server";

export function middleware(request) {
  if (request.nextUrl.pathname === "/profile") {
    return NextResponse.rewrite(new URL("/hello", request.url));
  }
}

so url will be displayed as localhost:3000/profile but content will be of localhost:3000/hello

  • Cookies and Headers : Cookies and headers can also be added to middleware. Advantage is being setting them for global level for all routes

In src/middleware.js

import { NextResponse } from "next/server";

export function middleware(request) {
  // refers to next response in middleware chain as reponse is to be send to next middleware or route handler is
  // current middleware is last one
  const response = NextResponse.next();

  const themePreference = request.cookies.get("theme");

  if (!themePreference) {
    response.cookies.set("theme", "dark");
  }

  response.headers.set("custom-header", "custom-value");

  return response;

  //   if (request.nextUrl.pathname === "/profile") {
  //     return NextResponse.rewrite(new URL("/hello", request.url));
  //   }
}