408 lines
11 KiB
Markdown
408 lines
11 KiB
Markdown
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`
|
|
```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
|
|
```text
|
|
src/app
|
|
flashboard
|
|
api
|
|
route.jsx
|
|
page.jsx
|
|
```
|
|
|
|
|
|
**GET Request**
|
|
|
|
in `src/app/comments/data.js`
|
|
```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`
|
|
```js
|
|
import { comments } from "./data";
|
|
|
|
export async function GET() {
|
|
return Response.json(comments);
|
|
}
|
|
|
|
```
|
|
|
|
|
|
**POST Request**
|
|
|
|
in `src/app/comments/route.js`
|
|
```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
|
|
```text
|
|
src/app
|
|
comments
|
|
[id]
|
|
route.js
|
|
route.js
|
|
```
|
|
|
|
in `src/app/comments/[id]/route.js`
|
|
```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`
|
|
```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`
|
|
```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`
|
|
```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
|
|
```js
|
|
"use client";
|
|
import { useRouter } from "next/navigation";
|
|
|
|
const router = useRouter()
|
|
router.push('/comments')
|
|
|
|
```
|
|
to navigate `/comments` in server side
|
|
```js
|
|
import {redirect} from "next/navigation";
|
|
redirect('/comments');
|
|
|
|
```
|
|
|
|
In `src/app/comments/[id]/route.js`
|
|
|
|
```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`
|
|
```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`
|
|
|
|
```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`
|
|
```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
|
|
|
|
```js
|
|
// 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
|
|
```js
|
|
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
|
|
```js
|
|
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`
|
|
```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`
|
|
```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));
|
|
// }
|
|
}
|
|
```
|
|
|
|
|