From 513d42403eb3905aed9b6d0b172d21057b40363e Mon Sep 17 00:00:00 2001 From: Arindam Majumder <109217591+Arindam200@users.noreply.github.com> Date: Thu, 25 Jul 2024 02:35:46 +0530 Subject: [PATCH 01/50] add: headless-api Signed-off-by: Arindam Majumder --- packages/create-next-app/create-app.ts | 11 ++++- .../templates/api/js/.env.example | 3 ++ .../templates/api/js/api/index.js | 3 ++ .../templates/api/js/eslintrc.json | 3 ++ .../templates/api/js/gitignore | 40 +++++++++++++++++++ .../templates/api/js/jsconfig.json | 7 ++++ .../templates/api/js/next.config.mjs | 4 ++ .../templates/api/ts/.env.example | 3 ++ .../templates/api/ts/api/index.ts | 5 +++ .../templates/api/ts/eslintrc.json | 3 ++ .../templates/api/ts/gitignore | 40 +++++++++++++++++++ .../templates/api/ts/next-env.d.ts | 5 +++ .../templates/api/ts/next.config.ts | 7 ++++ .../templates/api/ts/tsconfig.json | 27 +++++++++++++ packages/create-next-app/templates/index.ts | 1 + packages/create-next-app/templates/types.ts | 3 ++ 16 files changed, 163 insertions(+), 2 deletions(-) create mode 100644 packages/create-next-app/templates/api/js/.env.example create mode 100644 packages/create-next-app/templates/api/js/api/index.js create mode 100644 packages/create-next-app/templates/api/js/eslintrc.json create mode 100644 packages/create-next-app/templates/api/js/gitignore create mode 100644 packages/create-next-app/templates/api/js/jsconfig.json create mode 100644 packages/create-next-app/templates/api/js/next.config.mjs create mode 100644 packages/create-next-app/templates/api/ts/.env.example create mode 100644 packages/create-next-app/templates/api/ts/api/index.ts create mode 100644 packages/create-next-app/templates/api/ts/eslintrc.json create mode 100644 packages/create-next-app/templates/api/ts/gitignore create mode 100644 packages/create-next-app/templates/api/ts/next-env.d.ts create mode 100644 packages/create-next-app/templates/api/ts/next.config.ts create mode 100644 packages/create-next-app/templates/api/ts/tsconfig.json diff --git a/packages/create-next-app/create-app.ts b/packages/create-next-app/create-app.ts index fa53858f359e2..575716332d99d 100644 --- a/packages/create-next-app/create-app.ts +++ b/packages/create-next-app/create-app.ts @@ -37,6 +37,7 @@ export async function createApp({ skipInstall, empty, turbo, + api, // Add the api flag here }: { appPath: string packageManager: PackageManager @@ -51,11 +52,17 @@ export async function createApp({ skipInstall: boolean empty: boolean turbo: boolean + api: boolean // Add the api flag here }): Promise { let repoInfo: RepoInfo | undefined const mode: TemplateMode = typescript ? 'ts' : 'js' - const template: TemplateType = `${appRouter ? 'app' : 'default'}${tailwind ? '-tw' : ''}${empty ? '-empty' : ''}` + const template: TemplateType = api ? 'api' : `${appRouter ? 'app' : 'default'}${tailwind ? '-tw' : ''}${empty ? '-empty' : ''}` // Modify template selection logic + if (api && template !== 'api') { + console.error(`Invalid template for api flag: ${red(template)}`) + process.exit(1) + } + if (example) { let repoUrl: URL | undefined @@ -188,7 +195,7 @@ export async function createApp({ isErrorLike(reason) ? reason.message : reason + '' ) } - // Copy `.gitignore` if the application did not provide one + // Copy `.gitignore` if the application did not provide one const ignorePath = join(root, '.gitignore') if (!existsSync(ignorePath)) { copyFileSync( diff --git a/packages/create-next-app/templates/api/js/.env.example b/packages/create-next-app/templates/api/js/.env.example new file mode 100644 index 0000000000000..ce586fad292cd --- /dev/null +++ b/packages/create-next-app/templates/api/js/.env.example @@ -0,0 +1,3 @@ +# Rename this file to `.env.local` to use environment variables locally with `next dev` +# https://nextjs.org/docs/app/building-your-application/configuring/environment-variables +MY_HOST="example.com" diff --git a/packages/create-next-app/templates/api/js/api/index.js b/packages/create-next-app/templates/api/js/api/index.js new file mode 100644 index 0000000000000..fbe9ccef4523f --- /dev/null +++ b/packages/create-next-app/templates/api/js/api/index.js @@ -0,0 +1,3 @@ +export default (req, res) => { + return res.json({ message: "Hello World" }); +}; \ No newline at end of file diff --git a/packages/create-next-app/templates/api/js/eslintrc.json b/packages/create-next-app/templates/api/js/eslintrc.json new file mode 100644 index 0000000000000..bffb357a71225 --- /dev/null +++ b/packages/create-next-app/templates/api/js/eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "next/core-web-vitals" +} diff --git a/packages/create-next-app/templates/api/js/gitignore b/packages/create-next-app/templates/api/js/gitignore new file mode 100644 index 0000000000000..26b002aac1dd1 --- /dev/null +++ b/packages/create-next-app/templates/api/js/gitignore @@ -0,0 +1,40 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# env files (can opt-in for commiting if needed) +.env* + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/packages/create-next-app/templates/api/js/jsconfig.json b/packages/create-next-app/templates/api/js/jsconfig.json new file mode 100644 index 0000000000000..2a2e4b3bf8ba1 --- /dev/null +++ b/packages/create-next-app/templates/api/js/jsconfig.json @@ -0,0 +1,7 @@ +{ + "compilerOptions": { + "paths": { + "@/*": ["./*"] + } + } +} diff --git a/packages/create-next-app/templates/api/js/next.config.mjs b/packages/create-next-app/templates/api/js/next.config.mjs new file mode 100644 index 0000000000000..4678774e6d606 --- /dev/null +++ b/packages/create-next-app/templates/api/js/next.config.mjs @@ -0,0 +1,4 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = {}; + +export default nextConfig; diff --git a/packages/create-next-app/templates/api/ts/.env.example b/packages/create-next-app/templates/api/ts/.env.example new file mode 100644 index 0000000000000..ce586fad292cd --- /dev/null +++ b/packages/create-next-app/templates/api/ts/.env.example @@ -0,0 +1,3 @@ +# Rename this file to `.env.local` to use environment variables locally with `next dev` +# https://nextjs.org/docs/app/building-your-application/configuring/environment-variables +MY_HOST="example.com" diff --git a/packages/create-next-app/templates/api/ts/api/index.ts b/packages/create-next-app/templates/api/ts/api/index.ts new file mode 100644 index 0000000000000..56c734efc6caf --- /dev/null +++ b/packages/create-next-app/templates/api/ts/api/index.ts @@ -0,0 +1,5 @@ +import { VercelRequest, VercelResponse } from "@vercel/node"; + +export default (req: VercelRequest, res: VercelResponse) => { + return res.json({ message: "Hello World" }); +}; \ No newline at end of file diff --git a/packages/create-next-app/templates/api/ts/eslintrc.json b/packages/create-next-app/templates/api/ts/eslintrc.json new file mode 100644 index 0000000000000..37224185490e6 --- /dev/null +++ b/packages/create-next-app/templates/api/ts/eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": ["next/core-web-vitals", "next/typescript"] +} diff --git a/packages/create-next-app/templates/api/ts/gitignore b/packages/create-next-app/templates/api/ts/gitignore new file mode 100644 index 0000000000000..26b002aac1dd1 --- /dev/null +++ b/packages/create-next-app/templates/api/ts/gitignore @@ -0,0 +1,40 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# env files (can opt-in for commiting if needed) +.env* + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/packages/create-next-app/templates/api/ts/next-env.d.ts b/packages/create-next-app/templates/api/ts/next-env.d.ts new file mode 100644 index 0000000000000..4f11a03dc6cc3 --- /dev/null +++ b/packages/create-next-app/templates/api/ts/next-env.d.ts @@ -0,0 +1,5 @@ +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/packages/create-next-app/templates/api/ts/next.config.ts b/packages/create-next-app/templates/api/ts/next.config.ts new file mode 100644 index 0000000000000..e9ffa3083ad27 --- /dev/null +++ b/packages/create-next-app/templates/api/ts/next.config.ts @@ -0,0 +1,7 @@ +import type { NextConfig } from "next"; + +const nextConfig: NextConfig = { + /* config options here */ +}; + +export default nextConfig; diff --git a/packages/create-next-app/templates/api/ts/tsconfig.json b/packages/create-next-app/templates/api/ts/tsconfig.json new file mode 100644 index 0000000000000..d8b93235f205e --- /dev/null +++ b/packages/create-next-app/templates/api/ts/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "target": "ES2017", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./*"] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules"] +} diff --git a/packages/create-next-app/templates/index.ts b/packages/create-next-app/templates/index.ts index 1d54f8ae0e63c..82ab5897299db 100644 --- a/packages/create-next-app/templates/index.ts +++ b/packages/create-next-app/templates/index.ts @@ -40,6 +40,7 @@ export const installTemplate = async ({ importAlias, skipInstall, turbo, + api, }: InstallTemplateArgs) => { console.log(bold(`Using ${packageManager}.`)); diff --git a/packages/create-next-app/templates/types.ts b/packages/create-next-app/templates/types.ts index 1837495ab8a08..8fdb80d24a0e6 100644 --- a/packages/create-next-app/templates/types.ts +++ b/packages/create-next-app/templates/types.ts @@ -2,6 +2,7 @@ import { PackageManager } from "../helpers/get-pkg-manager"; export type TemplateType = | "app" + | "api" | "app-empty" | "app-tw" | "app-tw-empty" @@ -19,6 +20,7 @@ export interface GetTemplateFileArgs { export interface InstallTemplateArgs { appName: string; + api:boolean; root: string; packageManager: PackageManager; isOnline: boolean; @@ -31,3 +33,4 @@ export interface InstallTemplateArgs { skipInstall: boolean; turbo: boolean; } +j \ No newline at end of file From 63fb4717fc0f96c4ff35d32b709177dd0d1ef169 Mon Sep 17 00:00:00 2001 From: Arindam Majumder <109217591+Arindam200@users.noreply.github.com> Date: Thu, 25 Jul 2024 02:51:18 +0530 Subject: [PATCH 02/50] add: Readme Templates --- packages/create-next-app/create-app.ts | 4 +-- .../templates/api/js/README-template.md | 34 +++++++++++++++++++ .../templates/api/ts/README-template.md | 34 +++++++++++++++++++ 3 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 packages/create-next-app/templates/api/js/README-template.md create mode 100644 packages/create-next-app/templates/api/ts/README-template.md diff --git a/packages/create-next-app/create-app.ts b/packages/create-next-app/create-app.ts index 575716332d99d..9f5ffbfe5ea01 100644 --- a/packages/create-next-app/create-app.ts +++ b/packages/create-next-app/create-app.ts @@ -37,7 +37,7 @@ export async function createApp({ skipInstall, empty, turbo, - api, // Add the api flag here + api, }: { appPath: string packageManager: PackageManager @@ -52,7 +52,7 @@ export async function createApp({ skipInstall: boolean empty: boolean turbo: boolean - api: boolean // Add the api flag here + api: boolean }): Promise { let repoInfo: RepoInfo | undefined const mode: TemplateMode = typescript ? 'ts' : 'js' diff --git a/packages/create-next-app/templates/api/js/README-template.md b/packages/create-next-app/templates/api/js/README-template.md new file mode 100644 index 0000000000000..51e5207ba7d8f --- /dev/null +++ b/packages/create-next-app/templates/api/js/README-template.md @@ -0,0 +1,34 @@ +This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/create-next-app). + +## Getting Started + +First, run the development server: + +```bash +npm run dev +# or +yarn dev +# or +pnpm dev +# or +bun dev +``` + +Open [http://localhost:3000/api](http://localhost:3000/api) with your browser to see the result. + +You can start editing the page by modifying `app/index.js`. The page auto-updates as you edit the file. + +## Learn More + +To learn more about Next.js, take a look at the following resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. + +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! + +## Deploy on Vercel + +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. diff --git a/packages/create-next-app/templates/api/ts/README-template.md b/packages/create-next-app/templates/api/ts/README-template.md new file mode 100644 index 0000000000000..bfdf39966e848 --- /dev/null +++ b/packages/create-next-app/templates/api/ts/README-template.md @@ -0,0 +1,34 @@ +This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/create-next-app). + +## Getting Started + +First, run the development server: + +```bash +npm run dev +# or +yarn dev +# or +pnpm dev +# or +bun dev +``` + +Open [http://localhost:3000/api](http://localhost:3000/api) with your browser to see the result. + +You can start editing the page by modifying `app/index.ts`. The page auto-updates as you edit the file. + +## Learn More + +To learn more about Next.js, take a look at the following resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. + +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! + +## Deploy on Vercel + +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. From 632b11baba9a8f22641516fe98c95a5d18d50115 Mon Sep 17 00:00:00 2001 From: Arindam Majumder <109217591+Arindam200@users.noreply.github.com> Date: Thu, 25 Jul 2024 11:19:58 +0530 Subject: [PATCH 03/50] fix: --api flag --- packages/create-next-app/create-app.ts | 1 + packages/create-next-app/index.ts | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/packages/create-next-app/create-app.ts b/packages/create-next-app/create-app.ts index 9f5ffbfe5ea01..e381505be7b67 100644 --- a/packages/create-next-app/create-app.ts +++ b/packages/create-next-app/create-app.ts @@ -239,6 +239,7 @@ export async function createApp({ importAlias, skipInstall, turbo, + api, }) } diff --git a/packages/create-next-app/index.ts b/packages/create-next-app/index.ts index 1d068ac42d051..56be84031ed85 100644 --- a/packages/create-next-app/index.ts +++ b/packages/create-next-app/index.ts @@ -97,6 +97,12 @@ const program = new Command(packageJson.name) ` Specify import alias to use (default "@/*"). +` + ) + .option( + '--api', + ` + Initialize a Headless API. ` ) .option( @@ -484,6 +490,7 @@ async function run(): Promise { skipInstall: program.skipInstall, empty: program.empty, turbo: program.turbo, + api: program.api, }) } catch (reason) { if (!(reason instanceof DownloadError)) { From 393b43264588f09af760a8a702df2b71ece5a730 Mon Sep 17 00:00:00 2001 From: Arindam Majumder <109217591+Arindam200@users.noreply.github.com> Date: Thu, 25 Jul 2024 16:50:44 +0530 Subject: [PATCH 04/50] Update packages/create-next-app/create-app.ts Co-authored-by: Mert Can Altin --- packages/create-next-app/create-app.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/create-next-app/create-app.ts b/packages/create-next-app/create-app.ts index e381505be7b67..100cfb0d4df1d 100644 --- a/packages/create-next-app/create-app.ts +++ b/packages/create-next-app/create-app.ts @@ -56,7 +56,10 @@ export async function createApp({ }): Promise { let repoInfo: RepoInfo | undefined const mode: TemplateMode = typescript ? 'ts' : 'js' - const template: TemplateType = api ? 'api' : `${appRouter ? 'app' : 'default'}${tailwind ? '-tw' : ''}${empty ? '-empty' : ''}` // Modify template selection logic + const template: TemplateType = api + ? 'api' + : `${appRouter ? 'app' : 'default'}${tailwind ? '-tw' : ''}${empty ? '-empty' : ''}`; + if (api && template !== 'api') { console.error(`Invalid template for api flag: ${red(template)}`) From 76057cd927168b3f4738f7a2ea31d7d3767b6ae1 Mon Sep 17 00:00:00 2001 From: Arindam Majumder <109217591+Arindam200@users.noreply.github.com> Date: Sat, 27 Jul 2024 02:20:22 +0530 Subject: [PATCH 05/50] Update packages/create-next-app/templates/types.ts Co-authored-by: Jiwon Choi --- packages/create-next-app/templates/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/create-next-app/templates/types.ts b/packages/create-next-app/templates/types.ts index 8fdb80d24a0e6..c1f7ec8b1191c 100644 --- a/packages/create-next-app/templates/types.ts +++ b/packages/create-next-app/templates/types.ts @@ -20,7 +20,7 @@ export interface GetTemplateFileArgs { export interface InstallTemplateArgs { appName: string; - api:boolean; + api: boolean; root: string; packageManager: PackageManager; isOnline: boolean; From a0742b5561eb313f28a4fe44cc8edd7576f74cb1 Mon Sep 17 00:00:00 2001 From: Arindam Majumder <109217591+Arindam200@users.noreply.github.com> Date: Sat, 27 Jul 2024 02:20:35 +0530 Subject: [PATCH 06/50] Update packages/create-next-app/templates/types.ts Co-authored-by: Jiwon Choi --- packages/create-next-app/templates/types.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/create-next-app/templates/types.ts b/packages/create-next-app/templates/types.ts index c1f7ec8b1191c..cbd4c78aad0c7 100644 --- a/packages/create-next-app/templates/types.ts +++ b/packages/create-next-app/templates/types.ts @@ -32,5 +32,4 @@ export interface InstallTemplateArgs { importAlias: string; skipInstall: boolean; turbo: boolean; -} -j \ No newline at end of file +} \ No newline at end of file From d837d4d8c653845c7eec2e2d278939be8050beb4 Mon Sep 17 00:00:00 2001 From: Arindam Majumder <109217591+Arindam200@users.noreply.github.com> Date: Sat, 27 Jul 2024 02:20:55 +0530 Subject: [PATCH 07/50] Update packages/create-next-app/create-app.ts Co-authored-by: Jiwon Choi --- packages/create-next-app/create-app.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/create-next-app/create-app.ts b/packages/create-next-app/create-app.ts index 100cfb0d4df1d..b653ea6888a99 100644 --- a/packages/create-next-app/create-app.ts +++ b/packages/create-next-app/create-app.ts @@ -198,7 +198,7 @@ export async function createApp({ isErrorLike(reason) ? reason.message : reason + '' ) } - // Copy `.gitignore` if the application did not provide one + // Copy `.gitignore` if the application did not provide one const ignorePath = join(root, '.gitignore') if (!existsSync(ignorePath)) { copyFileSync( From 8eaecc15c5a9ca8a01e24189a521d3c7b44416c4 Mon Sep 17 00:00:00 2001 From: Arindam Majumder <109217591+Arindam200@users.noreply.github.com> Date: Sat, 27 Jul 2024 23:20:43 +0530 Subject: [PATCH 08/50] rename api to app-api --- packages/create-next-app/create-app.ts | 12 ++++++------ packages/create-next-app/index.ts | 2 +- packages/create-next-app/templates/index.ts | 2 +- packages/create-next-app/templates/types.ts | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/create-next-app/create-app.ts b/packages/create-next-app/create-app.ts index b653ea6888a99..55e609b4f4d10 100644 --- a/packages/create-next-app/create-app.ts +++ b/packages/create-next-app/create-app.ts @@ -37,7 +37,7 @@ export async function createApp({ skipInstall, empty, turbo, - api, + app-api, }: { appPath: string packageManager: PackageManager @@ -52,16 +52,16 @@ export async function createApp({ skipInstall: boolean empty: boolean turbo: boolean - api: boolean + app-api: boolean }): Promise { let repoInfo: RepoInfo | undefined const mode: TemplateMode = typescript ? 'ts' : 'js' - const template: TemplateType = api - ? 'api' + const template: TemplateType = app-api + ? 'app-api' : `${appRouter ? 'app' : 'default'}${tailwind ? '-tw' : ''}${empty ? '-empty' : ''}`; - if (api && template !== 'api') { + if (api && template !== 'app-api') { console.error(`Invalid template for api flag: ${red(template)}`) process.exit(1) } @@ -242,7 +242,7 @@ export async function createApp({ importAlias, skipInstall, turbo, - api, + app-api, }) } diff --git a/packages/create-next-app/index.ts b/packages/create-next-app/index.ts index de1f432ea8b00..6097ef75b2ccf 100644 --- a/packages/create-next-app/index.ts +++ b/packages/create-next-app/index.ts @@ -107,7 +107,7 @@ const program = new Command(packageJson.name) .option( '--api', ` - Initialize a Headless API. + Initialize a Headless API using app router. ` ) .option( diff --git a/packages/create-next-app/templates/index.ts b/packages/create-next-app/templates/index.ts index 82ab5897299db..90f012e7429ca 100644 --- a/packages/create-next-app/templates/index.ts +++ b/packages/create-next-app/templates/index.ts @@ -40,7 +40,7 @@ export const installTemplate = async ({ importAlias, skipInstall, turbo, - api, + app-api, }: InstallTemplateArgs) => { console.log(bold(`Using ${packageManager}.`)); diff --git a/packages/create-next-app/templates/types.ts b/packages/create-next-app/templates/types.ts index cbd4c78aad0c7..f8d51147104fa 100644 --- a/packages/create-next-app/templates/types.ts +++ b/packages/create-next-app/templates/types.ts @@ -2,7 +2,7 @@ import { PackageManager } from "../helpers/get-pkg-manager"; export type TemplateType = | "app" - | "api" + | "app-api" | "app-empty" | "app-tw" | "app-tw-empty" @@ -20,7 +20,7 @@ export interface GetTemplateFileArgs { export interface InstallTemplateArgs { appName: string; - api: boolean; + app-api: boolean; root: string; packageManager: PackageManager; isOnline: boolean; From e2e63a12535d4849a9906f21c3174e8a12d09557 Mon Sep 17 00:00:00 2001 From: Tim_Z Date: Sun, 28 Jul 2024 22:27:43 -0400 Subject: [PATCH 09/50] Improve next.config.ts and next.config.mjs for API template --- .husky/.env.example | 10 ++++++++++ .husky/next.config.ts | 7 +++++++ .../create-next-app/templates/api/js/.env.example | 7 +++++++ .../templates/api/js/next.config.mjs | 14 +++++++++++++- .../create-next-app/templates/api/ts/.env.example | 7 +++++++ .../templates/api/ts/next.config.ts | 10 ++++++++++ 6 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 .husky/.env.example create mode 100644 .husky/next.config.ts diff --git a/.husky/.env.example b/.husky/.env.example new file mode 100644 index 0000000000000..d47a0ff7191cc --- /dev/null +++ b/.husky/.env.example @@ -0,0 +1,10 @@ +# Rename this file to `.env.local` to use environment variables locally with `next dev` +# Add .env files to your .gitignore to prevent accidentally committing sensitive information. +# https://nextjs.org/docs/app/building-your-application/configuring/environment-variables +MY_HOST="example.com" +# API Configuration +API_BASE_URL="https://api.example.com" +API_VERSION="v1" +# Misc +NODE_ENV="development" +PORT="3000" \ No newline at end of file diff --git a/.husky/next.config.ts b/.husky/next.config.ts new file mode 100644 index 0000000000000..e9ffa3083ad27 --- /dev/null +++ b/.husky/next.config.ts @@ -0,0 +1,7 @@ +import type { NextConfig } from "next"; + +const nextConfig: NextConfig = { + /* config options here */ +}; + +export default nextConfig; diff --git a/packages/create-next-app/templates/api/js/.env.example b/packages/create-next-app/templates/api/js/.env.example index ce586fad292cd..d47a0ff7191cc 100644 --- a/packages/create-next-app/templates/api/js/.env.example +++ b/packages/create-next-app/templates/api/js/.env.example @@ -1,3 +1,10 @@ # Rename this file to `.env.local` to use environment variables locally with `next dev` +# Add .env files to your .gitignore to prevent accidentally committing sensitive information. # https://nextjs.org/docs/app/building-your-application/configuring/environment-variables MY_HOST="example.com" +# API Configuration +API_BASE_URL="https://api.example.com" +API_VERSION="v1" +# Misc +NODE_ENV="development" +PORT="3000" \ No newline at end of file diff --git a/packages/create-next-app/templates/api/js/next.config.mjs b/packages/create-next-app/templates/api/js/next.config.mjs index 4678774e6d606..31c82f620be88 100644 --- a/packages/create-next-app/templates/api/js/next.config.mjs +++ b/packages/create-next-app/templates/api/js/next.config.mjs @@ -1,4 +1,16 @@ /** @type {import('next').NextConfig} */ -const nextConfig = {}; +const nextConfig = { + // API routes configuration + api: { + + bodyParser: { + sizeLimit: '1mb', + }, + cors: { + origin: '*', + methods: ['GET', 'POST', 'PUT', 'DELETE'], + }, + }, +}; export default nextConfig; diff --git a/packages/create-next-app/templates/api/ts/.env.example b/packages/create-next-app/templates/api/ts/.env.example index ce586fad292cd..d47a0ff7191cc 100644 --- a/packages/create-next-app/templates/api/ts/.env.example +++ b/packages/create-next-app/templates/api/ts/.env.example @@ -1,3 +1,10 @@ # Rename this file to `.env.local` to use environment variables locally with `next dev` +# Add .env files to your .gitignore to prevent accidentally committing sensitive information. # https://nextjs.org/docs/app/building-your-application/configuring/environment-variables MY_HOST="example.com" +# API Configuration +API_BASE_URL="https://api.example.com" +API_VERSION="v1" +# Misc +NODE_ENV="development" +PORT="3000" \ No newline at end of file diff --git a/packages/create-next-app/templates/api/ts/next.config.ts b/packages/create-next-app/templates/api/ts/next.config.ts index e9ffa3083ad27..c8eab0e564b52 100644 --- a/packages/create-next-app/templates/api/ts/next.config.ts +++ b/packages/create-next-app/templates/api/ts/next.config.ts @@ -2,6 +2,16 @@ import type { NextConfig } from "next"; const nextConfig: NextConfig = { /* config options here */ + // API routes configuration + api: { + bodyParser: { + sizeLimit: '1mb', + }, + cors: { + origin: '*', + methods: ['GET', 'POST', 'PUT', 'DELETE'], + }, + }, }; export default nextConfig; From 01935962c62f60590cb5c7e178c9e7f5704ec774 Mon Sep 17 00:00:00 2001 From: Tim_Z Date: Sun, 28 Jul 2024 23:15:11 -0400 Subject: [PATCH 10/50] Add route.js for API routes using App Router --- .../templates/api/js/api/[param]/route.js | 11 +++++++++++ .../create-next-app/templates/api/js/api/route.js | 0 2 files changed, 11 insertions(+) create mode 100644 packages/create-next-app/templates/api/js/api/[param]/route.js create mode 100644 packages/create-next-app/templates/api/js/api/route.js diff --git a/packages/create-next-app/templates/api/js/api/[param]/route.js b/packages/create-next-app/templates/api/js/api/[param]/route.js new file mode 100644 index 0000000000000..8ef14a8bc9adb --- /dev/null +++ b/packages/create-next-app/templates/api/js/api/[param]/route.js @@ -0,0 +1,11 @@ + + +export async function GET(request, context) { + const { params } = context; + const { param } = params; // dynamic router + + return new Response(JSON.stringify({ message: `Hello, ${param}` }), { + headers: { 'Content-Type': 'application/json' }, + }); + } + \ No newline at end of file diff --git a/packages/create-next-app/templates/api/js/api/route.js b/packages/create-next-app/templates/api/js/api/route.js new file mode 100644 index 0000000000000..e69de29bb2d1d From 8f3a438fc05e0c392713b49e8932182540c09da1 Mon Sep 17 00:00:00 2001 From: Tim_Z Date: Mon, 29 Jul 2024 09:45:51 -0400 Subject: [PATCH 11/50] Save current changes --- .../templates/api/js/api/route.js | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/packages/create-next-app/templates/api/js/api/route.js b/packages/create-next-app/templates/api/js/api/route.js index e69de29bb2d1d..c19045f1979a1 100644 --- a/packages/create-next-app/templates/api/js/api/route.js +++ b/packages/create-next-app/templates/api/js/api/route.js @@ -0,0 +1,35 @@ +export async function GET(request) { + return new Response(JSON.stringify({ message: "Hello World" }), { + headers: { 'Content-Type': 'application/json' }, + }); + } + + export async function POST(request) { + const data = await request.json(); + return new Response(JSON.stringify({ message: "Data received", data }), { + headers: { 'Content-Type': 'application/json' }, + }); + } + + export async function PUT(request) { + const data = await request.json(); + return new Response(JSON.stringify({ message: "Data updated", data }), { + headers: { 'Content-Type': 'application/json' }, + }); + } + + export async function DELETE(request) { + return new Response(JSON.stringify({ message: "Data deleted" }), { + headers: { 'Content-Type': 'application/json' }, + }); + } + + export async function OPTIONS() { + return new Response(null, { + headers: { + 'Allow': 'GET, POST, PUT, DELETE, OPTIONS', + 'Content-Length': '0', + }, + }); + } + \ No newline at end of file From 30d146e25677055a05494d5fe2d676f88547d5d9 Mon Sep 17 00:00:00 2001 From: Tim_Z Date: Mon, 29 Jul 2024 21:24:38 -0400 Subject: [PATCH 12/50] added route.js and route.ts --- .../templates/api/js/.env.example | 7 ---- .../templates/api/ts/.env.example | 6 ---- .../templates/api/ts/api/route.ts | 36 +++++++++++++++++++ 3 files changed, 36 insertions(+), 13 deletions(-) create mode 100644 packages/create-next-app/templates/api/ts/api/route.ts diff --git a/packages/create-next-app/templates/api/js/.env.example b/packages/create-next-app/templates/api/js/.env.example index d47a0ff7191cc..ce586fad292cd 100644 --- a/packages/create-next-app/templates/api/js/.env.example +++ b/packages/create-next-app/templates/api/js/.env.example @@ -1,10 +1,3 @@ # Rename this file to `.env.local` to use environment variables locally with `next dev` -# Add .env files to your .gitignore to prevent accidentally committing sensitive information. # https://nextjs.org/docs/app/building-your-application/configuring/environment-variables MY_HOST="example.com" -# API Configuration -API_BASE_URL="https://api.example.com" -API_VERSION="v1" -# Misc -NODE_ENV="development" -PORT="3000" \ No newline at end of file diff --git a/packages/create-next-app/templates/api/ts/.env.example b/packages/create-next-app/templates/api/ts/.env.example index d47a0ff7191cc..b4b3144d2b90c 100644 --- a/packages/create-next-app/templates/api/ts/.env.example +++ b/packages/create-next-app/templates/api/ts/.env.example @@ -2,9 +2,3 @@ # Add .env files to your .gitignore to prevent accidentally committing sensitive information. # https://nextjs.org/docs/app/building-your-application/configuring/environment-variables MY_HOST="example.com" -# API Configuration -API_BASE_URL="https://api.example.com" -API_VERSION="v1" -# Misc -NODE_ENV="development" -PORT="3000" \ No newline at end of file diff --git a/packages/create-next-app/templates/api/ts/api/route.ts b/packages/create-next-app/templates/api/ts/api/route.ts new file mode 100644 index 0000000000000..63e15bf0c34f6 --- /dev/null +++ b/packages/create-next-app/templates/api/ts/api/route.ts @@ -0,0 +1,36 @@ +import { NextRequest, NextResponse } from 'next/server'; + +export async function GET(request: NextRequest) { + return NextResponse.json({ message: "Hello World" }, { + headers: { 'Content-Type': 'application/json' }, + }); +} + +export async function POST(request: NextRequest) { + const data = await request.json(); + return NextResponse.json({ message: "Data received", data }, { + headers: { 'Content-Type': 'application/json' }, + }); +} + +export async function PUT(request: NextRequest) { + const data = await request.json(); + return NextResponse.json({ message: "Data updated", data }, { + headers: { 'Content-Type': 'application/json' }, + }); +} + +export async function DELETE(request: NextRequest) { + return NextResponse.json({ message: "Data deleted" }, { + headers: { 'Content-Type': 'application/json' }, + }); +} + +export async function OPTIONS(request: NextRequest) { + return new Response(null, { + headers: { + 'Allow': 'GET, POST, PUT, DELETE, OPTIONS', + 'Content-Length': '0', + }, + }); +} From 945454affb0c869943459e67ceaf5eaeabc8dd2b Mon Sep 17 00:00:00 2001 From: devjiwonchoi Date: Tue, 30 Jul 2024 13:55:56 +0900 Subject: [PATCH 13/50] fix: kebab is delicious --- packages/create-next-app/create-app.ts | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/packages/create-next-app/create-app.ts b/packages/create-next-app/create-app.ts index 55e609b4f4d10..8e2e2f46ceae8 100644 --- a/packages/create-next-app/create-app.ts +++ b/packages/create-next-app/create-app.ts @@ -37,7 +37,7 @@ export async function createApp({ skipInstall, empty, turbo, - app-api, + api, }: { appPath: string packageManager: PackageManager @@ -52,20 +52,19 @@ export async function createApp({ skipInstall: boolean empty: boolean turbo: boolean - app-api: boolean + api?: boolean }): Promise { let repoInfo: RepoInfo | undefined const mode: TemplateMode = typescript ? 'ts' : 'js' - const template: TemplateType = app-api - ? 'app-api' - : `${appRouter ? 'app' : 'default'}${tailwind ? '-tw' : ''}${empty ? '-empty' : ''}`; - + const template: TemplateType = api + ? 'app-api' + : `${appRouter ? 'app' : 'default'}${tailwind ? '-tw' : ''}${empty ? '-empty' : ''}` if (api && template !== 'app-api') { console.error(`Invalid template for api flag: ${red(template)}`) process.exit(1) } - + if (example) { let repoUrl: URL | undefined @@ -242,7 +241,7 @@ export async function createApp({ importAlias, skipInstall, turbo, - app-api, + api, }) } From e9a494945c6c7139fc934e7fff1164d85e1b620e Mon Sep 17 00:00:00 2001 From: devjiwonchoi Date: Tue, 30 Jul 2024 14:02:58 +0900 Subject: [PATCH 14/50] fix kebab Co-authored-by: Ahmed Abdelbaset --- packages/create-next-app/create-app.ts | 1 - packages/create-next-app/templates/index.ts | 1 - packages/create-next-app/templates/types.ts | 3 +-- 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/create-next-app/create-app.ts b/packages/create-next-app/create-app.ts index 8e2e2f46ceae8..55aa19cd6264b 100644 --- a/packages/create-next-app/create-app.ts +++ b/packages/create-next-app/create-app.ts @@ -241,7 +241,6 @@ export async function createApp({ importAlias, skipInstall, turbo, - api, }) } diff --git a/packages/create-next-app/templates/index.ts b/packages/create-next-app/templates/index.ts index 90f012e7429ca..1d54f8ae0e63c 100644 --- a/packages/create-next-app/templates/index.ts +++ b/packages/create-next-app/templates/index.ts @@ -40,7 +40,6 @@ export const installTemplate = async ({ importAlias, skipInstall, turbo, - app-api, }: InstallTemplateArgs) => { console.log(bold(`Using ${packageManager}.`)); diff --git a/packages/create-next-app/templates/types.ts b/packages/create-next-app/templates/types.ts index f8d51147104fa..dab64e72816c4 100644 --- a/packages/create-next-app/templates/types.ts +++ b/packages/create-next-app/templates/types.ts @@ -20,7 +20,6 @@ export interface GetTemplateFileArgs { export interface InstallTemplateArgs { appName: string; - app-api: boolean; root: string; packageManager: PackageManager; isOnline: boolean; @@ -32,4 +31,4 @@ export interface InstallTemplateArgs { importAlias: string; skipInstall: boolean; turbo: boolean; -} \ No newline at end of file +} From d1491502540c518c20d2a95bda1f5d604a84f388 Mon Sep 17 00:00:00 2001 From: devjiwonchoi Date: Tue, 30 Jul 2024 14:04:39 +0900 Subject: [PATCH 15/50] template name from api to app-api --- .../templates/api/js/api/[param]/route.js | 11 ----- .../templates/api/js/api/index.js | 3 -- .../templates/api/js/api/route.js | 35 -------------- .../templates/api/ts/api/route.ts | 36 -------------- .../{api => app-api}/js/.env.example | 0 .../{api => app-api}/js/README-template.md | 0 .../templates/app-api/js/api/[param]/route.js | 8 ++++ .../templates/app-api/js/api/index.js | 3 ++ .../templates/app-api/js/api/route.js | 34 +++++++++++++ .../{api => app-api}/js/eslintrc.json | 0 .../templates/{api => app-api}/js/gitignore | 0 .../{api => app-api}/js/jsconfig.json | 0 .../{api => app-api}/js/next.config.mjs | 9 ++-- .../{api => app-api}/ts/.env.example | 0 .../{api => app-api}/ts/README-template.md | 0 .../{api => app-api}/ts/api/index.ts | 10 ++-- .../templates/app-api/ts/api/route.ts | 48 +++++++++++++++++++ .../{api => app-api}/ts/eslintrc.json | 0 .../templates/{api => app-api}/ts/gitignore | 0 .../{api => app-api}/ts/next-env.d.ts | 0 .../{api => app-api}/ts/next.config.ts | 6 +-- .../{api => app-api}/ts/tsconfig.json | 0 22 files changed, 105 insertions(+), 98 deletions(-) delete mode 100644 packages/create-next-app/templates/api/js/api/[param]/route.js delete mode 100644 packages/create-next-app/templates/api/js/api/index.js delete mode 100644 packages/create-next-app/templates/api/js/api/route.js delete mode 100644 packages/create-next-app/templates/api/ts/api/route.ts rename packages/create-next-app/templates/{api => app-api}/js/.env.example (100%) rename packages/create-next-app/templates/{api => app-api}/js/README-template.md (100%) create mode 100644 packages/create-next-app/templates/app-api/js/api/[param]/route.js create mode 100644 packages/create-next-app/templates/app-api/js/api/index.js create mode 100644 packages/create-next-app/templates/app-api/js/api/route.js rename packages/create-next-app/templates/{api => app-api}/js/eslintrc.json (100%) rename packages/create-next-app/templates/{api => app-api}/js/gitignore (100%) rename packages/create-next-app/templates/{api => app-api}/js/jsconfig.json (100%) rename packages/create-next-app/templates/{api => app-api}/js/next.config.mjs (53%) rename packages/create-next-app/templates/{api => app-api}/ts/.env.example (100%) rename packages/create-next-app/templates/{api => app-api}/ts/README-template.md (100%) rename packages/create-next-app/templates/{api => app-api}/ts/api/index.ts (96%) create mode 100644 packages/create-next-app/templates/app-api/ts/api/route.ts rename packages/create-next-app/templates/{api => app-api}/ts/eslintrc.json (100%) rename packages/create-next-app/templates/{api => app-api}/ts/gitignore (100%) rename packages/create-next-app/templates/{api => app-api}/ts/next-env.d.ts (100%) rename packages/create-next-app/templates/{api => app-api}/ts/next.config.ts (70%) rename packages/create-next-app/templates/{api => app-api}/ts/tsconfig.json (100%) diff --git a/packages/create-next-app/templates/api/js/api/[param]/route.js b/packages/create-next-app/templates/api/js/api/[param]/route.js deleted file mode 100644 index 8ef14a8bc9adb..0000000000000 --- a/packages/create-next-app/templates/api/js/api/[param]/route.js +++ /dev/null @@ -1,11 +0,0 @@ - - -export async function GET(request, context) { - const { params } = context; - const { param } = params; // dynamic router - - return new Response(JSON.stringify({ message: `Hello, ${param}` }), { - headers: { 'Content-Type': 'application/json' }, - }); - } - \ No newline at end of file diff --git a/packages/create-next-app/templates/api/js/api/index.js b/packages/create-next-app/templates/api/js/api/index.js deleted file mode 100644 index fbe9ccef4523f..0000000000000 --- a/packages/create-next-app/templates/api/js/api/index.js +++ /dev/null @@ -1,3 +0,0 @@ -export default (req, res) => { - return res.json({ message: "Hello World" }); -}; \ No newline at end of file diff --git a/packages/create-next-app/templates/api/js/api/route.js b/packages/create-next-app/templates/api/js/api/route.js deleted file mode 100644 index c19045f1979a1..0000000000000 --- a/packages/create-next-app/templates/api/js/api/route.js +++ /dev/null @@ -1,35 +0,0 @@ -export async function GET(request) { - return new Response(JSON.stringify({ message: "Hello World" }), { - headers: { 'Content-Type': 'application/json' }, - }); - } - - export async function POST(request) { - const data = await request.json(); - return new Response(JSON.stringify({ message: "Data received", data }), { - headers: { 'Content-Type': 'application/json' }, - }); - } - - export async function PUT(request) { - const data = await request.json(); - return new Response(JSON.stringify({ message: "Data updated", data }), { - headers: { 'Content-Type': 'application/json' }, - }); - } - - export async function DELETE(request) { - return new Response(JSON.stringify({ message: "Data deleted" }), { - headers: { 'Content-Type': 'application/json' }, - }); - } - - export async function OPTIONS() { - return new Response(null, { - headers: { - 'Allow': 'GET, POST, PUT, DELETE, OPTIONS', - 'Content-Length': '0', - }, - }); - } - \ No newline at end of file diff --git a/packages/create-next-app/templates/api/ts/api/route.ts b/packages/create-next-app/templates/api/ts/api/route.ts deleted file mode 100644 index 63e15bf0c34f6..0000000000000 --- a/packages/create-next-app/templates/api/ts/api/route.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { NextRequest, NextResponse } from 'next/server'; - -export async function GET(request: NextRequest) { - return NextResponse.json({ message: "Hello World" }, { - headers: { 'Content-Type': 'application/json' }, - }); -} - -export async function POST(request: NextRequest) { - const data = await request.json(); - return NextResponse.json({ message: "Data received", data }, { - headers: { 'Content-Type': 'application/json' }, - }); -} - -export async function PUT(request: NextRequest) { - const data = await request.json(); - return NextResponse.json({ message: "Data updated", data }, { - headers: { 'Content-Type': 'application/json' }, - }); -} - -export async function DELETE(request: NextRequest) { - return NextResponse.json({ message: "Data deleted" }, { - headers: { 'Content-Type': 'application/json' }, - }); -} - -export async function OPTIONS(request: NextRequest) { - return new Response(null, { - headers: { - 'Allow': 'GET, POST, PUT, DELETE, OPTIONS', - 'Content-Length': '0', - }, - }); -} diff --git a/packages/create-next-app/templates/api/js/.env.example b/packages/create-next-app/templates/app-api/js/.env.example similarity index 100% rename from packages/create-next-app/templates/api/js/.env.example rename to packages/create-next-app/templates/app-api/js/.env.example diff --git a/packages/create-next-app/templates/api/js/README-template.md b/packages/create-next-app/templates/app-api/js/README-template.md similarity index 100% rename from packages/create-next-app/templates/api/js/README-template.md rename to packages/create-next-app/templates/app-api/js/README-template.md diff --git a/packages/create-next-app/templates/app-api/js/api/[param]/route.js b/packages/create-next-app/templates/app-api/js/api/[param]/route.js new file mode 100644 index 0000000000000..964908db34056 --- /dev/null +++ b/packages/create-next-app/templates/app-api/js/api/[param]/route.js @@ -0,0 +1,8 @@ +export async function GET(request, context) { + const { params } = context; + const { param } = params; // dynamic router + + return new Response(JSON.stringify({ message: `Hello, ${param}` }), { + headers: { "Content-Type": "application/json" }, + }); +} diff --git a/packages/create-next-app/templates/app-api/js/api/index.js b/packages/create-next-app/templates/app-api/js/api/index.js new file mode 100644 index 0000000000000..6d4cf673b1a2d --- /dev/null +++ b/packages/create-next-app/templates/app-api/js/api/index.js @@ -0,0 +1,3 @@ +export default (req, res) => { + return res.json({ message: "Hello World" }); +}; diff --git a/packages/create-next-app/templates/app-api/js/api/route.js b/packages/create-next-app/templates/app-api/js/api/route.js new file mode 100644 index 0000000000000..56c8f4f3ab0b9 --- /dev/null +++ b/packages/create-next-app/templates/app-api/js/api/route.js @@ -0,0 +1,34 @@ +export async function GET(request) { + return new Response(JSON.stringify({ message: "Hello World" }), { + headers: { "Content-Type": "application/json" }, + }); +} + +export async function POST(request) { + const data = await request.json(); + return new Response(JSON.stringify({ message: "Data received", data }), { + headers: { "Content-Type": "application/json" }, + }); +} + +export async function PUT(request) { + const data = await request.json(); + return new Response(JSON.stringify({ message: "Data updated", data }), { + headers: { "Content-Type": "application/json" }, + }); +} + +export async function DELETE(request) { + return new Response(JSON.stringify({ message: "Data deleted" }), { + headers: { "Content-Type": "application/json" }, + }); +} + +export async function OPTIONS() { + return new Response(null, { + headers: { + Allow: "GET, POST, PUT, DELETE, OPTIONS", + "Content-Length": "0", + }, + }); +} diff --git a/packages/create-next-app/templates/api/js/eslintrc.json b/packages/create-next-app/templates/app-api/js/eslintrc.json similarity index 100% rename from packages/create-next-app/templates/api/js/eslintrc.json rename to packages/create-next-app/templates/app-api/js/eslintrc.json diff --git a/packages/create-next-app/templates/api/js/gitignore b/packages/create-next-app/templates/app-api/js/gitignore similarity index 100% rename from packages/create-next-app/templates/api/js/gitignore rename to packages/create-next-app/templates/app-api/js/gitignore diff --git a/packages/create-next-app/templates/api/js/jsconfig.json b/packages/create-next-app/templates/app-api/js/jsconfig.json similarity index 100% rename from packages/create-next-app/templates/api/js/jsconfig.json rename to packages/create-next-app/templates/app-api/js/jsconfig.json diff --git a/packages/create-next-app/templates/api/js/next.config.mjs b/packages/create-next-app/templates/app-api/js/next.config.mjs similarity index 53% rename from packages/create-next-app/templates/api/js/next.config.mjs rename to packages/create-next-app/templates/app-api/js/next.config.mjs index 31c82f620be88..ea1e35bee0061 100644 --- a/packages/create-next-app/templates/api/js/next.config.mjs +++ b/packages/create-next-app/templates/app-api/js/next.config.mjs @@ -1,14 +1,13 @@ /** @type {import('next').NextConfig} */ const nextConfig = { - // API routes configuration + // API routes configuration api: { - bodyParser: { - sizeLimit: '1mb', + sizeLimit: "1mb", }, cors: { - origin: '*', - methods: ['GET', 'POST', 'PUT', 'DELETE'], + origin: "*", + methods: ["GET", "POST", "PUT", "DELETE"], }, }, }; diff --git a/packages/create-next-app/templates/api/ts/.env.example b/packages/create-next-app/templates/app-api/ts/.env.example similarity index 100% rename from packages/create-next-app/templates/api/ts/.env.example rename to packages/create-next-app/templates/app-api/ts/.env.example diff --git a/packages/create-next-app/templates/api/ts/README-template.md b/packages/create-next-app/templates/app-api/ts/README-template.md similarity index 100% rename from packages/create-next-app/templates/api/ts/README-template.md rename to packages/create-next-app/templates/app-api/ts/README-template.md diff --git a/packages/create-next-app/templates/api/ts/api/index.ts b/packages/create-next-app/templates/app-api/ts/api/index.ts similarity index 96% rename from packages/create-next-app/templates/api/ts/api/index.ts rename to packages/create-next-app/templates/app-api/ts/api/index.ts index 56c734efc6caf..25e7547e83a43 100644 --- a/packages/create-next-app/templates/api/ts/api/index.ts +++ b/packages/create-next-app/templates/app-api/ts/api/index.ts @@ -1,5 +1,5 @@ -import { VercelRequest, VercelResponse } from "@vercel/node"; - -export default (req: VercelRequest, res: VercelResponse) => { - return res.json({ message: "Hello World" }); -}; \ No newline at end of file +import { VercelRequest, VercelResponse } from "@vercel/node"; + +export default (req: VercelRequest, res: VercelResponse) => { + return res.json({ message: "Hello World" }); +}; diff --git a/packages/create-next-app/templates/app-api/ts/api/route.ts b/packages/create-next-app/templates/app-api/ts/api/route.ts new file mode 100644 index 0000000000000..6afc26297f89a --- /dev/null +++ b/packages/create-next-app/templates/app-api/ts/api/route.ts @@ -0,0 +1,48 @@ +import { NextRequest, NextResponse } from "next/server"; + +export async function GET(request: NextRequest) { + return NextResponse.json( + { message: "Hello World" }, + { + headers: { "Content-Type": "application/json" }, + }, + ); +} + +export async function POST(request: NextRequest) { + const data = await request.json(); + return NextResponse.json( + { message: "Data received", data }, + { + headers: { "Content-Type": "application/json" }, + }, + ); +} + +export async function PUT(request: NextRequest) { + const data = await request.json(); + return NextResponse.json( + { message: "Data updated", data }, + { + headers: { "Content-Type": "application/json" }, + }, + ); +} + +export async function DELETE(request: NextRequest) { + return NextResponse.json( + { message: "Data deleted" }, + { + headers: { "Content-Type": "application/json" }, + }, + ); +} + +export async function OPTIONS(request: NextRequest) { + return new Response(null, { + headers: { + Allow: "GET, POST, PUT, DELETE, OPTIONS", + "Content-Length": "0", + }, + }); +} diff --git a/packages/create-next-app/templates/api/ts/eslintrc.json b/packages/create-next-app/templates/app-api/ts/eslintrc.json similarity index 100% rename from packages/create-next-app/templates/api/ts/eslintrc.json rename to packages/create-next-app/templates/app-api/ts/eslintrc.json diff --git a/packages/create-next-app/templates/api/ts/gitignore b/packages/create-next-app/templates/app-api/ts/gitignore similarity index 100% rename from packages/create-next-app/templates/api/ts/gitignore rename to packages/create-next-app/templates/app-api/ts/gitignore diff --git a/packages/create-next-app/templates/api/ts/next-env.d.ts b/packages/create-next-app/templates/app-api/ts/next-env.d.ts similarity index 100% rename from packages/create-next-app/templates/api/ts/next-env.d.ts rename to packages/create-next-app/templates/app-api/ts/next-env.d.ts diff --git a/packages/create-next-app/templates/api/ts/next.config.ts b/packages/create-next-app/templates/app-api/ts/next.config.ts similarity index 70% rename from packages/create-next-app/templates/api/ts/next.config.ts rename to packages/create-next-app/templates/app-api/ts/next.config.ts index c8eab0e564b52..c859e4b1be993 100644 --- a/packages/create-next-app/templates/api/ts/next.config.ts +++ b/packages/create-next-app/templates/app-api/ts/next.config.ts @@ -5,11 +5,11 @@ const nextConfig: NextConfig = { // API routes configuration api: { bodyParser: { - sizeLimit: '1mb', + sizeLimit: "1mb", }, cors: { - origin: '*', - methods: ['GET', 'POST', 'PUT', 'DELETE'], + origin: "*", + methods: ["GET", "POST", "PUT", "DELETE"], }, }, }; diff --git a/packages/create-next-app/templates/api/ts/tsconfig.json b/packages/create-next-app/templates/app-api/ts/tsconfig.json similarity index 100% rename from packages/create-next-app/templates/api/ts/tsconfig.json rename to packages/create-next-app/templates/app-api/ts/tsconfig.json From 7051cae4d912deb27e80e118eef90a7dd6252414 Mon Sep 17 00:00:00 2001 From: devjiwonchoi Date: Tue, 30 Jul 2024 14:06:06 +0900 Subject: [PATCH 16/50] remove unnecessary added husky --- .husky/.env.example | 10 ---------- .husky/next.config.ts | 7 ------- 2 files changed, 17 deletions(-) delete mode 100644 .husky/.env.example delete mode 100644 .husky/next.config.ts diff --git a/.husky/.env.example b/.husky/.env.example deleted file mode 100644 index d47a0ff7191cc..0000000000000 --- a/.husky/.env.example +++ /dev/null @@ -1,10 +0,0 @@ -# Rename this file to `.env.local` to use environment variables locally with `next dev` -# Add .env files to your .gitignore to prevent accidentally committing sensitive information. -# https://nextjs.org/docs/app/building-your-application/configuring/environment-variables -MY_HOST="example.com" -# API Configuration -API_BASE_URL="https://api.example.com" -API_VERSION="v1" -# Misc -NODE_ENV="development" -PORT="3000" \ No newline at end of file diff --git a/.husky/next.config.ts b/.husky/next.config.ts deleted file mode 100644 index e9ffa3083ad27..0000000000000 --- a/.husky/next.config.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { NextConfig } from "next"; - -const nextConfig: NextConfig = { - /* config options here */ -}; - -export default nextConfig; From 03767ee25e9e1c3ecda0ad6c693a0b9fda290ce7 Mon Sep 17 00:00:00 2001 From: Jiwon Choi Date: Tue, 30 Jul 2024 14:06:41 +0900 Subject: [PATCH 17/50] Update packages/create-next-app/index.ts Co-authored-by: Lee Robinson --- packages/create-next-app/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/create-next-app/index.ts b/packages/create-next-app/index.ts index 6097ef75b2ccf..8ec4b8004746e 100644 --- a/packages/create-next-app/index.ts +++ b/packages/create-next-app/index.ts @@ -107,7 +107,7 @@ const program = new Command(packageJson.name) .option( '--api', ` - Initialize a Headless API using app router. + Initialize a headless API using the App Router. ` ) .option( From 9af95756238cef150360d53c6a5f432af0eddff2 Mon Sep 17 00:00:00 2001 From: devjiwonchoi Date: Tue, 30 Jul 2024 14:37:53 +0900 Subject: [PATCH 18/50] remove eslint --- packages/create-next-app/create-app.ts | 6 ++++++ packages/create-next-app/templates/app-api/js/eslintrc.json | 3 --- packages/create-next-app/templates/app-api/ts/eslintrc.json | 3 --- 3 files changed, 6 insertions(+), 6 deletions(-) delete mode 100644 packages/create-next-app/templates/app-api/js/eslintrc.json delete mode 100644 packages/create-next-app/templates/app-api/ts/eslintrc.json diff --git a/packages/create-next-app/create-app.ts b/packages/create-next-app/create-app.ts index 55aa19cd6264b..48498548dbce3 100644 --- a/packages/create-next-app/create-app.ts +++ b/packages/create-next-app/create-app.ts @@ -65,6 +65,12 @@ export async function createApp({ process.exit(1) } + // Disable tailwind and eslint for API template + if (api) { + tailwind = false + eslint = false + } + if (example) { let repoUrl: URL | undefined diff --git a/packages/create-next-app/templates/app-api/js/eslintrc.json b/packages/create-next-app/templates/app-api/js/eslintrc.json deleted file mode 100644 index bffb357a71225..0000000000000 --- a/packages/create-next-app/templates/app-api/js/eslintrc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "next/core-web-vitals" -} diff --git a/packages/create-next-app/templates/app-api/ts/eslintrc.json b/packages/create-next-app/templates/app-api/ts/eslintrc.json deleted file mode 100644 index 37224185490e6..0000000000000 --- a/packages/create-next-app/templates/app-api/ts/eslintrc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": ["next/core-web-vitals", "next/typescript"] -} From fa604fbece8447061b55e106b896cf3f3bcc277a Mon Sep 17 00:00:00 2001 From: devjiwonchoi Date: Tue, 30 Jul 2024 14:38:15 +0900 Subject: [PATCH 19/50] unnecessary since is pages router config --- .../templates/app-api/ts/next.config.ts | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/packages/create-next-app/templates/app-api/ts/next.config.ts b/packages/create-next-app/templates/app-api/ts/next.config.ts index c859e4b1be993..e9ffa3083ad27 100644 --- a/packages/create-next-app/templates/app-api/ts/next.config.ts +++ b/packages/create-next-app/templates/app-api/ts/next.config.ts @@ -2,16 +2,6 @@ import type { NextConfig } from "next"; const nextConfig: NextConfig = { /* config options here */ - // API routes configuration - api: { - bodyParser: { - sizeLimit: "1mb", - }, - cors: { - origin: "*", - methods: ["GET", "POST", "PUT", "DELETE"], - }, - }, }; export default nextConfig; From 9160ea444789f165fb720cdb25741bba8248cdb3 Mon Sep 17 00:00:00 2001 From: Ephraim Atta-Duncan Date: Tue, 30 Jul 2024 10:47:22 +0000 Subject: [PATCH 20/50] add params to ts example --- .../templates/app-api/ts/api/[param]/route.ts | 13 +++++++++++++ .../templates/app-api/ts/api/index.ts | 2 +- .../templates/app-api/ts/api/route.ts | 2 +- 3 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 packages/create-next-app/templates/app-api/ts/api/[param]/route.ts diff --git a/packages/create-next-app/templates/app-api/ts/api/[param]/route.ts b/packages/create-next-app/templates/app-api/ts/api/[param]/route.ts new file mode 100644 index 0000000000000..36d4625b8e95f --- /dev/null +++ b/packages/create-next-app/templates/app-api/ts/api/[param]/route.ts @@ -0,0 +1,13 @@ +import { type NextRequest, NextResponse } from "next/server"; + +export async function GET( + request: NextRequest, + context: { params: { param: string } }, +) { + const { params } = context; + const { param } = params; // dynamic router + + return new Response(JSON.stringify({ message: `Hello, ${param}` }), { + headers: { "Content-Type": "application/json" }, + }); +} diff --git a/packages/create-next-app/templates/app-api/ts/api/index.ts b/packages/create-next-app/templates/app-api/ts/api/index.ts index 25e7547e83a43..917a3706d2525 100644 --- a/packages/create-next-app/templates/app-api/ts/api/index.ts +++ b/packages/create-next-app/templates/app-api/ts/api/index.ts @@ -1,4 +1,4 @@ -import { VercelRequest, VercelResponse } from "@vercel/node"; +import type { VercelRequest, VercelResponse } from "@vercel/node"; export default (req: VercelRequest, res: VercelResponse) => { return res.json({ message: "Hello World" }); diff --git a/packages/create-next-app/templates/app-api/ts/api/route.ts b/packages/create-next-app/templates/app-api/ts/api/route.ts index 6afc26297f89a..49590685090f4 100644 --- a/packages/create-next-app/templates/app-api/ts/api/route.ts +++ b/packages/create-next-app/templates/app-api/ts/api/route.ts @@ -1,4 +1,4 @@ -import { NextRequest, NextResponse } from "next/server"; +import { type NextRequest, NextResponse } from "next/server"; export async function GET(request: NextRequest) { return NextResponse.json( From 557c20154b12f9d14fb3fe71bc43b8a1517e9b5f Mon Sep 17 00:00:00 2001 From: Arindam Majumder <109217591+Arindam200@users.noreply.github.com> Date: Fri, 2 Aug 2024 00:11:36 +0530 Subject: [PATCH 21/50] add: status codes --- .../templates/app-api/js/api/index.js | 3 - .../templates/app-api/js/api/route.js | 61 ++++++++--- .../templates/app-api/ts/api/index.ts | 5 - .../templates/app-api/ts/api/route.ts | 100 +++++++++++++----- 4 files changed, 121 insertions(+), 48 deletions(-) delete mode 100644 packages/create-next-app/templates/app-api/js/api/index.js delete mode 100644 packages/create-next-app/templates/app-api/ts/api/index.ts diff --git a/packages/create-next-app/templates/app-api/js/api/index.js b/packages/create-next-app/templates/app-api/js/api/index.js deleted file mode 100644 index 6d4cf673b1a2d..0000000000000 --- a/packages/create-next-app/templates/app-api/js/api/index.js +++ /dev/null @@ -1,3 +0,0 @@ -export default (req, res) => { - return res.json({ message: "Hello World" }); -}; diff --git a/packages/create-next-app/templates/app-api/js/api/route.js b/packages/create-next-app/templates/app-api/js/api/route.js index 56c8f4f3ab0b9..354b4ab38dbc8 100644 --- a/packages/create-next-app/templates/app-api/js/api/route.js +++ b/packages/create-next-app/templates/app-api/js/api/route.js @@ -1,31 +1,64 @@ export async function GET(request) { - return new Response(JSON.stringify({ message: "Hello World" }), { - headers: { "Content-Type": "application/json" }, - }); + try { + return new Response(JSON.stringify({ message: "Hello World" }), { + status: 200, + headers: { "Content-Type": "application/json" }, + }); + } catch (error) { + return new Response(JSON.stringify({ error: "Internal Server Error" }), { + status: 500, + headers: { "Content-Type": "application/json" }, + }); + } } export async function POST(request) { - const data = await request.json(); - return new Response(JSON.stringify({ message: "Data received", data }), { - headers: { "Content-Type": "application/json" }, - }); + try { + const data = await request.json(); + return new Response(JSON.stringify({ message: "Data received", data }), { + status: 200, + headers: { "Content-Type": "application/json" }, + }); + } catch (error) { + return new Response(JSON.stringify({ error: "Internal Server Error" }), { + status: 500, + headers: { "Content-Type": "application/json" }, + }); + } } export async function PUT(request) { - const data = await request.json(); - return new Response(JSON.stringify({ message: "Data updated", data }), { - headers: { "Content-Type": "application/json" }, - }); + try { + const data = await request.json(); + return new Response(JSON.stringify({ message: "Data updated", data }), { + status: 200, + headers: { "Content-Type": "application/json" }, + }); + } catch (error) { + return new Response(JSON.stringify({ error: "Internal Server Error" }), { + status: 500, + headers: { "Content-Type": "application/json" }, + }); + } } export async function DELETE(request) { - return new Response(JSON.stringify({ message: "Data deleted" }), { - headers: { "Content-Type": "application/json" }, - }); + try { + return new Response(JSON.stringify({ message: "Data deleted" }), { + status: 200, + headers: { "Content-Type": "application/json" }, + }); + } catch (error) { + return new Response(JSON.stringify({ error: "Internal Server Error" }), { + status: 500, + headers: { "Content-Type": "application/json" }, + }); + } } export async function OPTIONS() { return new Response(null, { + status: 204, headers: { Allow: "GET, POST, PUT, DELETE, OPTIONS", "Content-Length": "0", diff --git a/packages/create-next-app/templates/app-api/ts/api/index.ts b/packages/create-next-app/templates/app-api/ts/api/index.ts deleted file mode 100644 index 917a3706d2525..0000000000000 --- a/packages/create-next-app/templates/app-api/ts/api/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -import type { VercelRequest, VercelResponse } from "@vercel/node"; - -export default (req: VercelRequest, res: VercelResponse) => { - return res.json({ message: "Hello World" }); -}; diff --git a/packages/create-next-app/templates/app-api/ts/api/route.ts b/packages/create-next-app/templates/app-api/ts/api/route.ts index 49590685090f4..7e36ddedae391 100644 --- a/packages/create-next-app/templates/app-api/ts/api/route.ts +++ b/packages/create-next-app/templates/app-api/ts/api/route.ts @@ -1,41 +1,87 @@ import { type NextRequest, NextResponse } from "next/server"; +export const dynamic = "force-dynamic"; export async function GET(request: NextRequest) { - return NextResponse.json( - { message: "Hello World" }, - { - headers: { "Content-Type": "application/json" }, - }, - ); + try { + return NextResponse.json( + { message: "Hello World" }, + { + headers: { + "Content-Type": "application/json", + }, + }, + ); + } catch (error) { + return NextResponse.json( + { message: "Internal Server Error" }, + { + status: 500, + headers: { "Content-Type": "application/json" }, + }, + ); + } } export async function POST(request: NextRequest) { - const data = await request.json(); - return NextResponse.json( - { message: "Data received", data }, - { - headers: { "Content-Type": "application/json" }, - }, - ); + try { + const data = await request.json(); + return NextResponse.json( + { message: "Data received", data }, + { + status: 200, + headers: { "Content-Type": "application/json" }, + }, + ); + } catch (error) { + return NextResponse.json( + { message: "Internal Server Error" }, + { + status: 500, + headers: { "Content-Type": "application/json" }, + }, + ); + } } export async function PUT(request: NextRequest) { - const data = await request.json(); - return NextResponse.json( - { message: "Data updated", data }, - { - headers: { "Content-Type": "application/json" }, - }, - ); + try { + const data = await request.json(); + return NextResponse.json( + { message: "Data updated", data }, + { + status: 200, + headers: { "Content-Type": "application/json" }, + }, + ); + } catch (error) { + return NextResponse.json( + { message: "Internal Server Error" }, + { + status: 500, + headers: { "Content-Type": "application/json" }, + }, + ); + } } export async function DELETE(request: NextRequest) { - return NextResponse.json( - { message: "Data deleted" }, - { - headers: { "Content-Type": "application/json" }, - }, - ); + try { + return NextResponse.json( + { message: "Data deleted" }, + { + status: 200, + headers: { "Content-Type": "application/json" }, + }, + ); + } catch (error) { + return NextResponse.json( + { message: "Internal Server Error" }, + { + status: 500, + headers: { "Content-Type": "application/json" }, + }, + ); + } } export async function OPTIONS(request: NextRequest) { @@ -43,6 +89,8 @@ export async function OPTIONS(request: NextRequest) { headers: { Allow: "GET, POST, PUT, DELETE, OPTIONS", "Content-Length": "0", + "Access-Control-Allow-Origin": "*", + "Access-Control-Allow-Headers": "Content-Type, Authorization", }, }); } From fc7f5246e4e60ffff701bab5f61842439608d02f Mon Sep 17 00:00:00 2001 From: devjiwonchoi Date: Sat, 3 Aug 2024 01:14:18 +0900 Subject: [PATCH 22/50] fix: left out merges --- packages/create-next-app/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/create-next-app/index.ts b/packages/create-next-app/index.ts index 90403281507bb..e57d84fb826c0 100644 --- a/packages/create-next-app/index.ts +++ b/packages/create-next-app/index.ts @@ -57,6 +57,7 @@ const program = new Command(packageJson.name) '--import-alias ', 'Specify import alias to use (default "@/*").' ) + .option('--api', 'Initialize a headless API using the App Router.') .option('--empty', 'Initialize an empty project.') .option( '--use-npm', @@ -437,7 +438,7 @@ async function run(): Promise { skipInstall: opts.skipInstall, empty: opts.empty, turbo: opts.turbo, - api: program.api, + api: opts.api, }) } catch (reason) { if (!(reason instanceof DownloadError)) { From 8698458230aedf7188300116a060f41b16460662 Mon Sep 17 00:00:00 2001 From: devjiwonchoi Date: Sat, 3 Aug 2024 02:21:12 +0900 Subject: [PATCH 23/50] fix: left out merges --- packages/create-next-app/create-app.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/create-next-app/create-app.ts b/packages/create-next-app/create-app.ts index 43505014b15e9..2f6d43bad7f6e 100644 --- a/packages/create-next-app/create-app.ts +++ b/packages/create-next-app/create-app.ts @@ -58,7 +58,7 @@ export async function createApp({ const mode: TemplateMode = typescript ? 'ts' : 'js' const template: TemplateType = api ? 'app-api' - : `${appRouter ? 'app' : 'default'}${tailwind ? '-tw' : ''}${empty ? '-empty' : ''}` + : `${app ? 'app' : 'default'}${tailwind ? '-tw' : ''}${empty ? '-empty' : ''}` if (api && template !== 'app-api') { console.error(`Invalid template for api flag: ${red(template)}`) From a150dfc351556b1803705d7ffade2f0831a850a2 Mon Sep 17 00:00:00 2001 From: devjiwonchoi Date: Sat, 3 Aug 2024 02:24:20 +0900 Subject: [PATCH 24/50] remove unnecessary api/ [no ci] --- .../templates/app-api/js/api/[param]/route.js | 8 -- .../templates/app-api/js/api/route.js | 67 ------------- .../templates/app-api/ts/api/[param]/route.ts | 13 --- .../templates/app-api/ts/api/route.ts | 96 ------------------- 4 files changed, 184 deletions(-) delete mode 100644 packages/create-next-app/templates/app-api/js/api/[param]/route.js delete mode 100644 packages/create-next-app/templates/app-api/js/api/route.js delete mode 100644 packages/create-next-app/templates/app-api/ts/api/[param]/route.ts delete mode 100644 packages/create-next-app/templates/app-api/ts/api/route.ts diff --git a/packages/create-next-app/templates/app-api/js/api/[param]/route.js b/packages/create-next-app/templates/app-api/js/api/[param]/route.js deleted file mode 100644 index 964908db34056..0000000000000 --- a/packages/create-next-app/templates/app-api/js/api/[param]/route.js +++ /dev/null @@ -1,8 +0,0 @@ -export async function GET(request, context) { - const { params } = context; - const { param } = params; // dynamic router - - return new Response(JSON.stringify({ message: `Hello, ${param}` }), { - headers: { "Content-Type": "application/json" }, - }); -} diff --git a/packages/create-next-app/templates/app-api/js/api/route.js b/packages/create-next-app/templates/app-api/js/api/route.js deleted file mode 100644 index 354b4ab38dbc8..0000000000000 --- a/packages/create-next-app/templates/app-api/js/api/route.js +++ /dev/null @@ -1,67 +0,0 @@ -export async function GET(request) { - try { - return new Response(JSON.stringify({ message: "Hello World" }), { - status: 200, - headers: { "Content-Type": "application/json" }, - }); - } catch (error) { - return new Response(JSON.stringify({ error: "Internal Server Error" }), { - status: 500, - headers: { "Content-Type": "application/json" }, - }); - } -} - -export async function POST(request) { - try { - const data = await request.json(); - return new Response(JSON.stringify({ message: "Data received", data }), { - status: 200, - headers: { "Content-Type": "application/json" }, - }); - } catch (error) { - return new Response(JSON.stringify({ error: "Internal Server Error" }), { - status: 500, - headers: { "Content-Type": "application/json" }, - }); - } -} - -export async function PUT(request) { - try { - const data = await request.json(); - return new Response(JSON.stringify({ message: "Data updated", data }), { - status: 200, - headers: { "Content-Type": "application/json" }, - }); - } catch (error) { - return new Response(JSON.stringify({ error: "Internal Server Error" }), { - status: 500, - headers: { "Content-Type": "application/json" }, - }); - } -} - -export async function DELETE(request) { - try { - return new Response(JSON.stringify({ message: "Data deleted" }), { - status: 200, - headers: { "Content-Type": "application/json" }, - }); - } catch (error) { - return new Response(JSON.stringify({ error: "Internal Server Error" }), { - status: 500, - headers: { "Content-Type": "application/json" }, - }); - } -} - -export async function OPTIONS() { - return new Response(null, { - status: 204, - headers: { - Allow: "GET, POST, PUT, DELETE, OPTIONS", - "Content-Length": "0", - }, - }); -} diff --git a/packages/create-next-app/templates/app-api/ts/api/[param]/route.ts b/packages/create-next-app/templates/app-api/ts/api/[param]/route.ts deleted file mode 100644 index 36d4625b8e95f..0000000000000 --- a/packages/create-next-app/templates/app-api/ts/api/[param]/route.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { type NextRequest, NextResponse } from "next/server"; - -export async function GET( - request: NextRequest, - context: { params: { param: string } }, -) { - const { params } = context; - const { param } = params; // dynamic router - - return new Response(JSON.stringify({ message: `Hello, ${param}` }), { - headers: { "Content-Type": "application/json" }, - }); -} diff --git a/packages/create-next-app/templates/app-api/ts/api/route.ts b/packages/create-next-app/templates/app-api/ts/api/route.ts deleted file mode 100644 index 7e36ddedae391..0000000000000 --- a/packages/create-next-app/templates/app-api/ts/api/route.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { type NextRequest, NextResponse } from "next/server"; -export const dynamic = "force-dynamic"; - -export async function GET(request: NextRequest) { - try { - return NextResponse.json( - { message: "Hello World" }, - { - headers: { - "Content-Type": "application/json", - }, - }, - ); - } catch (error) { - return NextResponse.json( - { message: "Internal Server Error" }, - { - status: 500, - headers: { "Content-Type": "application/json" }, - }, - ); - } -} - -export async function POST(request: NextRequest) { - try { - const data = await request.json(); - return NextResponse.json( - { message: "Data received", data }, - { - status: 200, - headers: { "Content-Type": "application/json" }, - }, - ); - } catch (error) { - return NextResponse.json( - { message: "Internal Server Error" }, - { - status: 500, - headers: { "Content-Type": "application/json" }, - }, - ); - } -} - -export async function PUT(request: NextRequest) { - try { - const data = await request.json(); - return NextResponse.json( - { message: "Data updated", data }, - { - status: 200, - headers: { "Content-Type": "application/json" }, - }, - ); - } catch (error) { - return NextResponse.json( - { message: "Internal Server Error" }, - { - status: 500, - headers: { "Content-Type": "application/json" }, - }, - ); - } -} - -export async function DELETE(request: NextRequest) { - try { - return NextResponse.json( - { message: "Data deleted" }, - { - status: 200, - headers: { "Content-Type": "application/json" }, - }, - ); - } catch (error) { - return NextResponse.json( - { message: "Internal Server Error" }, - { - status: 500, - headers: { "Content-Type": "application/json" }, - }, - ); - } -} - -export async function OPTIONS(request: NextRequest) { - return new Response(null, { - headers: { - Allow: "GET, POST, PUT, DELETE, OPTIONS", - "Content-Length": "0", - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": "Content-Type, Authorization", - }, - }); -} From 1639ef5ad955e68366ce57401cc147c245ef1b79 Mon Sep 17 00:00:00 2001 From: devjiwonchoi Date: Sat, 3 Aug 2024 02:35:38 +0900 Subject: [PATCH 25/50] fix: specification --- .../integration/create-next-app/lib/specification.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/integration/create-next-app/lib/specification.ts b/test/integration/create-next-app/lib/specification.ts index 8e4cfd87cbbff..81f498ec638fd 100644 --- a/test/integration/create-next-app/lib/specification.ts +++ b/test/integration/create-next-app/lib/specification.ts @@ -171,6 +171,18 @@ export const projectSpecification: ProjectSpecification = { ], }, }, + 'app-api': { + js: { + deps: [], + devDeps: [], + files: ['app/route.js', 'jsconfig.json'], + }, + ts: { + deps: [], + devDeps: ['@types/node', '@types/react', 'typescript'], + files: ['app/route.ts', 'tsconfig.json', 'next-env.d.ts'], + }, + }, 'app-empty': { js: { deps: [], From 03aede305297488cf041c028673ed6289a6dc348 Mon Sep 17 00:00:00 2001 From: devjiwonchoi Date: Sat, 3 Aug 2024 02:36:51 +0900 Subject: [PATCH 26/50] remove unnecessary check --- packages/create-next-app/create-app.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/packages/create-next-app/create-app.ts b/packages/create-next-app/create-app.ts index 2f6d43bad7f6e..5202de297ac88 100644 --- a/packages/create-next-app/create-app.ts +++ b/packages/create-next-app/create-app.ts @@ -60,11 +60,6 @@ export async function createApp({ ? 'app-api' : `${app ? 'app' : 'default'}${tailwind ? '-tw' : ''}${empty ? '-empty' : ''}` - if (api && template !== 'app-api') { - console.error(`Invalid template for api flag: ${red(template)}`) - process.exit(1) - } - // Disable tailwind and eslint for API template if (api) { tailwind = false From 6fb01e7f9b896e0e0829cae9c952d70efa155197 Mon Sep 17 00:00:00 2001 From: Tim_Z Date: Fri, 2 Aug 2024 22:11:02 -0400 Subject: [PATCH 27/50] Add user-info API route example with NextRequest and NextResponse --- .../app-api/ts/app/api/user-info/route.ts | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 packages/create-next-app/templates/app-api/ts/app/api/user-info/route.ts diff --git a/packages/create-next-app/templates/app-api/ts/app/api/user-info/route.ts b/packages/create-next-app/templates/app-api/ts/app/api/user-info/route.ts new file mode 100644 index 0000000000000..eac16d9351ee4 --- /dev/null +++ b/packages/create-next-app/templates/app-api/ts/app/api/user-info/route.ts @@ -0,0 +1,37 @@ +import { NextRequest, NextResponse } from 'next/server' + +export async function GET(request: NextRequest) { + + const theme = request.cookies.get('theme')?.value || 'light' + request.cookies.set('last-visited', new Date().toISOString()) + + // use nextUrl + const { searchParams } = request.nextUrl + const name = searchParams.get('name') || 'Anonymous' + + + const ip = request.ip || 'Unknown' + + + const geo = request.geo || { + city: 'Unknown', + country: 'Unknown', + region: 'Unknown', + latitude: 'Unknown', + longitude: 'Unknown', + } + + const response = { + message: `Hello, ${name}!`, + theme, + ip, + geo, + } + + return NextResponse.json(response, { + status: 200, + headers: { + 'Set-Cookie': `last-visit=${new Date().toISOString()}; Path=/; HttpOnly`, + }, + }) +} \ No newline at end of file From 325f55e6bfd735f02d0ec3ff7038eeaf7061740d Mon Sep 17 00:00:00 2001 From: Tim_Z Date: Fri, 2 Aug 2024 22:29:00 -0400 Subject: [PATCH 28/50] Add 404 and 500 error handling examples for API routes and update README --- .../templates/app-api/ts/app/api/error/route.ts | 12 ++++++++++++ .../templates/app-api/ts/app/api/not-found/route.ts | 8 ++++++++ 2 files changed, 20 insertions(+) create mode 100644 packages/create-next-app/templates/app-api/ts/app/api/error/route.ts create mode 100644 packages/create-next-app/templates/app-api/ts/app/api/not-found/route.ts diff --git a/packages/create-next-app/templates/app-api/ts/app/api/error/route.ts b/packages/create-next-app/templates/app-api/ts/app/api/error/route.ts new file mode 100644 index 0000000000000..421aa32e354aa --- /dev/null +++ b/packages/create-next-app/templates/app-api/ts/app/api/error/route.ts @@ -0,0 +1,12 @@ +import { NextResponse } from 'next/server'; + +export async function GET() { + // Internal Server Error + const error = new Error('Internal Server Error'); + console.error(error); + + return NextResponse.json( + { message: 'Internal Server Error' }, + { status: 500 } + ); +} \ No newline at end of file diff --git a/packages/create-next-app/templates/app-api/ts/app/api/not-found/route.ts b/packages/create-next-app/templates/app-api/ts/app/api/not-found/route.ts new file mode 100644 index 0000000000000..a74631847f463 --- /dev/null +++ b/packages/create-next-app/templates/app-api/ts/app/api/not-found/route.ts @@ -0,0 +1,8 @@ +import { NextResponse } from 'next/server'; +//404 +export async function GET() { + return NextResponse.json( + { message: 'Resource not found' }, + { status: 404 } + ); +} \ No newline at end of file From 31095dbb4eb28fa283ca1f8d4cdd3e1ca362bc08 Mon Sep 17 00:00:00 2001 From: Tim_Z Date: Fri, 2 Aug 2024 22:47:00 -0400 Subject: [PATCH 29/50] Add cors.ts --- .../create-next-app/templates/app-api/ts/cors.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 packages/create-next-app/templates/app-api/ts/cors.ts diff --git a/packages/create-next-app/templates/app-api/ts/cors.ts b/packages/create-next-app/templates/app-api/ts/cors.ts new file mode 100644 index 0000000000000..e5304ed3cca42 --- /dev/null +++ b/packages/create-next-app/templates/app-api/ts/cors.ts @@ -0,0 +1,16 @@ +import { NextResponse } from 'next/server'; +import type { NextRequest } from 'next/server'; + +export function middleware(request: NextRequest) { + const response = NextResponse.next(); + + response.headers.set('Access-Control-Allow-Origin', '*'); + response.headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS'); + response.headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization'); + + return response; +} + +export const config = { + matcher: '/api/:path*', +}; \ No newline at end of file From 894d544722c138b03cdd561c14f939e8d1a87e83 Mon Sep 17 00:00:00 2001 From: devjiwonchoi Date: Thu, 15 Aug 2024 14:41:43 +0900 Subject: [PATCH 30/50] add app-api for ts --- .../app-api/ts/app/[...not-found]/route.ts | 3 + .../app-api/ts/app/api/error/route.ts | 12 ---- .../app-api/ts/app/api/not-found/route.ts | 8 --- .../app-api/ts/app/api/user-info/route.ts | 37 ---------- .../app-api/ts/app/methods/[method]/route.ts | 60 ++++++++++++++++ .../app-api/ts/app/methods/methods.ts | 9 +++ .../templates/app-api/ts/app/methods/route.ts | 11 +++ .../app-api/ts/app/pokemon/[id]/route.ts | 11 +++ .../templates/app-api/ts/app/pokemon/route.ts | 51 ++++++++++++++ .../templates/app-api/ts/app/route.ts | 23 +++++++ .../app-api/ts/app/with-middleware.ts | 68 +++++++++++++++++++ .../templates/app-api/ts/cors.ts | 16 ----- 12 files changed, 236 insertions(+), 73 deletions(-) create mode 100644 packages/create-next-app/templates/app-api/ts/app/[...not-found]/route.ts delete mode 100644 packages/create-next-app/templates/app-api/ts/app/api/error/route.ts delete mode 100644 packages/create-next-app/templates/app-api/ts/app/api/not-found/route.ts delete mode 100644 packages/create-next-app/templates/app-api/ts/app/api/user-info/route.ts create mode 100644 packages/create-next-app/templates/app-api/ts/app/methods/[method]/route.ts create mode 100644 packages/create-next-app/templates/app-api/ts/app/methods/methods.ts create mode 100644 packages/create-next-app/templates/app-api/ts/app/methods/route.ts create mode 100644 packages/create-next-app/templates/app-api/ts/app/pokemon/[id]/route.ts create mode 100644 packages/create-next-app/templates/app-api/ts/app/pokemon/route.ts create mode 100644 packages/create-next-app/templates/app-api/ts/app/route.ts create mode 100644 packages/create-next-app/templates/app-api/ts/app/with-middleware.ts delete mode 100644 packages/create-next-app/templates/app-api/ts/cors.ts diff --git a/packages/create-next-app/templates/app-api/ts/app/[...not-found]/route.ts b/packages/create-next-app/templates/app-api/ts/app/[...not-found]/route.ts new file mode 100644 index 0000000000000..f504a7229813c --- /dev/null +++ b/packages/create-next-app/templates/app-api/ts/app/[...not-found]/route.ts @@ -0,0 +1,3 @@ +export async function GET() { + return new Response("Not Found", { status: 404 }); +} diff --git a/packages/create-next-app/templates/app-api/ts/app/api/error/route.ts b/packages/create-next-app/templates/app-api/ts/app/api/error/route.ts deleted file mode 100644 index 421aa32e354aa..0000000000000 --- a/packages/create-next-app/templates/app-api/ts/app/api/error/route.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { NextResponse } from 'next/server'; - -export async function GET() { - // Internal Server Error - const error = new Error('Internal Server Error'); - console.error(error); - - return NextResponse.json( - { message: 'Internal Server Error' }, - { status: 500 } - ); -} \ No newline at end of file diff --git a/packages/create-next-app/templates/app-api/ts/app/api/not-found/route.ts b/packages/create-next-app/templates/app-api/ts/app/api/not-found/route.ts deleted file mode 100644 index a74631847f463..0000000000000 --- a/packages/create-next-app/templates/app-api/ts/app/api/not-found/route.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { NextResponse } from 'next/server'; -//404 -export async function GET() { - return NextResponse.json( - { message: 'Resource not found' }, - { status: 404 } - ); -} \ No newline at end of file diff --git a/packages/create-next-app/templates/app-api/ts/app/api/user-info/route.ts b/packages/create-next-app/templates/app-api/ts/app/api/user-info/route.ts deleted file mode 100644 index eac16d9351ee4..0000000000000 --- a/packages/create-next-app/templates/app-api/ts/app/api/user-info/route.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { NextRequest, NextResponse } from 'next/server' - -export async function GET(request: NextRequest) { - - const theme = request.cookies.get('theme')?.value || 'light' - request.cookies.set('last-visited', new Date().toISOString()) - - // use nextUrl - const { searchParams } = request.nextUrl - const name = searchParams.get('name') || 'Anonymous' - - - const ip = request.ip || 'Unknown' - - - const geo = request.geo || { - city: 'Unknown', - country: 'Unknown', - region: 'Unknown', - latitude: 'Unknown', - longitude: 'Unknown', - } - - const response = { - message: `Hello, ${name}!`, - theme, - ip, - geo, - } - - return NextResponse.json(response, { - status: 200, - headers: { - 'Set-Cookie': `last-visit=${new Date().toISOString()}; Path=/; HttpOnly`, - }, - }) -} \ No newline at end of file diff --git a/packages/create-next-app/templates/app-api/ts/app/methods/[method]/route.ts b/packages/create-next-app/templates/app-api/ts/app/methods/[method]/route.ts new file mode 100644 index 0000000000000..dbec2ba949bc9 --- /dev/null +++ b/packages/create-next-app/templates/app-api/ts/app/methods/[method]/route.ts @@ -0,0 +1,60 @@ +import { NextResponse } from "next/server"; +import { methods } from "../methods"; +import { withMiddleware } from "@/app/with-middleware"; + +export const GET = withMiddleware( + async (request, context: { params: { method: string } }) => { + const method = context.params.method.toUpperCase(); + if (!methods.includes(method)) { + return NextResponse.json( + { error: `Method ${method} is not supported.` }, + { status: 404 }, + ); + } + + if (method === "GET") { + return NextResponse.json({ method: "GET" }); + } + + const url = request.nextUrl.toString(); + const res = await fetch(url, { method }); + + // if method is HEAD, body is null + if (method === "HEAD") { + const { ok, statusText, status, body } = res; + return NextResponse.json({ + ok, + statusText, + status, + body, + }); + } + + const data = await res.json(); + return NextResponse.json(data); + }, +); + +export async function POST() { + return NextResponse.json({ method: "POST" }); +} + +export async function PUT() { + return NextResponse.json({ method: "PUT" }); +} + +export async function DELETE() { + return NextResponse.json({ method: "DELETE" }); +} + +export async function PATCH() { + return NextResponse.json({ method: "PATCH" }); +} + +export async function OPTIONS() { + return NextResponse.json({ method: "OPTIONS" }); +} + +export async function HEAD() { + return new Response(null, { status: 200 }); +} diff --git a/packages/create-next-app/templates/app-api/ts/app/methods/methods.ts b/packages/create-next-app/templates/app-api/ts/app/methods/methods.ts new file mode 100644 index 0000000000000..ed887fff74f26 --- /dev/null +++ b/packages/create-next-app/templates/app-api/ts/app/methods/methods.ts @@ -0,0 +1,9 @@ +export const methods = [ + "GET", + "POST", + "PUT", + "DELETE", + "PATCH", + "OPTIONS", + "HEAD", +]; diff --git a/packages/create-next-app/templates/app-api/ts/app/methods/route.ts b/packages/create-next-app/templates/app-api/ts/app/methods/route.ts new file mode 100644 index 0000000000000..86c2985245d8d --- /dev/null +++ b/packages/create-next-app/templates/app-api/ts/app/methods/route.ts @@ -0,0 +1,11 @@ +import { type NextRequest, NextResponse } from "next/server"; +import { methods } from "./methods"; + +export async function GET(request: NextRequest) { + const url = request.nextUrl.toString(); + const apis = methods.map((method) => { + return { path: `${url}/${method}`, description: `${method} method` }; + }); + + return NextResponse.json(apis); +} diff --git a/packages/create-next-app/templates/app-api/ts/app/pokemon/[id]/route.ts b/packages/create-next-app/templates/app-api/ts/app/pokemon/[id]/route.ts new file mode 100644 index 0000000000000..b8186e60438fe --- /dev/null +++ b/packages/create-next-app/templates/app-api/ts/app/pokemon/[id]/route.ts @@ -0,0 +1,11 @@ +import { NextResponse } from "next/server"; +import { withMiddleware } from "@/app/with-middleware"; + +export const GET = withMiddleware(async (_request, context) => { + const res = await fetch( + `https://api.vercel.app/pokemon/${context.params.id}`, + ); + const pokemon = await res.json(); + + return NextResponse.json(pokemon); +}); diff --git a/packages/create-next-app/templates/app-api/ts/app/pokemon/route.ts b/packages/create-next-app/templates/app-api/ts/app/pokemon/route.ts new file mode 100644 index 0000000000000..102e512d5efcc --- /dev/null +++ b/packages/create-next-app/templates/app-api/ts/app/pokemon/route.ts @@ -0,0 +1,51 @@ +import { NextResponse } from "next/server"; +import { withMiddleware } from "@/app/with-middleware"; + +export const GET = withMiddleware(async (request) => { + const url = request.nextUrl.toString(); + + const { searchParams } = request.nextUrl; + const type = searchParams.get("type"); + + if (type && !POKEMON_TYPES.includes(type)) { + return NextResponse.json( + { error: `Invalid type: '${type}'`, types: POKEMON_TYPES }, + { status: 400 }, + ); + } + + const res = await fetch( + `https://api.vercel.app/pokemon${type ? `?type=${type}` : ""}`, + ); + const data = await res.json(); + + const pokedex = data.map((pokemon: { id: string }) => { + return { + ...pokemon, + url: `${url}/${pokemon.id}`, + }; + }); + + return NextResponse.json(pokedex); +}); + +const POKEMON_TYPES = [ + "normal", + "fire", + "water", + "electric", + "grass", + "ice", + "fighting", + "poison", + "ground", + "flying", + "psychic", + "bug", + "rock", + "ghost", + "dragon", + "dark", + "steel", + "fairy", +]; diff --git a/packages/create-next-app/templates/app-api/ts/app/route.ts b/packages/create-next-app/templates/app-api/ts/app/route.ts new file mode 100644 index 0000000000000..240a0c044d8d9 --- /dev/null +++ b/packages/create-next-app/templates/app-api/ts/app/route.ts @@ -0,0 +1,23 @@ +import { type NextRequest, NextResponse } from "next/server"; + +const paths = [ + { path: "pokemon", description: "gotta catch 'em all!" }, + { path: "pokemon?type=grass", description: "sort pokemon by grass type" }, + { path: "pokemon/25", description: "who's that pokemon?" }, + { path: "methods", description: "list of methods" }, +]; + +export async function GET(request: NextRequest) { + const url = request.nextUrl.toString(); + const apis = paths.map(({ path, description }) => { + return { path: url + path, description }; + }); + + return NextResponse.json(apis, { + headers: { + // CORS: https://nextjs.org/docs/app/building-your-application/routing/route-handlers#cors + "Access-Control-Allow-Origin": "*", + "Access-Control-Allow-Methods": "GET", + }, + }); +} diff --git a/packages/create-next-app/templates/app-api/ts/app/with-middleware.ts b/packages/create-next-app/templates/app-api/ts/app/with-middleware.ts new file mode 100644 index 0000000000000..026430e105467 --- /dev/null +++ b/packages/create-next-app/templates/app-api/ts/app/with-middleware.ts @@ -0,0 +1,68 @@ +import { NextRequest, NextResponse } from "next/server"; + +type RouteHandler> = ( + req: NextRequest, + ctx: { params: T }, +) => Promise | Response; + +export function withMiddleware>( + handler: RouteHandler, +): RouteHandler { + return async (req: NextRequest, ctx: { params: T }) => { + try { + console.log(`Middleware: Request to ${req.method} ${req.url}`); + + // Check if the request is coming from an allowed origin + if (!isAllowedOrigin(req)) { + return NextResponse.json({ error: "Forbidden" }, { status: 403 }); + } + + // Placeholder for authentication logic + const isAuthenticated = await checkAuth(req); + if (!isAuthenticated) { + return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); + } + + // Call the original handler + const result = await handler(req, ctx); + + // Log after executing the handler + console.log(`Middleware: Completed ${req.method} ${req.url}`); + + return result; + } catch (error) { + console.error( + `Middleware: Error in route handler: ${req.method} ${req.url}`, + error, + ); + return NextResponse.json( + { error: "Internal Server Error" }, + { status: 500 }, + ); + } + }; +} + +function isAllowedOrigin(req: NextRequest): boolean { + const referer = req.headers.get("referer"); + const host = req.headers.get("host"); + + // Allow requests from the same origin + if (referer && host) { + const refererUrl = new URL(referer); + return refererUrl.host === host; + } + + if (process.env.NODE_ENV === "development" && host?.includes("localhost")) { + return true; + } + + return false; +} + +async function checkAuth(req: NextRequest): Promise { + // Placeholder authentication logic + // You can implement your actual auth check here + const authToken = req.headers.get("authorization"); + return true; // For now, always return true +} diff --git a/packages/create-next-app/templates/app-api/ts/cors.ts b/packages/create-next-app/templates/app-api/ts/cors.ts deleted file mode 100644 index e5304ed3cca42..0000000000000 --- a/packages/create-next-app/templates/app-api/ts/cors.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { NextResponse } from 'next/server'; -import type { NextRequest } from 'next/server'; - -export function middleware(request: NextRequest) { - const response = NextResponse.next(); - - response.headers.set('Access-Control-Allow-Origin', '*'); - response.headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS'); - response.headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization'); - - return response; -} - -export const config = { - matcher: '/api/:path*', -}; \ No newline at end of file From 93ea62937be4b464d1d111a71e7c5e7527893b63 Mon Sep 17 00:00:00 2001 From: devjiwonchoi Date: Thu, 15 Aug 2024 15:01:10 +0900 Subject: [PATCH 31/50] readme --- .../templates/app-api/ts/README-template.md | 59 ++++++++++++------- 1 file changed, 39 insertions(+), 20 deletions(-) diff --git a/packages/create-next-app/templates/app-api/ts/README-template.md b/packages/create-next-app/templates/app-api/ts/README-template.md index 4d939b2a8fb74..cacee7ed90e91 100644 --- a/packages/create-next-app/templates/app-api/ts/README-template.md +++ b/packages/create-next-app/templates/app-api/ts/README-template.md @@ -14,9 +14,9 @@ pnpm dev bun dev ``` -Open [http://localhost:3000/api](http://localhost:3000/api) with your browser to see the result. +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. -You can start editing the page by modifying `app/index.ts`. The page auto-updates as you edit the file. +You can start editing the page by modifying `app/route.ts`. The page auto-updates as you edit the file. ## Learn More @@ -32,29 +32,48 @@ You can check out [the Next.js GitHub repository](https://github.com/vercel/next The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. + ## API Routes This directory contains example API routes for the headless API app. -### GET /api/user-info -Returns information about the user based on the request data. -Query Parameters: -- `name`: The name of the user (optional) +Here's an overview of the available routes: -Example usage: -```http -GET /api/user-info?name=John +1. Root Route (`/`): -### GET /api/error -Demonstrates a custom error response. + - Provides a list of available API endpoints. + - Useful for discovering what the API offers. -Example usage: -```http -GET /api/error -``` -### GET /api/not-found -Demonstrates a 404 Not Found response. +2. Pokemon Routes: + + - List all Pokemon (`/pokemon`): Get an overview of available Pokemon. + - Filter Pokemon by type (`/pokemon?type=grass`): Find Pokemon of a specific element. + - Get a specific Pokemon (`/pokemon/25`): Fetch details about a particular Pokemon by ID. + +3. HTTP Methods Route (`/methods`): + + - Information about supported HTTP methods. + - Great for learning about various types of API requests. + +4. Specific HTTP Method Routes (`/methods/[method]`): + + - Demonstrates different HTTP methods (GET, POST, PUT, DELETE, PATCH). + - Helps understand how different types of API requests behave. + +5. Not Found Route: + - Handles requests to undefined routes. + - Returns a "Not Found" response for non-existent endpoints. + +Each route is wrapped with middleware that adds: + +- Request logging for monitoring and debugging. +- Origin checking for security. +- Authentication placeholders for future expansion. + +To explore these routes: + +1. Start the development server. +2. Open your browser and navigate to `http://localhost:3000/[route]`. +3. Examine the responses to understand how each route works. -Example usage: -```http -GET /api/not-found +For more details on the implementation, check the corresponding files in the `app` directory. From 57523746811ac045d6639be533a8c46902bf015d Mon Sep 17 00:00:00 2001 From: devjiwonchoi Date: Thu, 15 Aug 2024 15:02:12 +0900 Subject: [PATCH 32/50] remove js for now --- .../templates/app-api/js/.env.example | 3 -- .../templates/app-api/js/README-template.md | 34 ---------------- .../templates/app-api/js/gitignore | 40 ------------------- .../templates/app-api/js/jsconfig.json | 7 ---- .../templates/app-api/js/next.config.mjs | 15 ------- 5 files changed, 99 deletions(-) delete mode 100644 packages/create-next-app/templates/app-api/js/.env.example delete mode 100644 packages/create-next-app/templates/app-api/js/README-template.md delete mode 100644 packages/create-next-app/templates/app-api/js/gitignore delete mode 100644 packages/create-next-app/templates/app-api/js/jsconfig.json delete mode 100644 packages/create-next-app/templates/app-api/js/next.config.mjs diff --git a/packages/create-next-app/templates/app-api/js/.env.example b/packages/create-next-app/templates/app-api/js/.env.example deleted file mode 100644 index ce586fad292cd..0000000000000 --- a/packages/create-next-app/templates/app-api/js/.env.example +++ /dev/null @@ -1,3 +0,0 @@ -# Rename this file to `.env.local` to use environment variables locally with `next dev` -# https://nextjs.org/docs/app/building-your-application/configuring/environment-variables -MY_HOST="example.com" diff --git a/packages/create-next-app/templates/app-api/js/README-template.md b/packages/create-next-app/templates/app-api/js/README-template.md deleted file mode 100644 index 51e5207ba7d8f..0000000000000 --- a/packages/create-next-app/templates/app-api/js/README-template.md +++ /dev/null @@ -1,34 +0,0 @@ -This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/create-next-app). - -## Getting Started - -First, run the development server: - -```bash -npm run dev -# or -yarn dev -# or -pnpm dev -# or -bun dev -``` - -Open [http://localhost:3000/api](http://localhost:3000/api) with your browser to see the result. - -You can start editing the page by modifying `app/index.js`. The page auto-updates as you edit the file. - -## Learn More - -To learn more about Next.js, take a look at the following resources: - -- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. -- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. - -You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! - -## Deploy on Vercel - -The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. - -Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. diff --git a/packages/create-next-app/templates/app-api/js/gitignore b/packages/create-next-app/templates/app-api/js/gitignore deleted file mode 100644 index 26b002aac1dd1..0000000000000 --- a/packages/create-next-app/templates/app-api/js/gitignore +++ /dev/null @@ -1,40 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.* -.yarn/* -!.yarn/patches -!.yarn/plugins -!.yarn/releases -!.yarn/versions - -# testing -/coverage - -# next.js -/.next/ -/out/ - -# production -/build - -# misc -.DS_Store -*.pem - -# debug -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# env files (can opt-in for commiting if needed) -.env* - -# vercel -.vercel - -# typescript -*.tsbuildinfo -next-env.d.ts diff --git a/packages/create-next-app/templates/app-api/js/jsconfig.json b/packages/create-next-app/templates/app-api/js/jsconfig.json deleted file mode 100644 index 2a2e4b3bf8ba1..0000000000000 --- a/packages/create-next-app/templates/app-api/js/jsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "compilerOptions": { - "paths": { - "@/*": ["./*"] - } - } -} diff --git a/packages/create-next-app/templates/app-api/js/next.config.mjs b/packages/create-next-app/templates/app-api/js/next.config.mjs deleted file mode 100644 index ea1e35bee0061..0000000000000 --- a/packages/create-next-app/templates/app-api/js/next.config.mjs +++ /dev/null @@ -1,15 +0,0 @@ -/** @type {import('next').NextConfig} */ -const nextConfig = { - // API routes configuration - api: { - bodyParser: { - sizeLimit: "1mb", - }, - cors: { - origin: "*", - methods: ["GET", "POST", "PUT", "DELETE"], - }, - }, -}; - -export default nextConfig; From d290b262793c5ec4e0ead8051997fe3d978857f3 Mon Sep 17 00:00:00 2001 From: devjiwonchoi Date: Fri, 16 Aug 2024 03:17:07 +0900 Subject: [PATCH 33/50] js --- .../templates/app-api/js/.env.example | 3 + .../templates/app-api/js/README-template.md | 79 +++++++++++++++++++ .../app-api/js/app/[...not-found]/route.js | 3 + .../app-api/js/app/methods/[method]/route.js | 58 ++++++++++++++ .../app-api/js/app/methods/methods.js | 9 +++ .../templates/app-api/js/app/methods/route.js | 11 +++ .../app-api/js/app/pokemon/[id]/route.js | 11 +++ .../templates/app-api/js/app/pokemon/route.js | 51 ++++++++++++ .../templates/app-api/js/app/route.js | 23 ++++++ .../app-api/js/app/with-middleware.js | 61 ++++++++++++++ .../templates/app-api/js/gitignore | 40 ++++++++++ .../templates/app-api/js/jsconfig.json | 7 ++ .../templates/app-api/js/next.config.mjs | 4 + 13 files changed, 360 insertions(+) create mode 100644 packages/create-next-app/templates/app-api/js/.env.example create mode 100644 packages/create-next-app/templates/app-api/js/README-template.md create mode 100644 packages/create-next-app/templates/app-api/js/app/[...not-found]/route.js create mode 100644 packages/create-next-app/templates/app-api/js/app/methods/[method]/route.js create mode 100644 packages/create-next-app/templates/app-api/js/app/methods/methods.js create mode 100644 packages/create-next-app/templates/app-api/js/app/methods/route.js create mode 100644 packages/create-next-app/templates/app-api/js/app/pokemon/[id]/route.js create mode 100644 packages/create-next-app/templates/app-api/js/app/pokemon/route.js create mode 100644 packages/create-next-app/templates/app-api/js/app/route.js create mode 100644 packages/create-next-app/templates/app-api/js/app/with-middleware.js create mode 100644 packages/create-next-app/templates/app-api/js/gitignore create mode 100644 packages/create-next-app/templates/app-api/js/jsconfig.json create mode 100644 packages/create-next-app/templates/app-api/js/next.config.mjs diff --git a/packages/create-next-app/templates/app-api/js/.env.example b/packages/create-next-app/templates/app-api/js/.env.example new file mode 100644 index 0000000000000..ce586fad292cd --- /dev/null +++ b/packages/create-next-app/templates/app-api/js/.env.example @@ -0,0 +1,3 @@ +# Rename this file to `.env.local` to use environment variables locally with `next dev` +# https://nextjs.org/docs/app/building-your-application/configuring/environment-variables +MY_HOST="example.com" diff --git a/packages/create-next-app/templates/app-api/js/README-template.md b/packages/create-next-app/templates/app-api/js/README-template.md new file mode 100644 index 0000000000000..58746e0ed01a5 --- /dev/null +++ b/packages/create-next-app/templates/app-api/js/README-template.md @@ -0,0 +1,79 @@ +This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/create-next-app). + +## Getting Started + +First, run the development server: + +```bash +npm run dev +# or +yarn dev +# or +pnpm dev +# or +bun dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `app/route.js`. The page auto-updates as you edit the file. + +## Learn More + +To learn more about Next.js, take a look at the following resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. + +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! + +## Deploy on Vercel + +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. + +## API Routes + +This directory contains example API routes for the headless API app. + +Here's an overview of the available routes: + +1. Root Route (`/`): + + - Provides a list of available API endpoints. + - Useful for discovering what the API offers. + +2. Pokemon Routes: + + - List all Pokemon (`/pokemon`): Get an overview of available Pokemon. + - Filter Pokemon by type (`/pokemon?type=grass`): Find Pokemon of a specific element. + - Get a specific Pokemon (`/pokemon/25`): Fetch details about a particular Pokemon by ID. + +3. HTTP Methods Route (`/methods`): + + - Information about supported HTTP methods. + - Great for learning about various types of API requests. + +4. Specific HTTP Method Routes (`/methods/[method]`): + + - Demonstrates different HTTP methods (GET, POST, PUT, DELETE, PATCH). + - Helps understand how different types of API requests behave. + +5. Not Found Route: + - Handles requests to undefined routes. + - Returns a "Not Found" response for non-existent endpoints. + +Each route is wrapped with middleware that adds: + +- Request logging for monitoring and debugging. +- Origin checking for security. +- Authentication placeholders for future expansion. + +To explore these routes: + +1. Start the development server. +2. Open your browser and navigate to `http://localhost:3000/[route]`. +3. Examine the responses to understand how each route works. + +For more details on the implementation, check the corresponding files in the `app` directory. diff --git a/packages/create-next-app/templates/app-api/js/app/[...not-found]/route.js b/packages/create-next-app/templates/app-api/js/app/[...not-found]/route.js new file mode 100644 index 0000000000000..f504a7229813c --- /dev/null +++ b/packages/create-next-app/templates/app-api/js/app/[...not-found]/route.js @@ -0,0 +1,3 @@ +export async function GET() { + return new Response("Not Found", { status: 404 }); +} diff --git a/packages/create-next-app/templates/app-api/js/app/methods/[method]/route.js b/packages/create-next-app/templates/app-api/js/app/methods/[method]/route.js new file mode 100644 index 0000000000000..2172b78479e14 --- /dev/null +++ b/packages/create-next-app/templates/app-api/js/app/methods/[method]/route.js @@ -0,0 +1,58 @@ +import { NextResponse } from "next/server"; +import { methods } from "../methods"; +import { withMiddleware } from "@/app/with-middleware"; + +export const GET = withMiddleware(async (request, context) => { + const method = context.params.method.toUpperCase(); + if (!methods.includes(method)) { + return NextResponse.json( + { error: `Method ${method} is not supported.` }, + { status: 404 }, + ); + } + + if (method === "GET") { + return NextResponse.json({ method: "GET" }); + } + + const url = request.nextUrl.toString(); + const res = await fetch(url, { method }); + + // if method is HEAD, body is null + if (method === "HEAD") { + const { ok, statusText, status, body } = res; + return NextResponse.json({ + ok, + statusText, + status, + body, + }); + } + + const data = await res.json(); + return NextResponse.json(data); +}); + +export async function POST() { + return NextResponse.json({ method: "POST" }); +} + +export async function PUT() { + return NextResponse.json({ method: "PUT" }); +} + +export async function DELETE() { + return NextResponse.json({ method: "DELETE" }); +} + +export async function PATCH() { + return NextResponse.json({ method: "PATCH" }); +} + +export async function OPTIONS() { + return NextResponse.json({ method: "OPTIONS" }); +} + +export async function HEAD() { + return new Response(null, { status: 200 }); +} diff --git a/packages/create-next-app/templates/app-api/js/app/methods/methods.js b/packages/create-next-app/templates/app-api/js/app/methods/methods.js new file mode 100644 index 0000000000000..ed887fff74f26 --- /dev/null +++ b/packages/create-next-app/templates/app-api/js/app/methods/methods.js @@ -0,0 +1,9 @@ +export const methods = [ + "GET", + "POST", + "PUT", + "DELETE", + "PATCH", + "OPTIONS", + "HEAD", +]; diff --git a/packages/create-next-app/templates/app-api/js/app/methods/route.js b/packages/create-next-app/templates/app-api/js/app/methods/route.js new file mode 100644 index 0000000000000..57c81aefd534b --- /dev/null +++ b/packages/create-next-app/templates/app-api/js/app/methods/route.js @@ -0,0 +1,11 @@ +import { NextResponse } from "next/server"; +import { methods } from "./methods"; + +export async function GET(request) { + const url = request.nextUrl.toString(); + const apis = methods.map((method) => { + return { path: `${url}/${method}`, description: `${method} method` }; + }); + + return NextResponse.json(apis); +} diff --git a/packages/create-next-app/templates/app-api/js/app/pokemon/[id]/route.js b/packages/create-next-app/templates/app-api/js/app/pokemon/[id]/route.js new file mode 100644 index 0000000000000..b8186e60438fe --- /dev/null +++ b/packages/create-next-app/templates/app-api/js/app/pokemon/[id]/route.js @@ -0,0 +1,11 @@ +import { NextResponse } from "next/server"; +import { withMiddleware } from "@/app/with-middleware"; + +export const GET = withMiddleware(async (_request, context) => { + const res = await fetch( + `https://api.vercel.app/pokemon/${context.params.id}`, + ); + const pokemon = await res.json(); + + return NextResponse.json(pokemon); +}); diff --git a/packages/create-next-app/templates/app-api/js/app/pokemon/route.js b/packages/create-next-app/templates/app-api/js/app/pokemon/route.js new file mode 100644 index 0000000000000..7e840038db686 --- /dev/null +++ b/packages/create-next-app/templates/app-api/js/app/pokemon/route.js @@ -0,0 +1,51 @@ +import { NextResponse } from "next/server"; +import { withMiddleware } from "@/app/with-middleware"; + +export const GET = withMiddleware(async (request) => { + const url = request.nextUrl.toString(); + + const { searchParams } = request.nextUrl; + const type = searchParams.get("type"); + + if (type && !POKEMON_TYPES.includes(type)) { + return NextResponse.json( + { error: `Invalid type: '${type}'`, types: POKEMON_TYPES }, + { status: 400 }, + ); + } + + const res = await fetch( + `https://api.vercel.app/pokemon${type ? `?type=${type}` : ""}`, + ); + const data = await res.json(); + + const pokedex = data.map((pokemon) => { + return { + ...pokemon, + url: `${url}/${pokemon.id}`, + }; + }); + + return NextResponse.json(pokedex); +}); + +const POKEMON_TYPES = [ + "normal", + "fire", + "water", + "electric", + "grass", + "ice", + "fighting", + "poison", + "ground", + "flying", + "psychic", + "bug", + "rock", + "ghost", + "dragon", + "dark", + "steel", + "fairy", +]; diff --git a/packages/create-next-app/templates/app-api/js/app/route.js b/packages/create-next-app/templates/app-api/js/app/route.js new file mode 100644 index 0000000000000..31de4cb5e6be7 --- /dev/null +++ b/packages/create-next-app/templates/app-api/js/app/route.js @@ -0,0 +1,23 @@ +import { NextResponse } from "next/server"; + +const paths = [ + { path: "pokemon", description: "gotta catch 'em all!" }, + { path: "pokemon?type=grass", description: "sort pokemon by grass type" }, + { path: "pokemon/25", description: "who's that pokemon?" }, + { path: "methods", description: "list of methods" }, +]; + +export async function GET(request) { + const url = request.nextUrl.toString(); + const apis = paths.map(({ path, description }) => { + return { path: url + path, description }; + }); + + return NextResponse.json(apis, { + headers: { + // CORS: https://nextjs.org/docs/app/building-your-application/routing/route-handlers#cors + "Access-Control-Allow-Origin": "*", + "Access-Control-Allow-Methods": "GET", + }, + }); +} diff --git a/packages/create-next-app/templates/app-api/js/app/with-middleware.js b/packages/create-next-app/templates/app-api/js/app/with-middleware.js new file mode 100644 index 0000000000000..5149fc08483a3 --- /dev/null +++ b/packages/create-next-app/templates/app-api/js/app/with-middleware.js @@ -0,0 +1,61 @@ +import { NextRequest, NextResponse } from "next/server"; + +export function withMiddleware(handler) { + return async (req, ctx) => { + try { + console.log(`Middleware: Request to ${req.method} ${req.url}`); + + // Check if the request is coming from an allowed origin + if (!isAllowedOrigin(req)) { + return NextResponse.json({ error: "Forbidden" }, { status: 403 }); + } + + // Placeholder for authentication logic + const isAuthenticated = await checkAuth(req); + if (!isAuthenticated) { + return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); + } + + // Call the original handler + const result = await handler(req, ctx); + + // Log after executing the handler + console.log(`Middleware: Completed ${req.method} ${req.url}`); + + return result; + } catch (error) { + console.error( + `Middleware: Error in route handler: ${req.method} ${req.url}`, + error, + ); + return NextResponse.json( + { error: "Internal Server Error" }, + { status: 500 }, + ); + } + }; +} + +function isAllowedOrigin(req) { + const referer = req.headers.get("referer"); + const host = req.headers.get("host"); + + // Allow requests from the same origin + if (referer && host) { + const refererUrl = new URL(referer); + return refererUrl.host === host; + } + + if (process.env.NODE_ENV === "development" && host?.includes("localhost")) { + return true; + } + + return false; +} + +async function checkAuth(req) { + // Placeholder authentication logic + // You can implement your actual auth check here + const authToken = req.headers.get("authorization"); + return true; // For now, always return true +} diff --git a/packages/create-next-app/templates/app-api/js/gitignore b/packages/create-next-app/templates/app-api/js/gitignore new file mode 100644 index 0000000000000..8777267507c0e --- /dev/null +++ b/packages/create-next-app/templates/app-api/js/gitignore @@ -0,0 +1,40 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env*.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/packages/create-next-app/templates/app-api/js/jsconfig.json b/packages/create-next-app/templates/app-api/js/jsconfig.json new file mode 100644 index 0000000000000..2a2e4b3bf8ba1 --- /dev/null +++ b/packages/create-next-app/templates/app-api/js/jsconfig.json @@ -0,0 +1,7 @@ +{ + "compilerOptions": { + "paths": { + "@/*": ["./*"] + } + } +} diff --git a/packages/create-next-app/templates/app-api/js/next.config.mjs b/packages/create-next-app/templates/app-api/js/next.config.mjs new file mode 100644 index 0000000000000..4678774e6d606 --- /dev/null +++ b/packages/create-next-app/templates/app-api/js/next.config.mjs @@ -0,0 +1,4 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = {}; + +export default nextConfig; From 6dfb7d511fca7fc9747e25ce0dc7ecf4d9cc659e Mon Sep 17 00:00:00 2001 From: devjiwonchoi Date: Fri, 16 Aug 2024 04:06:37 +0900 Subject: [PATCH 34/50] update id to number --- .../app-api/js/app/pokemon/{[id] => [number]}/route.js | 2 +- .../app-api/ts/app/pokemon/{[id] => [number]}/route.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename packages/create-next-app/templates/app-api/js/app/pokemon/{[id] => [number]}/route.js (81%) rename packages/create-next-app/templates/app-api/ts/app/pokemon/{[id] => [number]}/route.ts (81%) diff --git a/packages/create-next-app/templates/app-api/js/app/pokemon/[id]/route.js b/packages/create-next-app/templates/app-api/js/app/pokemon/[number]/route.js similarity index 81% rename from packages/create-next-app/templates/app-api/js/app/pokemon/[id]/route.js rename to packages/create-next-app/templates/app-api/js/app/pokemon/[number]/route.js index b8186e60438fe..ebc5f78f9ef7e 100644 --- a/packages/create-next-app/templates/app-api/js/app/pokemon/[id]/route.js +++ b/packages/create-next-app/templates/app-api/js/app/pokemon/[number]/route.js @@ -3,7 +3,7 @@ import { withMiddleware } from "@/app/with-middleware"; export const GET = withMiddleware(async (_request, context) => { const res = await fetch( - `https://api.vercel.app/pokemon/${context.params.id}`, + `https://api.vercel.app/pokemon/${context.params.number}`, ); const pokemon = await res.json(); diff --git a/packages/create-next-app/templates/app-api/ts/app/pokemon/[id]/route.ts b/packages/create-next-app/templates/app-api/ts/app/pokemon/[number]/route.ts similarity index 81% rename from packages/create-next-app/templates/app-api/ts/app/pokemon/[id]/route.ts rename to packages/create-next-app/templates/app-api/ts/app/pokemon/[number]/route.ts index b8186e60438fe..ebc5f78f9ef7e 100644 --- a/packages/create-next-app/templates/app-api/ts/app/pokemon/[id]/route.ts +++ b/packages/create-next-app/templates/app-api/ts/app/pokemon/[number]/route.ts @@ -3,7 +3,7 @@ import { withMiddleware } from "@/app/with-middleware"; export const GET = withMiddleware(async (_request, context) => { const res = await fetch( - `https://api.vercel.app/pokemon/${context.params.id}`, + `https://api.vercel.app/pokemon/${context.params.number}`, ); const pokemon = await res.json(); From 4b4599158f1104a808efd06ddfee718f73ba0993 Mon Sep 17 00:00:00 2001 From: devjiwonchoi Date: Fri, 16 Aug 2024 04:07:25 +0900 Subject: [PATCH 35/50] update --- packages/create-next-app/create-app.ts | 12 +---- packages/create-next-app/index.ts | 6 +-- packages/create-next-app/templates/index.ts | 60 ++++++++++++--------- 3 files changed, 41 insertions(+), 37 deletions(-) diff --git a/packages/create-next-app/create-app.ts b/packages/create-next-app/create-app.ts index 5202de297ac88..380d5df0ebd67 100644 --- a/packages/create-next-app/create-app.ts +++ b/packages/create-next-app/create-app.ts @@ -56,15 +56,7 @@ export async function createApp({ }): Promise { let repoInfo: RepoInfo | undefined const mode: TemplateMode = typescript ? 'ts' : 'js' - const template: TemplateType = api - ? 'app-api' - : `${app ? 'app' : 'default'}${tailwind ? '-tw' : ''}${empty ? '-empty' : ''}` - - // Disable tailwind and eslint for API template - if (api) { - tailwind = false - eslint = false - } + const template: TemplateType = `${app ? 'app' : 'default'}${tailwind ? '-tw' : ''}${empty ? '-empty' : ''}` if (example) { let repoUrl: URL | undefined @@ -232,7 +224,7 @@ export async function createApp({ await installTemplate({ appName, root, - template, + template: api ? 'app-api' : template, mode, packageManager, isOnline, diff --git a/packages/create-next-app/index.ts b/packages/create-next-app/index.ts index 8ce66762313e2..bb66e69d7b271 100644 --- a/packages/create-next-app/index.ts +++ b/packages/create-next-app/index.ts @@ -274,7 +274,7 @@ async function run(): Promise { } } - if (!opts.eslint && !args.includes('--no-eslint')) { + if (!opts.eslint && !args.includes('--no-eslint') && !opts.api) { if (skipPrompt) { opts.eslint = getPrefOrDefault('eslint') } else { @@ -293,7 +293,7 @@ async function run(): Promise { } } - if (!opts.tailwind && !args.includes('--no-tailwind')) { + if (!opts.tailwind && !args.includes('--no-tailwind') && !opts.api) { if (skipPrompt) { opts.tailwind = getPrefOrDefault('tailwind') } else { @@ -331,7 +331,7 @@ async function run(): Promise { } } - if (!opts.app && !args.includes('--no-app')) { + if (!opts.app && !args.includes('--no-app') && !opts.api) { if (skipPrompt) { opts.app = getPrefOrDefault('app') } else { diff --git a/packages/create-next-app/templates/index.ts b/packages/create-next-app/templates/index.ts index 6dbbb45582e70..7d4a5ae2b8968 100644 --- a/packages/create-next-app/templates/index.ts +++ b/packages/create-next-app/templates/index.ts @@ -47,6 +47,7 @@ export const installTemplate = async ({ * Copy the template files to the target directory. */ console.log("\nInitializing project with template:", template, "\n"); + const isApi = template === "app-api"; const templatePath = path.join(__dirname, template, mode); const copySource = ["**"]; if (!eslint) copySource.push("!eslintrc.json"); @@ -134,35 +135,37 @@ export const installTemplate = async ({ }), ); - const isAppTemplate = template.startsWith("app"); + if (!isApi) { + const isAppTemplate = template.startsWith("app"); - // Change the `Get started by editing pages/index` / `app/page` to include `src` - const indexPageFile = path.join( - "src", - isAppTemplate ? "app" : "pages", - `${isAppTemplate ? "page" : "index"}.${mode === "ts" ? "tsx" : "js"}`, - ); - - await fs.writeFile( - indexPageFile, - (await fs.readFile(indexPageFile, "utf8")).replace( - isAppTemplate ? "app/page" : "pages/index", - isAppTemplate ? "src/app/page" : "src/pages/index", - ), - ); - - if (tailwind) { - const tailwindConfigFile = path.join( - root, - mode === "ts" ? "tailwind.config.ts" : "tailwind.config.js", + // Change the `Get started by editing pages/index` / `app/page` to include `src` + const indexPageFile = path.join( + "src", + isAppTemplate ? "app" : "pages", + `${isAppTemplate ? "page" : "index"}.${mode === "ts" ? "tsx" : "js"}`, ); + await fs.writeFile( - tailwindConfigFile, - (await fs.readFile(tailwindConfigFile, "utf8")).replace( - /\.\/(\w+)\/\*\*\/\*\.\{js,ts,jsx,tsx,mdx\}/g, - "./src/$1/**/*.{js,ts,jsx,tsx,mdx}", + indexPageFile, + (await fs.readFile(indexPageFile, "utf8")).replace( + isAppTemplate ? "app/page" : "pages/index", + isAppTemplate ? "src/app/page" : "src/pages/index", ), ); + + if (tailwind) { + const tailwindConfigFile = path.join( + root, + mode === "ts" ? "tailwind.config.ts" : "tailwind.config.js", + ); + await fs.writeFile( + tailwindConfigFile, + (await fs.readFile(tailwindConfigFile, "utf8")).replace( + /\.\/(\w+)\/\*\*\/\*\.\{js,ts,jsx,tsx,mdx\}/g, + "./src/$1/**/*.{js,ts,jsx,tsx,mdx}", + ), + ); + } } } @@ -222,6 +225,15 @@ export const installTemplate = async ({ }; } + if (isApi) { + delete packageJson.dependencies.react; + delete packageJson.dependencies["react-dom"]; + delete packageJson.devDependencies["@types/react"]; + delete packageJson.devDependencies["@types/react-dom"]; + + delete packageJson.scripts.lint; + } + const devDeps = Object.keys(packageJson.devDependencies).length; if (!devDeps) delete packageJson.devDependencies; From 906539c4d69f6e284775c19a1c44a3204f0a5c60 Mon Sep 17 00:00:00 2001 From: devjiwonchoi Date: Fri, 16 Aug 2024 04:59:52 +0900 Subject: [PATCH 36/50] add test --- .../create-next-app/lib/specification.ts | 31 ++- .../create-next-app/templates/app-api.test.ts | 186 ++++++++++++++++++ test/integration/create-next-app/utils.ts | 4 + 3 files changed, 216 insertions(+), 5 deletions(-) create mode 100644 test/integration/create-next-app/templates/app-api.test.ts diff --git a/test/integration/create-next-app/lib/specification.ts b/test/integration/create-next-app/lib/specification.ts index 81f498ec638fd..bd35f23b524b5 100644 --- a/test/integration/create-next-app/lib/specification.ts +++ b/test/integration/create-next-app/lib/specification.ts @@ -173,14 +173,35 @@ export const projectSpecification: ProjectSpecification = { }, 'app-api': { js: { - deps: [], + deps: ['next'], devDeps: [], - files: ['app/route.js', 'jsconfig.json'], + files: [ + 'app/[...not-found]/route.js', + 'app/methods/[method]/route.js', + 'app/methods/methods.js', + 'app/methods/route.js', + 'app/pokemon/[number]/route.js', + 'app/pokemon/route.js', + 'app/route.js', + 'app/with-middleware.js', + 'jsconfig.json', + ], }, ts: { - deps: [], - devDeps: ['@types/node', '@types/react', 'typescript'], - files: ['app/route.ts', 'tsconfig.json', 'next-env.d.ts'], + deps: ['next'], + devDeps: ['@types/node', 'typescript'], + files: [ + 'app/[...not-found]/route.ts', + 'app/methods/[method]/route.ts', + 'app/methods/methods.ts', + 'app/methods/route.ts', + 'app/pokemon/[number]/route.ts', + 'app/pokemon/route.ts', + 'app/route.ts', + 'app/with-middleware.ts', + 'tsconfig.json', + 'next-env.d.ts', + ], }, }, 'app-empty': { diff --git a/test/integration/create-next-app/templates/app-api.test.ts b/test/integration/create-next-app/templates/app-api.test.ts new file mode 100644 index 0000000000000..ab32f605160e7 --- /dev/null +++ b/test/integration/create-next-app/templates/app-api.test.ts @@ -0,0 +1,186 @@ +import { join } from 'node:path' +import { + projectShouldHaveNoGitChanges, + tryNextDev, + run, + useTempDir, + projectFilesShouldExist, +} from '../utils' +import { mapSrcFiles, projectSpecification } from '../lib/specification' +import { projectDepsShouldBe } from '../lib/utils' + +function shouldBeApiTemplateProject({ + cwd, + projectName, + mode, + srcDir, +}: { + cwd: string + projectName: string + mode: 'js' | 'ts' + srcDir?: boolean +}) { + const template = 'app-api' + + projectFilesShouldExist({ + cwd, + projectName, + files: mapSrcFiles(projectSpecification[template][mode].files, srcDir), + }) + + projectDepsShouldBe({ + type: 'dependencies', + cwd, + projectName, + deps: mapSrcFiles(projectSpecification[template][mode].deps, srcDir), + }) + + projectDepsShouldBe({ + type: 'devDependencies', + cwd, + projectName, + deps: mapSrcFiles(projectSpecification[template][mode].devDeps, srcDir), + }) +} + +describe('create-next-app --api (Headless App)', () => { + let nextTgzFilename: string + + beforeAll(() => { + if (!process.env.NEXT_TEST_PKG_PATHS) { + throw new Error('This test needs to be run with `node run-tests.js`.') + } + + const pkgPaths = new Map( + JSON.parse(process.env.NEXT_TEST_PKG_PATHS) + ) + + nextTgzFilename = pkgPaths.get('next')! + }) + + it('should create JavaScript project with --js flag', async () => { + await useTempDir(async (cwd) => { + const projectName = 'app-js' + const { exitCode } = await run( + [ + projectName, + '--js', + '--api', + '--no-turbo', + '--no-src-dir', + '--no-import-alias', + ], + nextTgzFilename, + { + cwd, + } + ) + + expect(exitCode).toBe(0) + shouldBeApiTemplateProject({ + cwd, + projectName, + mode: 'js', + }) + await tryNextDev({ + cwd, + isApi: true, + projectName, + }) + }) + }) + + it('should create TypeScript project with --ts flag', async () => { + await useTempDir(async (cwd) => { + const projectName = 'app-ts' + const { exitCode } = await run( + [ + projectName, + '--ts', + '--api', + '--no-turbo', + '--no-src-dir', + '--no-import-alias', + ], + nextTgzFilename, + { + cwd, + } + ) + + expect(exitCode).toBe(0) + shouldBeApiTemplateProject({ + cwd, + projectName, + mode: 'ts', + }) + await tryNextDev({ cwd, isApi: true, projectName }) + projectShouldHaveNoGitChanges({ cwd, projectName }) + }) + }) + + it('should create project inside "src" directory with --src-dir flag', async () => { + await useTempDir(async (cwd) => { + const projectName = 'app-src-dir' + const { exitCode } = await run( + [ + projectName, + '--ts', + '--api', + '--no-turbo', + '--src-dir', + '--no-import-alias', + ], + nextTgzFilename, + { + cwd, + stdio: 'inherit', + } + ) + + expect(exitCode).toBe(0) + shouldBeApiTemplateProject({ + cwd, + projectName, + mode: 'ts', + srcDir: true, + }) + await tryNextDev({ + cwd, + isApi: true, + projectName, + }) + }) + }) + + it('should enable turbopack dev with --turbo flag', async () => { + await useTempDir(async (cwd) => { + const projectName = 'app-turbo' + const { exitCode } = await run( + [ + projectName, + '--ts', + '--api', + '--turbo', + '--no-src-dir', + '--no-import-alias', + ], + nextTgzFilename, + { + cwd, + } + ) + + expect(exitCode).toBe(0) + const projectRoot = join(cwd, projectName) + const pkgJson = require(join(projectRoot, 'package.json')) + expect(pkgJson.scripts.dev).toBe('next dev --turbo') + + await tryNextDev({ + cwd, + isApi: true, + projectName, + }) + }) + }) +}) diff --git a/test/integration/create-next-app/utils.ts b/test/integration/create-next-app/utils.ts index c0097e5d6af06..1482c859012a6 100644 --- a/test/integration/create-next-app/utils.ts +++ b/test/integration/create-next-app/utils.ts @@ -46,11 +46,13 @@ export async function tryNextDev({ cwd, projectName, isApp = true, + isApi = false, isEmpty = false, }: { cwd: string projectName: string isApp?: boolean + isApi?: boolean isEmpty?: boolean }) { const dir = join(cwd, projectName) @@ -63,6 +65,8 @@ export async function tryNextDev({ const res = await fetchViaHTTP(port, '/') if (isEmpty) { expect(await res.text()).toContain('Hello world!') + } else if (isApi) { + expect(await res.text()).toContain("who's that pokemon?") } else { expect(await res.text()).toContain('Get started by editing') } From db6c611d9981708e82871c108ed3b480b39d4372 Mon Sep 17 00:00:00 2001 From: Arindam Majumder <109217591+Arindam200@users.noreply.github.com> Date: Fri, 16 Aug 2024 12:49:50 +0530 Subject: [PATCH 37/50] remove NextRequest type --- .../create-next-app/templates/app-api/js/app/with-middleware.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/create-next-app/templates/app-api/js/app/with-middleware.js b/packages/create-next-app/templates/app-api/js/app/with-middleware.js index 5149fc08483a3..fd9a6a28eacbb 100644 --- a/packages/create-next-app/templates/app-api/js/app/with-middleware.js +++ b/packages/create-next-app/templates/app-api/js/app/with-middleware.js @@ -1,4 +1,4 @@ -import { NextRequest, NextResponse } from "next/server"; +import { NextResponse } from "next/server"; export function withMiddleware(handler) { return async (req, ctx) => { From d6a48aad4ee79f258becb1d33c45ebe071200e33 Mon Sep 17 00:00:00 2001 From: Arindam Majumder <109217591+Arindam200@users.noreply.github.com> Date: Fri, 16 Aug 2024 13:00:59 +0530 Subject: [PATCH 38/50] add `type` NextRequest --- .../create-next-app/templates/app-api/ts/app/with-middleware.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/create-next-app/templates/app-api/ts/app/with-middleware.ts b/packages/create-next-app/templates/app-api/ts/app/with-middleware.ts index 026430e105467..b95b60aa9a066 100644 --- a/packages/create-next-app/templates/app-api/ts/app/with-middleware.ts +++ b/packages/create-next-app/templates/app-api/ts/app/with-middleware.ts @@ -1,4 +1,4 @@ -import { NextRequest, NextResponse } from "next/server"; +import { type NextRequest, NextResponse } from "next/server"; type RouteHandler> = ( req: NextRequest, From e7ac0a4fc25d2c2ca4ecb40f4d9155dcc393b9ea Mon Sep 17 00:00:00 2001 From: devjiwonchoi Date: Thu, 22 Aug 2024 02:36:16 +0900 Subject: [PATCH 39/50] test: remove git diff --- .../create-next-app/templates/app-api.test.ts | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/test/integration/create-next-app/templates/app-api.test.ts b/test/integration/create-next-app/templates/app-api.test.ts index ab32f605160e7..2b34402be20f3 100644 --- a/test/integration/create-next-app/templates/app-api.test.ts +++ b/test/integration/create-next-app/templates/app-api.test.ts @@ -1,11 +1,5 @@ import { join } from 'node:path' -import { - projectShouldHaveNoGitChanges, - tryNextDev, - run, - useTempDir, - projectFilesShouldExist, -} from '../utils' +import { tryNextDev, run, useTempDir, projectFilesShouldExist } from '../utils' import { mapSrcFiles, projectSpecification } from '../lib/specification' import { projectDepsShouldBe } from '../lib/utils' @@ -115,7 +109,6 @@ describe('create-next-app --api (Headless App)', () => { mode: 'ts', }) await tryNextDev({ cwd, isApi: true, projectName }) - projectShouldHaveNoGitChanges({ cwd, projectName }) }) }) From a32bb608d07c4de31de72be0ab8a413c4f428b8f Mon Sep 17 00:00:00 2001 From: devjiwonchoi Date: Thu, 22 Aug 2024 02:54:31 +0900 Subject: [PATCH 40/50] cannot delete `@types/react` yet --- packages/create-next-app/templates/index.ts | 7 ++++++- test/integration/create-next-app/lib/specification.ts | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/create-next-app/templates/index.ts b/packages/create-next-app/templates/index.ts index 8dddf09f640f9..af97c28efdf10 100644 --- a/packages/create-next-app/templates/index.ts +++ b/packages/create-next-app/templates/index.ts @@ -228,7 +228,12 @@ export const installTemplate = async ({ if (isApi) { delete packageJson.dependencies.react; delete packageJson.dependencies["react-dom"]; - delete packageJson.devDependencies["@types/react"]; + // We cannot delete `@types/react` now since it is used in + // route type definitions e.g. `.next/types/app/page.ts`. + // TODO(jiwon): Implement this when we added logic to + // auto-install `react` and `react-dom` if page.tsx was used. + // We can achieve this during verify-typescript stage and see + // if a type error was thrown at `distDir/types/app/page.ts`. delete packageJson.devDependencies["@types/react-dom"]; delete packageJson.scripts.lint; diff --git a/test/integration/create-next-app/lib/specification.ts b/test/integration/create-next-app/lib/specification.ts index bd35f23b524b5..1ad34bc97a7f9 100644 --- a/test/integration/create-next-app/lib/specification.ts +++ b/test/integration/create-next-app/lib/specification.ts @@ -189,7 +189,7 @@ export const projectSpecification: ProjectSpecification = { }, ts: { deps: ['next'], - devDeps: ['@types/node', 'typescript'], + devDeps: ['@types/node', '@types/react', 'typescript'], files: [ 'app/[...not-found]/route.ts', 'app/methods/[method]/route.ts', From ef2c545acbb7ef74d3219435d56fa728cc957d62 Mon Sep 17 00:00:00 2001 From: devjiwonchoi Date: Thu, 22 Aug 2024 02:58:11 +0900 Subject: [PATCH 41/50] Revert "test: remove git diff" This reverts commit e7ac0a4fc25d2c2ca4ecb40f4d9155dcc393b9ea. --- .../create-next-app/templates/app-api.test.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/test/integration/create-next-app/templates/app-api.test.ts b/test/integration/create-next-app/templates/app-api.test.ts index 2b34402be20f3..ab32f605160e7 100644 --- a/test/integration/create-next-app/templates/app-api.test.ts +++ b/test/integration/create-next-app/templates/app-api.test.ts @@ -1,5 +1,11 @@ import { join } from 'node:path' -import { tryNextDev, run, useTempDir, projectFilesShouldExist } from '../utils' +import { + projectShouldHaveNoGitChanges, + tryNextDev, + run, + useTempDir, + projectFilesShouldExist, +} from '../utils' import { mapSrcFiles, projectSpecification } from '../lib/specification' import { projectDepsShouldBe } from '../lib/utils' @@ -109,6 +115,7 @@ describe('create-next-app --api (Headless App)', () => { mode: 'ts', }) await tryNextDev({ cwd, isApi: true, projectName }) + projectShouldHaveNoGitChanges({ cwd, projectName }) }) }) From 2e63b3f63ceda0b66dc55ccc84c1845318afed45 Mon Sep 17 00:00:00 2001 From: devjiwonchoi Date: Thu, 12 Sep 2024 16:17:45 +0900 Subject: [PATCH 42/50] move to example --- examples/app-api/.gitignore | 40 +++++++++++ examples/app-api/README.md | 71 +++++++++++++++++++ .../app-api}/app/[...not-found]/route.ts | 0 examples/app-api/app/pokemon/[id]/route.ts | 11 +++ .../app-api}/app/pokemon/route.ts | 46 ++++++------ .../app-api/app/request-handler.ts | 53 +++++++------- examples/app-api/app/route.ts | 22 ++++++ examples/app-api/next.config.ts | 7 ++ examples/app-api/package.json | 16 +++++ examples/app-api/tsconfig.json | 27 +++++++ .../templates/app-api/js/.env.example | 3 - .../templates/app-api/js/README-template.md | 43 +---------- .../app-api/js/app/[...not-found]/route.js | 3 - .../app-api/js/app/methods/[method]/route.js | 58 --------------- .../app-api/js/app/methods/methods.js | 9 --- .../templates/app-api/js/app/methods/route.js | 11 --- .../app-api/js/app/pokemon/[number]/route.js | 11 --- .../templates/app-api/js/app/pokemon/route.js | 51 ------------- .../templates/app-api/js/app/route.js | 22 +----- .../app-api/js/app/with-middleware.js | 61 ---------------- .../templates/app-api/ts/.env.example | 4 -- .../templates/app-api/ts/README-template.md | 41 +---------- .../app-api/ts/app/methods/[method]/route.ts | 60 ---------------- .../app-api/ts/app/methods/methods.ts | 9 --- .../templates/app-api/ts/app/methods/route.ts | 11 --- .../app-api/ts/app/pokemon/[number]/route.ts | 11 --- .../templates/app-api/ts/app/route.ts | 24 +------ 27 files changed, 252 insertions(+), 473 deletions(-) create mode 100644 examples/app-api/.gitignore create mode 100644 examples/app-api/README.md rename {packages/create-next-app/templates/app-api/ts => examples/app-api}/app/[...not-found]/route.ts (100%) create mode 100644 examples/app-api/app/pokemon/[id]/route.ts rename {packages/create-next-app/templates/app-api/ts => examples/app-api}/app/pokemon/route.ts (88%) rename packages/create-next-app/templates/app-api/ts/app/with-middleware.ts => examples/app-api/app/request-handler.ts (92%) create mode 100644 examples/app-api/app/route.ts create mode 100644 examples/app-api/next.config.ts create mode 100644 examples/app-api/package.json create mode 100644 examples/app-api/tsconfig.json delete mode 100644 packages/create-next-app/templates/app-api/js/.env.example delete mode 100644 packages/create-next-app/templates/app-api/js/app/[...not-found]/route.js delete mode 100644 packages/create-next-app/templates/app-api/js/app/methods/[method]/route.js delete mode 100644 packages/create-next-app/templates/app-api/js/app/methods/methods.js delete mode 100644 packages/create-next-app/templates/app-api/js/app/methods/route.js delete mode 100644 packages/create-next-app/templates/app-api/js/app/pokemon/[number]/route.js delete mode 100644 packages/create-next-app/templates/app-api/js/app/pokemon/route.js delete mode 100644 packages/create-next-app/templates/app-api/js/app/with-middleware.js delete mode 100644 packages/create-next-app/templates/app-api/ts/.env.example delete mode 100644 packages/create-next-app/templates/app-api/ts/app/methods/[method]/route.ts delete mode 100644 packages/create-next-app/templates/app-api/ts/app/methods/methods.ts delete mode 100644 packages/create-next-app/templates/app-api/ts/app/methods/route.ts delete mode 100644 packages/create-next-app/templates/app-api/ts/app/pokemon/[number]/route.ts diff --git a/examples/app-api/.gitignore b/examples/app-api/.gitignore new file mode 100644 index 0000000000000..26b002aac1dd1 --- /dev/null +++ b/examples/app-api/.gitignore @@ -0,0 +1,40 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# env files (can opt-in for commiting if needed) +.env* + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/examples/app-api/README.md b/examples/app-api/README.md new file mode 100644 index 0000000000000..21bfcbbe35ff7 --- /dev/null +++ b/examples/app-api/README.md @@ -0,0 +1,71 @@ +# App Router Headless API + +## How to use + +Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init), [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/), or [pnpm](https://pnpm.io) to bootstrap the example: + +```bash +npx create-next-app --example app-api +``` + +```bash +yarn create next-app --example app-api +``` + +```bash +pnpm create next-app --example app-api +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `app/route.ts`. The page auto-updates as you edit the file. + +## Learn More + +To learn more about Next.js, take a look at the following resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. + +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! + +## Deploy on Vercel + +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. + +## API Routes + +This directory contains example API routes for the headless API app. + +Here's an overview of the available routes: + +1. Root Route (`/`): + + - Provides a list of available API endpoints. + - Useful for discovering what the API offers. + +2. Pokemon Routes: + + - List all Pokemon (`/pokemon`): Get an overview of available Pokemon. + - Filter Pokemon by type (`/pokemon?type=grass`): Find Pokemon of a specific element. + - Get a specific Pokemon (`/pokemon/25`): Fetch details about a particular Pokemon by ID. + +3. Not Found Route: + - Handles requests to undefined routes. + - Returns a "Not Found" response for non-existent endpoints. + +Each route is wrapped with middleware that adds: + +- Request logging for monitoring and debugging. +- Origin checking for security. +- Authentication placeholders for future expansion. + +To explore these routes: + +1. Start the development server. +2. Open your browser and navigate to `http://localhost:3000/[route]`. +3. Examine the responses to understand how each route works. + +For more details on the implementation, check the corresponding files in the `app` directory. diff --git a/packages/create-next-app/templates/app-api/ts/app/[...not-found]/route.ts b/examples/app-api/app/[...not-found]/route.ts similarity index 100% rename from packages/create-next-app/templates/app-api/ts/app/[...not-found]/route.ts rename to examples/app-api/app/[...not-found]/route.ts diff --git a/examples/app-api/app/pokemon/[id]/route.ts b/examples/app-api/app/pokemon/[id]/route.ts new file mode 100644 index 0000000000000..17745170676f5 --- /dev/null +++ b/examples/app-api/app/pokemon/[id]/route.ts @@ -0,0 +1,11 @@ +import { NextResponse } from "next/server"; +import { requestHandler } from "@/app/request-handler"; + +export const GET = requestHandler(async (_, context) => { + const res = await fetch( + `https://api.vercel.app/pokemon/${context.params.id}`, + ); + const pokemon = await res.json(); + + return NextResponse.json(pokemon); +}); diff --git a/packages/create-next-app/templates/app-api/ts/app/pokemon/route.ts b/examples/app-api/app/pokemon/route.ts similarity index 88% rename from packages/create-next-app/templates/app-api/ts/app/pokemon/route.ts rename to examples/app-api/app/pokemon/route.ts index 102e512d5efcc..f8bd2347667e5 100644 --- a/packages/create-next-app/templates/app-api/ts/app/pokemon/route.ts +++ b/examples/app-api/app/pokemon/route.ts @@ -1,7 +1,28 @@ import { NextResponse } from "next/server"; -import { withMiddleware } from "@/app/with-middleware"; +import { requestHandler } from "@/app/request-handler"; -export const GET = withMiddleware(async (request) => { +const POKEMON_TYPES = [ + "normal", + "fire", + "water", + "electric", + "grass", + "ice", + "fighting", + "poison", + "ground", + "flying", + "psychic", + "bug", + "rock", + "ghost", + "dragon", + "dark", + "steel", + "fairy", +]; + +export const GET = requestHandler(async (request) => { const url = request.nextUrl.toString(); const { searchParams } = request.nextUrl; @@ -28,24 +49,3 @@ export const GET = withMiddleware(async (request) => { return NextResponse.json(pokedex); }); - -const POKEMON_TYPES = [ - "normal", - "fire", - "water", - "electric", - "grass", - "ice", - "fighting", - "poison", - "ground", - "flying", - "psychic", - "bug", - "rock", - "ghost", - "dragon", - "dark", - "steel", - "fairy", -]; diff --git a/packages/create-next-app/templates/app-api/ts/app/with-middleware.ts b/examples/app-api/app/request-handler.ts similarity index 92% rename from packages/create-next-app/templates/app-api/ts/app/with-middleware.ts rename to examples/app-api/app/request-handler.ts index b95b60aa9a066..8802a987bed9c 100644 --- a/packages/create-next-app/templates/app-api/ts/app/with-middleware.ts +++ b/examples/app-api/app/request-handler.ts @@ -1,11 +1,36 @@ -import { type NextRequest, NextResponse } from "next/server"; +import { NextRequest, NextResponse } from "next/server"; type RouteHandler> = ( req: NextRequest, ctx: { params: T }, ) => Promise | Response; -export function withMiddleware>( +function isAllowedOrigin(req: NextRequest): boolean { + const referer = req.headers.get("referer"); + const host = req.headers.get("host"); + + // Allow requests from the same origin + if (referer && host) { + const refererUrl = new URL(referer); + return refererUrl.host === host; + } + + if (process.env.NODE_ENV === "development" && host?.includes("localhost")) { + return true; + } + + return false; +} + +async function checkAuth(req: NextRequest): Promise { + // Placeholder authentication logic + // You can implement your actual auth check here + const authToken = req.headers.get("authorization"); + console.log({ authToken }); + return true; // For now, always return true +} + +export function requestHandler>( handler: RouteHandler, ): RouteHandler { return async (req: NextRequest, ctx: { params: T }) => { @@ -42,27 +67,3 @@ export function withMiddleware>( } }; } - -function isAllowedOrigin(req: NextRequest): boolean { - const referer = req.headers.get("referer"); - const host = req.headers.get("host"); - - // Allow requests from the same origin - if (referer && host) { - const refererUrl = new URL(referer); - return refererUrl.host === host; - } - - if (process.env.NODE_ENV === "development" && host?.includes("localhost")) { - return true; - } - - return false; -} - -async function checkAuth(req: NextRequest): Promise { - // Placeholder authentication logic - // You can implement your actual auth check here - const authToken = req.headers.get("authorization"); - return true; // For now, always return true -} diff --git a/examples/app-api/app/route.ts b/examples/app-api/app/route.ts new file mode 100644 index 0000000000000..f2f9acf4207e1 --- /dev/null +++ b/examples/app-api/app/route.ts @@ -0,0 +1,22 @@ +import { type NextRequest, NextResponse } from "next/server"; + +const paths = [ + { path: "pokemon", description: "gotta catch 'em all!" }, + { path: "pokemon?type=grass", description: "sort pokemon by grass type" }, + { path: "pokemon/25", description: "who's that pokemon?" }, +]; + +export async function GET(request: NextRequest) { + const url = request.nextUrl.toString(); + const apis = paths.map(({ path, description }) => { + return { path: url + path, description }; + }); + + return NextResponse.json(apis, { + headers: { + // CORS: https://nextjs.org/docs/app/building-your-application/routing/route-handlers#cors + "Access-Control-Allow-Origin": "*", + "Access-Control-Allow-Methods": "GET", + }, + }); +} diff --git a/examples/app-api/next.config.ts b/examples/app-api/next.config.ts new file mode 100644 index 0000000000000..e9ffa3083ad27 --- /dev/null +++ b/examples/app-api/next.config.ts @@ -0,0 +1,7 @@ +import type { NextConfig } from "next"; + +const nextConfig: NextConfig = { + /* config options here */ +}; + +export default nextConfig; diff --git a/examples/app-api/package.json b/examples/app-api/package.json new file mode 100644 index 0000000000000..d6a8e427fee09 --- /dev/null +++ b/examples/app-api/package.json @@ -0,0 +1,16 @@ +{ + "private": true, + "scripts": { + "dev": "next dev --turbo", + "build": "next build", + "start": "next start" + }, + "dependencies": { + "next": "14.2.9" + }, + "devDependencies": { + "@types/node": "20.16.5", + "@types/react": "18.3.5", + "typescript": "5.6.2" + } +} diff --git a/examples/app-api/tsconfig.json b/examples/app-api/tsconfig.json new file mode 100644 index 0000000000000..d8b93235f205e --- /dev/null +++ b/examples/app-api/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "target": "ES2017", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./*"] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules"] +} diff --git a/packages/create-next-app/templates/app-api/js/.env.example b/packages/create-next-app/templates/app-api/js/.env.example deleted file mode 100644 index ce586fad292cd..0000000000000 --- a/packages/create-next-app/templates/app-api/js/.env.example +++ /dev/null @@ -1,3 +0,0 @@ -# Rename this file to `.env.local` to use environment variables locally with `next dev` -# https://nextjs.org/docs/app/building-your-application/configuring/environment-variables -MY_HOST="example.com" diff --git a/packages/create-next-app/templates/app-api/js/README-template.md b/packages/create-next-app/templates/app-api/js/README-template.md index 58746e0ed01a5..4f3cc28d3a821 100644 --- a/packages/create-next-app/templates/app-api/js/README-template.md +++ b/packages/create-next-app/templates/app-api/js/README-template.md @@ -16,7 +16,7 @@ bun dev Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. -You can start editing the page by modifying `app/route.js`. The page auto-updates as you edit the file. +You can start editing the page by modifying `app/route.ts`. The page auto-updates as you edit the file. ## Learn More @@ -37,43 +37,4 @@ Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/bui This directory contains example API routes for the headless API app. -Here's an overview of the available routes: - -1. Root Route (`/`): - - - Provides a list of available API endpoints. - - Useful for discovering what the API offers. - -2. Pokemon Routes: - - - List all Pokemon (`/pokemon`): Get an overview of available Pokemon. - - Filter Pokemon by type (`/pokemon?type=grass`): Find Pokemon of a specific element. - - Get a specific Pokemon (`/pokemon/25`): Fetch details about a particular Pokemon by ID. - -3. HTTP Methods Route (`/methods`): - - - Information about supported HTTP methods. - - Great for learning about various types of API requests. - -4. Specific HTTP Method Routes (`/methods/[method]`): - - - Demonstrates different HTTP methods (GET, POST, PUT, DELETE, PATCH). - - Helps understand how different types of API requests behave. - -5. Not Found Route: - - Handles requests to undefined routes. - - Returns a "Not Found" response for non-existent endpoints. - -Each route is wrapped with middleware that adds: - -- Request logging for monitoring and debugging. -- Origin checking for security. -- Authentication placeholders for future expansion. - -To explore these routes: - -1. Start the development server. -2. Open your browser and navigate to `http://localhost:3000/[route]`. -3. Examine the responses to understand how each route works. - -For more details on the implementation, check the corresponding files in the `app` directory. +For more details, see [route.js file convention](https://nextjs.org/docs/app/api-reference/file-conventions/route). diff --git a/packages/create-next-app/templates/app-api/js/app/[...not-found]/route.js b/packages/create-next-app/templates/app-api/js/app/[...not-found]/route.js deleted file mode 100644 index f504a7229813c..0000000000000 --- a/packages/create-next-app/templates/app-api/js/app/[...not-found]/route.js +++ /dev/null @@ -1,3 +0,0 @@ -export async function GET() { - return new Response("Not Found", { status: 404 }); -} diff --git a/packages/create-next-app/templates/app-api/js/app/methods/[method]/route.js b/packages/create-next-app/templates/app-api/js/app/methods/[method]/route.js deleted file mode 100644 index 2172b78479e14..0000000000000 --- a/packages/create-next-app/templates/app-api/js/app/methods/[method]/route.js +++ /dev/null @@ -1,58 +0,0 @@ -import { NextResponse } from "next/server"; -import { methods } from "../methods"; -import { withMiddleware } from "@/app/with-middleware"; - -export const GET = withMiddleware(async (request, context) => { - const method = context.params.method.toUpperCase(); - if (!methods.includes(method)) { - return NextResponse.json( - { error: `Method ${method} is not supported.` }, - { status: 404 }, - ); - } - - if (method === "GET") { - return NextResponse.json({ method: "GET" }); - } - - const url = request.nextUrl.toString(); - const res = await fetch(url, { method }); - - // if method is HEAD, body is null - if (method === "HEAD") { - const { ok, statusText, status, body } = res; - return NextResponse.json({ - ok, - statusText, - status, - body, - }); - } - - const data = await res.json(); - return NextResponse.json(data); -}); - -export async function POST() { - return NextResponse.json({ method: "POST" }); -} - -export async function PUT() { - return NextResponse.json({ method: "PUT" }); -} - -export async function DELETE() { - return NextResponse.json({ method: "DELETE" }); -} - -export async function PATCH() { - return NextResponse.json({ method: "PATCH" }); -} - -export async function OPTIONS() { - return NextResponse.json({ method: "OPTIONS" }); -} - -export async function HEAD() { - return new Response(null, { status: 200 }); -} diff --git a/packages/create-next-app/templates/app-api/js/app/methods/methods.js b/packages/create-next-app/templates/app-api/js/app/methods/methods.js deleted file mode 100644 index ed887fff74f26..0000000000000 --- a/packages/create-next-app/templates/app-api/js/app/methods/methods.js +++ /dev/null @@ -1,9 +0,0 @@ -export const methods = [ - "GET", - "POST", - "PUT", - "DELETE", - "PATCH", - "OPTIONS", - "HEAD", -]; diff --git a/packages/create-next-app/templates/app-api/js/app/methods/route.js b/packages/create-next-app/templates/app-api/js/app/methods/route.js deleted file mode 100644 index 57c81aefd534b..0000000000000 --- a/packages/create-next-app/templates/app-api/js/app/methods/route.js +++ /dev/null @@ -1,11 +0,0 @@ -import { NextResponse } from "next/server"; -import { methods } from "./methods"; - -export async function GET(request) { - const url = request.nextUrl.toString(); - const apis = methods.map((method) => { - return { path: `${url}/${method}`, description: `${method} method` }; - }); - - return NextResponse.json(apis); -} diff --git a/packages/create-next-app/templates/app-api/js/app/pokemon/[number]/route.js b/packages/create-next-app/templates/app-api/js/app/pokemon/[number]/route.js deleted file mode 100644 index ebc5f78f9ef7e..0000000000000 --- a/packages/create-next-app/templates/app-api/js/app/pokemon/[number]/route.js +++ /dev/null @@ -1,11 +0,0 @@ -import { NextResponse } from "next/server"; -import { withMiddleware } from "@/app/with-middleware"; - -export const GET = withMiddleware(async (_request, context) => { - const res = await fetch( - `https://api.vercel.app/pokemon/${context.params.number}`, - ); - const pokemon = await res.json(); - - return NextResponse.json(pokemon); -}); diff --git a/packages/create-next-app/templates/app-api/js/app/pokemon/route.js b/packages/create-next-app/templates/app-api/js/app/pokemon/route.js deleted file mode 100644 index 7e840038db686..0000000000000 --- a/packages/create-next-app/templates/app-api/js/app/pokemon/route.js +++ /dev/null @@ -1,51 +0,0 @@ -import { NextResponse } from "next/server"; -import { withMiddleware } from "@/app/with-middleware"; - -export const GET = withMiddleware(async (request) => { - const url = request.nextUrl.toString(); - - const { searchParams } = request.nextUrl; - const type = searchParams.get("type"); - - if (type && !POKEMON_TYPES.includes(type)) { - return NextResponse.json( - { error: `Invalid type: '${type}'`, types: POKEMON_TYPES }, - { status: 400 }, - ); - } - - const res = await fetch( - `https://api.vercel.app/pokemon${type ? `?type=${type}` : ""}`, - ); - const data = await res.json(); - - const pokedex = data.map((pokemon) => { - return { - ...pokemon, - url: `${url}/${pokemon.id}`, - }; - }); - - return NextResponse.json(pokedex); -}); - -const POKEMON_TYPES = [ - "normal", - "fire", - "water", - "electric", - "grass", - "ice", - "fighting", - "poison", - "ground", - "flying", - "psychic", - "bug", - "rock", - "ghost", - "dragon", - "dark", - "steel", - "fairy", -]; diff --git a/packages/create-next-app/templates/app-api/js/app/route.js b/packages/create-next-app/templates/app-api/js/app/route.js index 31de4cb5e6be7..5a44a8809d2ae 100644 --- a/packages/create-next-app/templates/app-api/js/app/route.js +++ b/packages/create-next-app/templates/app-api/js/app/route.js @@ -1,23 +1,5 @@ import { NextResponse } from "next/server"; -const paths = [ - { path: "pokemon", description: "gotta catch 'em all!" }, - { path: "pokemon?type=grass", description: "sort pokemon by grass type" }, - { path: "pokemon/25", description: "who's that pokemon?" }, - { path: "methods", description: "list of methods" }, -]; - -export async function GET(request) { - const url = request.nextUrl.toString(); - const apis = paths.map(({ path, description }) => { - return { path: url + path, description }; - }); - - return NextResponse.json(apis, { - headers: { - // CORS: https://nextjs.org/docs/app/building-your-application/routing/route-handlers#cors - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Methods": "GET", - }, - }); +export async function GET() { + return NextResponse.json({ message: "Hello World!" }); } diff --git a/packages/create-next-app/templates/app-api/js/app/with-middleware.js b/packages/create-next-app/templates/app-api/js/app/with-middleware.js deleted file mode 100644 index fd9a6a28eacbb..0000000000000 --- a/packages/create-next-app/templates/app-api/js/app/with-middleware.js +++ /dev/null @@ -1,61 +0,0 @@ -import { NextResponse } from "next/server"; - -export function withMiddleware(handler) { - return async (req, ctx) => { - try { - console.log(`Middleware: Request to ${req.method} ${req.url}`); - - // Check if the request is coming from an allowed origin - if (!isAllowedOrigin(req)) { - return NextResponse.json({ error: "Forbidden" }, { status: 403 }); - } - - // Placeholder for authentication logic - const isAuthenticated = await checkAuth(req); - if (!isAuthenticated) { - return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); - } - - // Call the original handler - const result = await handler(req, ctx); - - // Log after executing the handler - console.log(`Middleware: Completed ${req.method} ${req.url}`); - - return result; - } catch (error) { - console.error( - `Middleware: Error in route handler: ${req.method} ${req.url}`, - error, - ); - return NextResponse.json( - { error: "Internal Server Error" }, - { status: 500 }, - ); - } - }; -} - -function isAllowedOrigin(req) { - const referer = req.headers.get("referer"); - const host = req.headers.get("host"); - - // Allow requests from the same origin - if (referer && host) { - const refererUrl = new URL(referer); - return refererUrl.host === host; - } - - if (process.env.NODE_ENV === "development" && host?.includes("localhost")) { - return true; - } - - return false; -} - -async function checkAuth(req) { - // Placeholder authentication logic - // You can implement your actual auth check here - const authToken = req.headers.get("authorization"); - return true; // For now, always return true -} diff --git a/packages/create-next-app/templates/app-api/ts/.env.example b/packages/create-next-app/templates/app-api/ts/.env.example deleted file mode 100644 index b4b3144d2b90c..0000000000000 --- a/packages/create-next-app/templates/app-api/ts/.env.example +++ /dev/null @@ -1,4 +0,0 @@ -# Rename this file to `.env.local` to use environment variables locally with `next dev` -# Add .env files to your .gitignore to prevent accidentally committing sensitive information. -# https://nextjs.org/docs/app/building-your-application/configuring/environment-variables -MY_HOST="example.com" diff --git a/packages/create-next-app/templates/app-api/ts/README-template.md b/packages/create-next-app/templates/app-api/ts/README-template.md index cacee7ed90e91..4f3cc28d3a821 100644 --- a/packages/create-next-app/templates/app-api/ts/README-template.md +++ b/packages/create-next-app/templates/app-api/ts/README-template.md @@ -37,43 +37,4 @@ Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/bui This directory contains example API routes for the headless API app. -Here's an overview of the available routes: - -1. Root Route (`/`): - - - Provides a list of available API endpoints. - - Useful for discovering what the API offers. - -2. Pokemon Routes: - - - List all Pokemon (`/pokemon`): Get an overview of available Pokemon. - - Filter Pokemon by type (`/pokemon?type=grass`): Find Pokemon of a specific element. - - Get a specific Pokemon (`/pokemon/25`): Fetch details about a particular Pokemon by ID. - -3. HTTP Methods Route (`/methods`): - - - Information about supported HTTP methods. - - Great for learning about various types of API requests. - -4. Specific HTTP Method Routes (`/methods/[method]`): - - - Demonstrates different HTTP methods (GET, POST, PUT, DELETE, PATCH). - - Helps understand how different types of API requests behave. - -5. Not Found Route: - - Handles requests to undefined routes. - - Returns a "Not Found" response for non-existent endpoints. - -Each route is wrapped with middleware that adds: - -- Request logging for monitoring and debugging. -- Origin checking for security. -- Authentication placeholders for future expansion. - -To explore these routes: - -1. Start the development server. -2. Open your browser and navigate to `http://localhost:3000/[route]`. -3. Examine the responses to understand how each route works. - -For more details on the implementation, check the corresponding files in the `app` directory. +For more details, see [route.js file convention](https://nextjs.org/docs/app/api-reference/file-conventions/route). diff --git a/packages/create-next-app/templates/app-api/ts/app/methods/[method]/route.ts b/packages/create-next-app/templates/app-api/ts/app/methods/[method]/route.ts deleted file mode 100644 index dbec2ba949bc9..0000000000000 --- a/packages/create-next-app/templates/app-api/ts/app/methods/[method]/route.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { NextResponse } from "next/server"; -import { methods } from "../methods"; -import { withMiddleware } from "@/app/with-middleware"; - -export const GET = withMiddleware( - async (request, context: { params: { method: string } }) => { - const method = context.params.method.toUpperCase(); - if (!methods.includes(method)) { - return NextResponse.json( - { error: `Method ${method} is not supported.` }, - { status: 404 }, - ); - } - - if (method === "GET") { - return NextResponse.json({ method: "GET" }); - } - - const url = request.nextUrl.toString(); - const res = await fetch(url, { method }); - - // if method is HEAD, body is null - if (method === "HEAD") { - const { ok, statusText, status, body } = res; - return NextResponse.json({ - ok, - statusText, - status, - body, - }); - } - - const data = await res.json(); - return NextResponse.json(data); - }, -); - -export async function POST() { - return NextResponse.json({ method: "POST" }); -} - -export async function PUT() { - return NextResponse.json({ method: "PUT" }); -} - -export async function DELETE() { - return NextResponse.json({ method: "DELETE" }); -} - -export async function PATCH() { - return NextResponse.json({ method: "PATCH" }); -} - -export async function OPTIONS() { - return NextResponse.json({ method: "OPTIONS" }); -} - -export async function HEAD() { - return new Response(null, { status: 200 }); -} diff --git a/packages/create-next-app/templates/app-api/ts/app/methods/methods.ts b/packages/create-next-app/templates/app-api/ts/app/methods/methods.ts deleted file mode 100644 index ed887fff74f26..0000000000000 --- a/packages/create-next-app/templates/app-api/ts/app/methods/methods.ts +++ /dev/null @@ -1,9 +0,0 @@ -export const methods = [ - "GET", - "POST", - "PUT", - "DELETE", - "PATCH", - "OPTIONS", - "HEAD", -]; diff --git a/packages/create-next-app/templates/app-api/ts/app/methods/route.ts b/packages/create-next-app/templates/app-api/ts/app/methods/route.ts deleted file mode 100644 index 86c2985245d8d..0000000000000 --- a/packages/create-next-app/templates/app-api/ts/app/methods/route.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { type NextRequest, NextResponse } from "next/server"; -import { methods } from "./methods"; - -export async function GET(request: NextRequest) { - const url = request.nextUrl.toString(); - const apis = methods.map((method) => { - return { path: `${url}/${method}`, description: `${method} method` }; - }); - - return NextResponse.json(apis); -} diff --git a/packages/create-next-app/templates/app-api/ts/app/pokemon/[number]/route.ts b/packages/create-next-app/templates/app-api/ts/app/pokemon/[number]/route.ts deleted file mode 100644 index ebc5f78f9ef7e..0000000000000 --- a/packages/create-next-app/templates/app-api/ts/app/pokemon/[number]/route.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { NextResponse } from "next/server"; -import { withMiddleware } from "@/app/with-middleware"; - -export const GET = withMiddleware(async (_request, context) => { - const res = await fetch( - `https://api.vercel.app/pokemon/${context.params.number}`, - ); - const pokemon = await res.json(); - - return NextResponse.json(pokemon); -}); diff --git a/packages/create-next-app/templates/app-api/ts/app/route.ts b/packages/create-next-app/templates/app-api/ts/app/route.ts index 240a0c044d8d9..5a44a8809d2ae 100644 --- a/packages/create-next-app/templates/app-api/ts/app/route.ts +++ b/packages/create-next-app/templates/app-api/ts/app/route.ts @@ -1,23 +1,5 @@ -import { type NextRequest, NextResponse } from "next/server"; +import { NextResponse } from "next/server"; -const paths = [ - { path: "pokemon", description: "gotta catch 'em all!" }, - { path: "pokemon?type=grass", description: "sort pokemon by grass type" }, - { path: "pokemon/25", description: "who's that pokemon?" }, - { path: "methods", description: "list of methods" }, -]; - -export async function GET(request: NextRequest) { - const url = request.nextUrl.toString(); - const apis = paths.map(({ path, description }) => { - return { path: url + path, description }; - }); - - return NextResponse.json(apis, { - headers: { - // CORS: https://nextjs.org/docs/app/building-your-application/routing/route-handlers#cors - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Methods": "GET", - }, - }); +export async function GET() { + return NextResponse.json({ message: "Hello World!" }); } From 80adef53debaf9a9efdc54608f6033760d4ec57b Mon Sep 17 00:00:00 2001 From: devjiwonchoi Date: Thu, 12 Sep 2024 16:18:59 +0900 Subject: [PATCH 43/50] specification --- .../create-next-app/lib/specification.ts | 25 ++----------------- 1 file changed, 2 insertions(+), 23 deletions(-) diff --git a/test/integration/create-next-app/lib/specification.ts b/test/integration/create-next-app/lib/specification.ts index 1ad34bc97a7f9..8539fd37ea809 100644 --- a/test/integration/create-next-app/lib/specification.ts +++ b/test/integration/create-next-app/lib/specification.ts @@ -175,33 +175,12 @@ export const projectSpecification: ProjectSpecification = { js: { deps: ['next'], devDeps: [], - files: [ - 'app/[...not-found]/route.js', - 'app/methods/[method]/route.js', - 'app/methods/methods.js', - 'app/methods/route.js', - 'app/pokemon/[number]/route.js', - 'app/pokemon/route.js', - 'app/route.js', - 'app/with-middleware.js', - 'jsconfig.json', - ], + files: ['app/route.js', 'jsconfig.json'], }, ts: { deps: ['next'], devDeps: ['@types/node', '@types/react', 'typescript'], - files: [ - 'app/[...not-found]/route.ts', - 'app/methods/[method]/route.ts', - 'app/methods/methods.ts', - 'app/methods/route.ts', - 'app/pokemon/[number]/route.ts', - 'app/pokemon/route.ts', - 'app/route.ts', - 'app/with-middleware.ts', - 'tsconfig.json', - 'next-env.d.ts', - ], + files: ['app/route.ts', 'tsconfig.json', 'next-env.d.ts'], }, }, 'app-empty': { From 10b88b8d09ada92db2d671f89912092c1d1c6fef Mon Sep 17 00:00:00 2001 From: devjiwonchoi Date: Thu, 12 Sep 2024 16:22:12 +0900 Subject: [PATCH 44/50] util update --- packages/create-next-app/templates/app-api/ts/app/route.ts | 2 +- test/integration/create-next-app/utils.ts | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/create-next-app/templates/app-api/ts/app/route.ts b/packages/create-next-app/templates/app-api/ts/app/route.ts index 5a44a8809d2ae..5c2378c0afc74 100644 --- a/packages/create-next-app/templates/app-api/ts/app/route.ts +++ b/packages/create-next-app/templates/app-api/ts/app/route.ts @@ -1,5 +1,5 @@ import { NextResponse } from "next/server"; export async function GET() { - return NextResponse.json({ message: "Hello World!" }); + return NextResponse.json({ message: "Hello world!" }); } diff --git a/test/integration/create-next-app/utils.ts b/test/integration/create-next-app/utils.ts index 1482c859012a6..1b59e00ad0094 100644 --- a/test/integration/create-next-app/utils.ts +++ b/test/integration/create-next-app/utils.ts @@ -63,10 +63,8 @@ export async function tryNextDev({ try { const res = await fetchViaHTTP(port, '/') - if (isEmpty) { + if (isEmpty || isApi) { expect(await res.text()).toContain('Hello world!') - } else if (isApi) { - expect(await res.text()).toContain("who's that pokemon?") } else { expect(await res.text()).toContain('Get started by editing') } From 318067bd588b55d98d52465cce75d5bfd0c3ca74 Mon Sep 17 00:00:00 2001 From: devjiwonchoi Date: Thu, 12 Sep 2024 16:23:11 +0900 Subject: [PATCH 45/50] js hello world --- packages/create-next-app/templates/app-api/js/app/route.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/create-next-app/templates/app-api/js/app/route.js b/packages/create-next-app/templates/app-api/js/app/route.js index 5a44a8809d2ae..5c2378c0afc74 100644 --- a/packages/create-next-app/templates/app-api/js/app/route.js +++ b/packages/create-next-app/templates/app-api/js/app/route.js @@ -1,5 +1,5 @@ import { NextResponse } from "next/server"; export async function GET() { - return NextResponse.json({ message: "Hello World!" }); + return NextResponse.json({ message: "Hello world!" }); } From 4935b2fc5431917c9fe4de15fad75f573ae6d716 Mon Sep 17 00:00:00 2001 From: devjiwonchoi Date: Thu, 12 Sep 2024 22:08:06 +0900 Subject: [PATCH 46/50] write tests for example app-api --- examples/app-api/app/request-handler.ts | 2 +- examples/app-api/package.json | 2 +- test/e2e/app-dir/app-api/app-api.test.ts | 77 +++++++++++++++++++ .../app-api/app/[...not-found]/route.ts | 3 + .../app-dir/app-api/app/pokemon/[id]/route.ts | 9 +++ test/e2e/app-dir/app-api/app/pokemon/route.ts | 51 ++++++++++++ .../app-dir/app-api/app/request-handler.ts | 69 +++++++++++++++++ test/e2e/app-dir/app-api/app/route.ts | 22 ++++++ test/e2e/app-dir/app-api/next.config.ts | 7 ++ 9 files changed, 240 insertions(+), 2 deletions(-) create mode 100644 test/e2e/app-dir/app-api/app-api.test.ts create mode 100644 test/e2e/app-dir/app-api/app/[...not-found]/route.ts create mode 100644 test/e2e/app-dir/app-api/app/pokemon/[id]/route.ts create mode 100644 test/e2e/app-dir/app-api/app/pokemon/route.ts create mode 100644 test/e2e/app-dir/app-api/app/request-handler.ts create mode 100644 test/e2e/app-dir/app-api/app/route.ts create mode 100644 test/e2e/app-dir/app-api/next.config.ts diff --git a/examples/app-api/app/request-handler.ts b/examples/app-api/app/request-handler.ts index 8802a987bed9c..820609dfc9ec0 100644 --- a/examples/app-api/app/request-handler.ts +++ b/examples/app-api/app/request-handler.ts @@ -15,7 +15,7 @@ function isAllowedOrigin(req: NextRequest): boolean { return refererUrl.host === host; } - if (process.env.NODE_ENV === "development" && host?.includes("localhost")) { + if (host?.includes("localhost")) { return true; } diff --git a/examples/app-api/package.json b/examples/app-api/package.json index d6a8e427fee09..6c7d587806763 100644 --- a/examples/app-api/package.json +++ b/examples/app-api/package.json @@ -11,6 +11,6 @@ "devDependencies": { "@types/node": "20.16.5", "@types/react": "18.3.5", - "typescript": "5.6.2" + "typescript": "5.5.4" } } diff --git a/test/e2e/app-dir/app-api/app-api.test.ts b/test/e2e/app-dir/app-api/app-api.test.ts new file mode 100644 index 0000000000000..f098008a9811d --- /dev/null +++ b/test/e2e/app-dir/app-api/app-api.test.ts @@ -0,0 +1,77 @@ +import { nextTestSetup } from 'e2e-utils' + +describe('app-api', () => { + const { next } = nextTestSetup({ + files: __dirname, + }) + + it('should display the api routes mapped with the current url', async () => { + const url = `http://localhost:${next.appPort}` + const res = await next.fetch('/') + const json = await res.json() + expect(json).toEqual([ + { path: `${url}/pokemon`, description: "gotta catch 'em all!" }, + { + path: `${url}/pokemon?type=grass`, + description: 'sort pokemon by grass type', + }, + { path: `${url}/pokemon/25`, description: "who's that pokemon?" }, + ]) + }) + + it('should return a list of pokemon', async () => { + const res = await next.fetch('/pokemon') + const json = await res.json() + expect(res.status).toBe(200) + expect(Array.isArray(json)).toBe(true) + expect(json.length).toBeGreaterThan(0) + expect(json[0]).toHaveProperty('id') + expect(json[0]).toHaveProperty('name') + expect(json[0]).toHaveProperty('url') + }) + + it('should filter pokemon by type', async () => { + const type = 'fire' + const res = await next.fetch(`/pokemon?type=${type}`) + const json = await res.json() + expect(res.status).toBe(200) + expect(Array.isArray(json)).toBe(true) + json.forEach((pokemon) => { + expect(pokemon.type).toContain(type) + }) + }) + + it('should return an error for invalid type', async () => { + const invalidType = 'invalid' + const res = await next.fetch(`/pokemon?type=${invalidType}`) + const json = await res.json() + expect(res.status).toBe(400) + expect(json).toHaveProperty('error') + expect(json.error).toBe(`Invalid type: '${invalidType}'`) + expect(json).toHaveProperty('types') + expect(Array.isArray(json.types)).toBe(true) + }) + + it('should return a specific pokemon by id', async () => { + const pokemonId = 25 + const res = await next.fetch(`/pokemon/${pokemonId}`) + const json = await res.json() + expect(res.status).toBe(200) + expect(json).toHaveProperty('id', pokemonId) + expect(json).toHaveProperty('name', 'pikachu') + }) + + it('should return 404 for non-existent routes', async () => { + const res = await next.fetch('/non-existent-route') + expect(res.status).toBe(404) + const text = await res.text() + expect(text).toBe('Not Found') + }) + + it('should return 404 for nested non-existent routes', async () => { + const res = await next.fetch('/some/nested/non-existent-route') + expect(res.status).toBe(404) + const text = await res.text() + expect(text).toBe('Not Found') + }) +}) diff --git a/test/e2e/app-dir/app-api/app/[...not-found]/route.ts b/test/e2e/app-dir/app-api/app/[...not-found]/route.ts new file mode 100644 index 0000000000000..ab24a75560d24 --- /dev/null +++ b/test/e2e/app-dir/app-api/app/[...not-found]/route.ts @@ -0,0 +1,3 @@ +export async function GET() { + return new Response('Not Found', { status: 404 }) +} diff --git a/test/e2e/app-dir/app-api/app/pokemon/[id]/route.ts b/test/e2e/app-dir/app-api/app/pokemon/[id]/route.ts new file mode 100644 index 0000000000000..17c43e2cbed07 --- /dev/null +++ b/test/e2e/app-dir/app-api/app/pokemon/[id]/route.ts @@ -0,0 +1,9 @@ +import { NextResponse } from 'next/server' +import { requestHandler } from '../../request-handler' + +export const GET = requestHandler(async (_, context) => { + const res = await fetch(`https://api.vercel.app/pokemon/${context.params.id}`) + const pokemon = await res.json() + + return NextResponse.json(pokemon) +}) diff --git a/test/e2e/app-dir/app-api/app/pokemon/route.ts b/test/e2e/app-dir/app-api/app/pokemon/route.ts new file mode 100644 index 0000000000000..ced6274c1970f --- /dev/null +++ b/test/e2e/app-dir/app-api/app/pokemon/route.ts @@ -0,0 +1,51 @@ +import { NextResponse } from 'next/server' +import { requestHandler } from '../request-handler' + +const POKEMON_TYPES = [ + 'normal', + 'fire', + 'water', + 'electric', + 'grass', + 'ice', + 'fighting', + 'poison', + 'ground', + 'flying', + 'psychic', + 'bug', + 'rock', + 'ghost', + 'dragon', + 'dark', + 'steel', + 'fairy', +] + +export const GET = requestHandler(async (request) => { + const url = request.nextUrl.toString() + + const { searchParams } = request.nextUrl + const type = searchParams.get('type') + + if (type && !POKEMON_TYPES.includes(type)) { + return NextResponse.json( + { error: `Invalid type: '${type}'`, types: POKEMON_TYPES }, + { status: 400 } + ) + } + + const res = await fetch( + `https://api.vercel.app/pokemon${type ? `?type=${type}` : ''}` + ) + const data = await res.json() + + const pokedex = data.map((pokemon: { id: string }) => { + return { + ...pokemon, + url: `${url}/${pokemon.id}`, + } + }) + + return NextResponse.json(pokedex) +}) diff --git a/test/e2e/app-dir/app-api/app/request-handler.ts b/test/e2e/app-dir/app-api/app/request-handler.ts new file mode 100644 index 0000000000000..74d616594dd7d --- /dev/null +++ b/test/e2e/app-dir/app-api/app/request-handler.ts @@ -0,0 +1,69 @@ +import { NextRequest, NextResponse } from 'next/server' + +type RouteHandler> = ( + req: NextRequest, + ctx: { params: T } +) => Promise | Response + +function isAllowedOrigin(req: NextRequest): boolean { + const referer = req.headers.get('referer') + const host = req.headers.get('host') + + // Allow requests from the same origin + if (referer && host) { + const refererUrl = new URL(referer) + return refererUrl.host === host + } + + if (host?.includes('localhost')) { + return true + } + + return false +} + +async function checkAuth(req: NextRequest): Promise { + // Placeholder authentication logic + // You can implement your actual auth check here + const authToken = req.headers.get('authorization') + console.log({ authToken }) + return true // For now, always return true +} + +export function requestHandler>( + handler: RouteHandler +): RouteHandler { + return async (req: NextRequest, ctx: { params: T }) => { + try { + console.log(`Middleware: Request to ${req.method} ${req.url}`) + + // Check if the request is coming from an allowed origin + if (!isAllowedOrigin(req)) { + return NextResponse.json({ error: 'Forbidden' }, { status: 403 }) + } + + // Placeholder for authentication logic + const isAuthenticated = await checkAuth(req) + if (!isAuthenticated) { + return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) + } + + // Call the original handler + const result = await handler(req, ctx) + + // Log after executing the handler + console.log(`Middleware: Completed ${req.method} ${req.url}`) + + return result + } catch (error) { + console.error( + `Middleware: Error in route handler: ${req.method} ${req.url}`, + error + ) + return NextResponse.json( + { error: 'Internal Server Error' }, + { status: 500 } + ) + } + } +} diff --git a/test/e2e/app-dir/app-api/app/route.ts b/test/e2e/app-dir/app-api/app/route.ts new file mode 100644 index 0000000000000..3132747ca3888 --- /dev/null +++ b/test/e2e/app-dir/app-api/app/route.ts @@ -0,0 +1,22 @@ +import { type NextRequest, NextResponse } from 'next/server' + +const paths = [ + { path: 'pokemon', description: "gotta catch 'em all!" }, + { path: 'pokemon?type=grass', description: 'sort pokemon by grass type' }, + { path: 'pokemon/25', description: "who's that pokemon?" }, +] + +export async function GET(request: NextRequest) { + const url = request.nextUrl.toString() + const apis = paths.map(({ path, description }) => { + return { path: url + path, description } + }) + + return NextResponse.json(apis, { + headers: { + // CORS: https://nextjs.org/docs/app/building-your-application/routing/route-handlers#cors + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Methods': 'GET', + }, + }) +} diff --git a/test/e2e/app-dir/app-api/next.config.ts b/test/e2e/app-dir/app-api/next.config.ts new file mode 100644 index 0000000000000..73290639bc0ae --- /dev/null +++ b/test/e2e/app-dir/app-api/next.config.ts @@ -0,0 +1,7 @@ +import type { NextConfig } from 'next' + +const nextConfig: NextConfig = { + /* config options here */ +} + +export default nextConfig From 61603de166ac6da78e71a3937c943d6a23d1a601 Mon Sep 17 00:00:00 2001 From: Jiwon Choi Date: Thu, 12 Sep 2024 23:11:48 +0900 Subject: [PATCH 47/50] Update examples/app-api/package.json --- examples/app-api/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/app-api/package.json b/examples/app-api/package.json index 6c7d587806763..fab4470f4f55a 100644 --- a/examples/app-api/package.json +++ b/examples/app-api/package.json @@ -1,7 +1,7 @@ { "private": true, "scripts": { - "dev": "next dev --turbo", + "dev": "next dev", "build": "next build", "start": "next start" }, From ba031aebef8687200da540837b4be2c8c691fae5 Mon Sep 17 00:00:00 2001 From: devjiwonchoi Date: Thu, 12 Sep 2024 23:15:11 +0900 Subject: [PATCH 48/50] remove not-found --- examples/app-api/app/[...not-found]/route.ts | 3 --- test/e2e/app-dir/app-api/app-api.test.ts | 14 -------------- .../app-dir/app-api/app/[...not-found]/route.ts | 3 --- 3 files changed, 20 deletions(-) delete mode 100644 examples/app-api/app/[...not-found]/route.ts delete mode 100644 test/e2e/app-dir/app-api/app/[...not-found]/route.ts diff --git a/examples/app-api/app/[...not-found]/route.ts b/examples/app-api/app/[...not-found]/route.ts deleted file mode 100644 index f504a7229813c..0000000000000 --- a/examples/app-api/app/[...not-found]/route.ts +++ /dev/null @@ -1,3 +0,0 @@ -export async function GET() { - return new Response("Not Found", { status: 404 }); -} diff --git a/test/e2e/app-dir/app-api/app-api.test.ts b/test/e2e/app-dir/app-api/app-api.test.ts index f098008a9811d..ffe05c8532181 100644 --- a/test/e2e/app-dir/app-api/app-api.test.ts +++ b/test/e2e/app-dir/app-api/app-api.test.ts @@ -60,18 +60,4 @@ describe('app-api', () => { expect(json).toHaveProperty('id', pokemonId) expect(json).toHaveProperty('name', 'pikachu') }) - - it('should return 404 for non-existent routes', async () => { - const res = await next.fetch('/non-existent-route') - expect(res.status).toBe(404) - const text = await res.text() - expect(text).toBe('Not Found') - }) - - it('should return 404 for nested non-existent routes', async () => { - const res = await next.fetch('/some/nested/non-existent-route') - expect(res.status).toBe(404) - const text = await res.text() - expect(text).toBe('Not Found') - }) }) diff --git a/test/e2e/app-dir/app-api/app/[...not-found]/route.ts b/test/e2e/app-dir/app-api/app/[...not-found]/route.ts deleted file mode 100644 index ab24a75560d24..0000000000000 --- a/test/e2e/app-dir/app-api/app/[...not-found]/route.ts +++ /dev/null @@ -1,3 +0,0 @@ -export async function GET() { - return new Response('Not Found', { status: 404 }) -} From 57fb6a7f190eaf45debd4b67dd66c5fb3f157308 Mon Sep 17 00:00:00 2001 From: devjiwonchoi Date: Thu, 12 Sep 2024 23:27:40 +0900 Subject: [PATCH 49/50] next latest --- examples/app-api/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/app-api/package.json b/examples/app-api/package.json index fab4470f4f55a..9cb5eb317cf05 100644 --- a/examples/app-api/package.json +++ b/examples/app-api/package.json @@ -6,7 +6,7 @@ "start": "next start" }, "dependencies": { - "next": "14.2.9" + "next": "latest" }, "devDependencies": { "@types/node": "20.16.5", From f11da16f1f413ee53157fc4ebcd97e436ecb3558 Mon Sep 17 00:00:00 2001 From: Arindam Majumder <109217591+Arindam200@users.noreply.github.com> Date: Sat, 26 Oct 2024 19:45:48 +0000 Subject: [PATCH 50/50] improve comments --- examples/app-api/app/request-handler.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/examples/app-api/app/request-handler.ts b/examples/app-api/app/request-handler.ts index 820609dfc9ec0..99e47ae3fed71 100644 --- a/examples/app-api/app/request-handler.ts +++ b/examples/app-api/app/request-handler.ts @@ -5,6 +5,7 @@ type RouteHandler> = ( ctx: { params: T }, ) => Promise | Response; +// Checks if the incoming request is from an allowed origin function isAllowedOrigin(req: NextRequest): boolean { const referer = req.headers.get("referer"); const host = req.headers.get("host"); @@ -14,20 +15,22 @@ function isAllowedOrigin(req: NextRequest): boolean { const refererUrl = new URL(referer); return refererUrl.host === host; } - + + // Allow requests from localhost (for development) if (host?.includes("localhost")) { return true; } - + // Deny by default return false; } +// Placeholder for actual authentication logic (to be implemented) async function checkAuth(req: NextRequest): Promise { - // Placeholder authentication logic - // You can implement your actual auth check here const authToken = req.headers.get("authorization"); console.log({ authToken }); - return true; // For now, always return true + + // Currently returns true for all requests; replace with actual auth logic + return true; } export function requestHandler>( @@ -37,7 +40,7 @@ export function requestHandler>( try { console.log(`Middleware: Request to ${req.method} ${req.url}`); - // Check if the request is coming from an allowed origin + // Check if the request is from an allowed origin if (!isAllowedOrigin(req)) { return NextResponse.json({ error: "Forbidden" }, { status: 403 }); } @@ -48,7 +51,7 @@ export function requestHandler>( return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); } - // Call the original handler + // Call the main handler if all checks pass const result = await handler(req, ctx); // Log after executing the handler