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=firstwe 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
/commentsin 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-Agentindicates browser and OSAccept: indicates content-type that client will understand and expects server to send it in 'Content-Type' in response headersAuthorization: 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.headersin its constructor -
there is a method named
headerswhich 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-Cookiefield 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
profileroute 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));
}
}
-
rewrites : map a new path to an existing app component path without displaying URL changes to the user. it will route the traffic while masking original path. its limitation is that it cant route traffic to external domain https://docs.digitalocean.com/products/app-platform/how-to/url-rewrites/
In
src/middleware.js
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));
// }
}