This repository has been archived by the owner on Nov 28, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
- Loading branch information
1 parent
9a006b2
commit 081761c
Showing
10 changed files
with
1,911 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
/// <reference types="next" /> | ||
/// <reference types="next/image-types/global" /> | ||
|
||
// NOTE: This file should not be edited | ||
// see https://nextjs.org/docs/basic-features/typescript for more information. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
const withNextra = require("nextra")({ | ||
theme: "nextra-theme-docs", | ||
themeConfig: "./theme.config.tsx", | ||
}); | ||
|
||
module.exports = withNextra(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
{ | ||
"name": "docs", | ||
"version": "1.0.0", | ||
"description": "", | ||
"main": "index.js", | ||
"scripts": { | ||
"dev": "next", | ||
"build": "next build", | ||
"start": "next start" | ||
}, | ||
"keywords": [], | ||
"author": "", | ||
"license": "ISC", | ||
"dependencies": { | ||
"next": "^12.2.4", | ||
"nextra": "beta", | ||
"nextra-theme-docs": "beta", | ||
"react": "^18.1.0", | ||
"react-dom": "^18.1.0" | ||
}, | ||
"devDependencies": { | ||
"@types/node": "^17.0.38", | ||
"@types/react": "^18.0.10", | ||
"@types/react-dom": "^18.0.5", | ||
"typescript": "^4.7.2" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
# Next Fetch | ||
|
||
Think in React, instead about routing: Next Fetch is an intuitive way to dynamically fetch data from API endpoints in Next.js, using your favorite libraries. | ||
|
||
💃 Import your API endpoints instead of making a stringified dance | ||
|
||
🔒 Infer the types end-to-end for your data based on its implementation | ||
|
||
⚛ Think in React, instead of routing: you only export a React hook! | ||
|
||
🕵 Embrace best-practices: input validation, error handling, etc. | ||
|
||
🌐 Use `Request` and `Response` classes as building blocks, no matter what runtime you're running on (Node.js or Edge) | ||
|
||
📝 Use `<Form />` component for making progressive enhanced experiences | ||
|
||
🤯 Supports [SWR](https://swr.vercel.app) and [React Query](https://tanstack.com/query/v4) out of the box! | ||
|
||
## What does that mean? | ||
|
||
Next Fetch is using compile-time transformations to allow you to _import_ your API endpoints instead of referecing them as plain strings, while keeping the type definitions co-located with the implementation. | ||
|
||
<table> | ||
<thead> | ||
<tr> | ||
<th></th> | ||
<th>Next.js + Next Fetch</th> | ||
<th>Plain Next.js</th> | ||
</tr> | ||
</thead> | ||
|
||
<tbody> | ||
<tr> | ||
<td>API implementation</td> | ||
<td> | ||
|
||
```tsx | ||
// pages/api/message.swr.tsx | ||
import { query } from "@next-fetch/swr"; | ||
import z from "zod"; | ||
|
||
export const useMessage = query( | ||
// use zod for input validation | ||
z.object({ | ||
name: z.string(), | ||
}), | ||
async function ({ name }) { | ||
// this.request is a `Request` instance | ||
return { hello: `world, ${name}` }; | ||
} | ||
); | ||
``` | ||
|
||
</td> | ||
<td> | ||
|
||
```tsx | ||
// pages/api/message.tsx | ||
import type { NextApiRequest, NextApiResponse } from "next"; | ||
|
||
export default (req: NextApiRequest, res: NextApiResponse) => { | ||
// ad-hoc input validation | ||
const name = Array.isArray(req.query.name) | ||
? req.query.name[0] | ||
: req.query.name; | ||
if (!name) { | ||
return res.status(400).json({ error: "No name provided" }); | ||
} | ||
|
||
// explicit type defniitions required | ||
return res.status(200).json({ hello: `world, ${name}` }); | ||
}; | ||
``` | ||
|
||
</td> | ||
</tr> | ||
<tr> | ||
<td>API consumption</td> | ||
<td> | ||
|
||
```tsx | ||
import { useMessage } from "./api/message"; | ||
|
||
export default function MyPage() { | ||
const { data, error } = useMessage({ | ||
// will autocomplete and | ||
// type-check the input arguments | ||
name: "John Doe", | ||
}); | ||
|
||
// autocompletes and type-checks! | ||
const hello = data?.hello; | ||
|
||
return <div>{/* ... */}</div>; | ||
} | ||
``` | ||
|
||
</td> | ||
<td> | ||
|
||
```tsx | ||
// pages/index.tsx | ||
import useSWR from "swr"; | ||
|
||
const fetcher = (url: string) => { | ||
const response = await fetch(url); | ||
|
||
// handle input validation or runtime errors | ||
if (!response.ok) { | ||
const text = await response.text().catch(() => null); | ||
throw new Error(`response is not okay${text ? `: ${text}` : ""}`); | ||
} | ||
|
||
return await response.json(); | ||
}; | ||
|
||
export default function MyPage() { | ||
const { data, error } = useSWR("/api/message?name=John%20Doe", fetcher); | ||
/** `data` is `any`, so we need to explicitly type-cast here */ | ||
return <div>{/* ... */}</div>; | ||
} | ||
``` | ||
|
||
</td> | ||
</tr> | ||
</tbody> | ||
|
||
</table> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"index": "Introduction", | ||
"swr": "Using SWR", | ||
"react-query": "Using React Query" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
--- | ||
title: "Next Fetch + React Query quickstart" | ||
--- | ||
|
||
import { Callout } from "nextra-theme-docs"; | ||
|
||
<Callout type="info"> | ||
Looking for integrating Next Fetch with SWR? [Check out the SWR integration | ||
guide](./swr). | ||
</Callout> | ||
|
||
Next Fetch supports [React Query v4](https://tanstack.com/query/v4) out of the box, through the `@next-fetch/react-query` package. | ||
|
||
## Set up React Query | ||
|
||
First, make sure you went through [React Query's quick start guide](https://tanstack.com/query/v4/docs/quick-start) to set up the necessary environment. The `QueryClientProvider` needs to be set up in `_app` to allow React Query to work properly. | ||
|
||
## Installation | ||
|
||
Assuming you already installed React Query v4 in your project, install `@next-fetch/react-query` and your runtime type validation of choice. In this guide we will use [Zod](https://zod.dev). | ||
|
||
```bash | ||
# pnpm | ||
pnpm add @next-fetch/react-query zod | ||
# yarn | ||
yarn add @next-fetch/react-query zod | ||
# npm | ||
npm install --save @next-fetch/react-query zod | ||
``` | ||
|
||
## Set up `next.config.js` | ||
|
||
Within your `next.config.js` file, make the following changes to use `withReactQueryApiEndpoints`: | ||
|
||
```diff | ||
+ const { withReactQueryApiEndpoints } = require("@next-fetch/react-query"); | ||
+ | ||
const config = { /* ... */ }; | ||
- module.exports = config; | ||
+ module.exports = withReactQueryApiEndpoints(config); | ||
``` | ||
|
||
Now you can restart your app, to make sure the changes in `next.config.js` are applied. | ||
|
||
## Create your first API endpoint | ||
|
||
Any `pages/api/*.rq.ts` file will be automatically compiled using Next Fetch's compiler. For our test, we can create a simple `pages/api/simple.rq.ts` endpoint: | ||
|
||
```ts | ||
// pages/api/simple.rq.ts | ||
|
||
import { query } from "@next-fetch/react-query"; | ||
import z from "zod"; | ||
|
||
export const useMessage = query( | ||
z.object({ name: z.string() }), | ||
async ({ name }) => { | ||
return { message: `Hello ${name}` }; | ||
} | ||
); | ||
``` | ||
|
||
## Use the API endpoint in any page | ||
|
||
```ts | ||
// pages/index.tsx | ||
|
||
import { useMessage } from "./api/simple.rq"; | ||
|
||
export default function Home() { | ||
const { data, error, isLoading } = useMessage({ name: "World" }); | ||
|
||
if (isLoading) { | ||
return <div>Loading...</div>; | ||
} | ||
|
||
if (error) { | ||
return <div>Error: {String(error)}</div>; | ||
} | ||
|
||
return <div>{data.message}</div>; | ||
} | ||
``` | ||
|
||
## Add a mutation to your API endpoint | ||
|
||
```ts | ||
// pages/api/simple.rq.ts | ||
|
||
export const useMutation = query( | ||
z.object({ name: z.string() }), | ||
async ({ name }) => { | ||
return { message: "Hello " + name }; | ||
}, | ||
{ | ||
// This will be called when the form is sent before JavaScript loads | ||
// and enables to add logic for smart server-side redirections | ||
hookResponse(data) { | ||
const newUrl = new URL("/form", this.request.url); | ||
newUrl.searchParams.set("message", data.message); | ||
return Response.redirect(newUrl.toString(), 302); | ||
}, | ||
} | ||
); | ||
``` | ||
|
||
## Use the `<Form />` component to call the mutation | ||
|
||
```ts | ||
// pages/form.tsx | ||
|
||
import { useMutation } from "./api/simple.rq"; | ||
import { Form } from "@next-fetch/react-query/form"; | ||
|
||
export default function MyFormPage() { | ||
const mutation = useMutation(); | ||
|
||
return ( | ||
<Form mutation={mutation}> | ||
<input type="text" name="name" /> | ||
<button type="submit">Submit</button> | ||
</Form> | ||
); | ||
} | ||
``` | ||
|
||
## That's it. | ||
|
||
Now you have a fullly working app with Next Fetch and React Query. |
Oops, something went wrong.
081761c
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs:
next-swr-endpoints-example-app – ./
next-swr-endpoints-example-app-git-main-vercel-labs.vercel.app
next-swr-endpoints-example-app-vercel-labs.vercel.app
next-swr-endpoints-example-app.vercel.app
081761c
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs:
next-fetch – ./packages/docs
next-fetch-pi.vercel.app
next-fetch-vercel-labs.vercel.app
next-fetch-git-main-vercel-labs.vercel.app