기본적으로 pages/api 폴더 안에서 바로 api를 구현할 수 있는 기능을 제공하고 있음.
pages/api/hello.ts에 다음 코드를 작성했다고 하면 ~~/api/hello로 접속했을 때 해당 api가 제공하는 정보를 받아볼 수 있다.
export default function handler(req, res) {
res.status(200).json({ name: 'John Doe' })
}
공식 문서에서 말하기를, API route가 작동하기 위해서는 function을 default로 export해야한다. 이때 export되는 함수를 request handler라고 부르는 것 같다. 또한 request handler는 req, res를 파라미터로 불러와야한다.
다른 HTTP 메소드를 다루기 위해서는, req.method를 request handler에서 사용해주면 된다.
export default function handler(req,res) {
if (req.method === 'POST') {
// Process a POST request
} else {
// Handle and other HTTP method
}
}
api를 Next Project 내부에서 사용할 경우 그냥 /api/파일명 형태로 입력해주면 된다.
api route도 dynamic routing을 지원한단다. 이것도 마찬가지로 파일명을 통해 구현할 수 있다.
pages/api/post/[pid].js
export default function handler(req,res){
const { pid } = req.query;
res.end(`Post: ${pid}`);
}
Catch all API routes
점 세 개를 찍으면 그 요소 안에 있는 모든 element에 대한 path에 모두 접근할 수 있다.
pages/api/post/[…slug].js → /api/post/a에도 접근할 수 있고, api/post/a/b에도 접근할 수 있고, api/post/a/b/c에도 접근할 수 있다.
url을 api/post/a/b라고 입력하면 slug은 { “slug”: [”a”, “b”] }와 같은 형태로 저장됨.
url을 api/post/a/b로 입력했을 경우
export default function handler(req,res){
const { slug } = req.query;
res.end(`Post : ${slug.join(', ')});
}
ㄹㅏ고 하면 Post : a, b, c라는 데이터가 출려됨.
Optional catch all API routes
모든 route를 catch하는 것을 선택적으로 하는 방법에 대한 설명이다.
→ 대괄호로 한 번 더 감싸면 된다.
[[…slug]]과 같은 형태로 사용하면 된다.
위에 있는 catch all과 다른 점은 매개 변수가 없는 경로도 일치한다는 점? 이다.
→ /api/post로만 입력했을 때. 즉, slug에 아무 데이터가 없을때도 동작한다!
{ } // GET `/api/post` (empty object)
{ "slug": ["a"] } // `GET /api/post/a` (single-element array)
{ "slug": ["a", "b"] } // `GET /api/post/a/b` (multi-element arra
API Route는 내장 request helper를 제공한다.
Custom config
모든 API Route는 config
object를 export할 수 있다. → 초기 상태를 바꾸고자 할 때 씀.
export const config = {
api: {
bodyParser: {
sizeLimit: '1mb',
}
}
}
api 객체는 모든 config 옵션(API Route에서 사용가능 한)들을 포함한다.
bodyParser
는 자동으로 enabled되어있다. 원한다면 false
로 설정할 수 있음.
bodyParser를 false로 설정하는 예시 중 하나는 webhook request의 raw body를 확인하고자 할때?
export const config = {
api: {
bodyParser: false,
},
}
bodyParser.sizeLimit
는 parse된 body의 최대 사이즈를 설정해준다. 바이트 단위로 된 크기를 입력해주면 됨.
export const config = {
api: {
bodyParser: {
sizeLimit: '500kb',
},
},
}
externalResolver는 이 route가 express 또는 connect와 같은 외부 resolver에 의해 처리되고 있음을 서버에 알리는 명시적 flag이다. 이 옵션을 활성화하면 확인되지 않은 요청에 대한 경고가 비활성화된다.
export const config = {
api: {
externalResolver: true,
},
}
responseLimit
은 처음에는 자동으로 4MB로 설정되어있음.
만약 serverless 환경에서 Next.js를 사용하지 않고있고 CDN을 사용하지 않을 때의 퍼포먼스 영향을 이해하고 있다면, 이 제한을 false
로 둘 수 있다.
export const config = {
api: {
responseLimit: false,
},
}
responseLimit
도 또한 byte단위로 설정해줄 수 있다. false 자리에 ‘8mb’같은 문자열을 넣어주면 됨.
여기서 객체의 확장이라 함은 객체가 기본적으로 포함하고 있는 property에 새로운 property를 추가하는 것을 의미하는 것으로 보인다.
더 나은 type-safety를 위해서, req와 res 객체를 확정하는 것은 추천되지 않는다.
하지만 객체의 확장을 피할 수 없다면, 새로운 type을 새로 만들어야한다.
// pages/api/foo.ts
import { NextApiRequest, NextApiResponse } from 'next';
import { withFoo } from 'external-lib-foo';
type NextApiRequestWithFoo = NextApiRequest & {
foo: (bar: string) => void
}
const handler = (req: NextApiRequestWithFoo, res: NextApiResponse) => {
req.foo('bar');
res.end('ok');
}
export default withFoo(handler)
response 객체에는 Experss와 비슷한 helper메소드가 있다.
getStaticProps
urlPath는 반드시 문자열 이어야 함. → “Revalidate a page on demand”는 차차 공부해보는 것으로 하자. → 캐시와 비슷한 의미인 것 같기도?다음은 위 메소에 대한 예시 코드임.
export default function handler(req, res) {
res.status(200).json({ message: 'Hello from Next.js!' })
}
export default async function handler(req, res) {
try {
const result = await someAsyncOperation()
res.status(200).send({ result })
} catch (err) {
res.status(500).send({ error: 'failed to fetch data' })
}
}
export default async function handler(req, res) {
const { name, message } = req.body
try {
await handleFormInputAsync({ name, message })
res.redirect(307, '/')
} catch (err) {
res.status(500).send({ error: 'failed to fetch data' })
}
}
Edge API Routes는 Next.js에서 High 퍼모먼스 API를 만들 수 있게 해준다.
Edge Runtime을 사용하면, 웬만한 Node.js 베이스의 API보다 속도가 빠르다.
(pages/api에 있는 모든 파일은 페이지가 아닌 API endpoint로 여겨진다. 이것은 server-side 번들이고 client-size 번들에 영향을 끼치지 않는다.
이렇게 설정해주면 edge runtime으로 사용할 수 있다.
export const config = {
runtime: 'edge',
}
export default (req) => new Response('Hello world!')
json data 응답
import type { NextRequest } from 'next/server'
export const config = {
runtime: 'edge',
}
export default async function handler(req: NextRequest) {
return new Response(
JSON.stringify({
name: 'Jim Halpert',
}),
{
status: 200,
headers: {
'content-type': 'application/json',
},
}
)
}
cache-control
import type { NextRequest } from 'next/server'
export const config = {
runtime: 'edge',
}
export default async function handler(req: NextRequest) {
return new Response(
JSON.stringify({
name: 'Jim Halpert',
}),
{
status: 200,
headers: {
'content-type': 'application/json',
'cache-control': 'public, s-maxage=1200, stale-while-revalidate=600',
},
}
)
}
쿼리 파라미터 받아오기
import type { NextRequest } from 'next/server'
export const config = {
runtime: 'edge',
}
export default async function handler(req: NextRequest) {
const { searchParams } = new URL(req.url)
const email = searchParams.get('email')
return new Response(email)
}
엣지 api route는 edge runtime을 사용해서 node.js 런타임을 사용한 api route보다 빠르다.
엣지 라우트는 서버로부터의 응답을 분산시키고 파일 캐시 이후에 실행된다.
server-side streaming은 Time to First Byte (TTFB)라는 기술을 사용해서 퍼포먼스를 좋게 만들 수 있다.
TTFB에 대해서는 나중에 공부해보도록 하자.