Skip to content

Commit

Permalink
Merge pull request #6496 from aws-amplify/fix-next-js
Browse files Browse the repository at this point in the history
Updated Next.js Tutorial to address customer feedback
  • Loading branch information
renebrandel authored Nov 20, 2023
2 parents 3449483 + 60b11e7 commit 735ce21
Show file tree
Hide file tree
Showing 2 changed files with 132 additions and 30 deletions.
133 changes: 116 additions & 17 deletions src/pages/[platform]/start/getting-started/data-model/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -1158,28 +1158,130 @@ Now if you wish to subscribe to data, import the `onCreateTodo` subscription and

In this section you will create a way to list and create todos from the Next.js application. To do this, you will fetch & render the latest todos from the server as well as create a new todo.

Open **app/page.tsx** and replace it with the following code:
### Generate the Amplify GraphQL API client

```jsx
// app/page.tsx
To make any GraphQL API requests service-side, we need to first generate an API client that we can use on server-side. To generate a new API client, import `generateServerClientUsingCookies` from `@aws-amplify/adapter-nextjs/api` and use it to generate a cookiesClient using the **amplifyconfiguration.json** in our project as `config` and `cookies` from `next/headers`.

<Callout info>

Amplify offers two API clients for Next.js server-side runtimes. Use `generateServerClientUsingCookies` primarily for use cases where `cookies` from `next/headers` is available, such as in App Router's React Server Components, Server Actions. Use `generateServerClientUsingReqRes` for use cases where a `NextRequest`/`NextResponse` are available, such as in the Pages Router or Middleware. Review [Connect to data from server-side runtimes](/[platform]/build-a-backend/graphqlapi/connect-from-server-runtime/) to review in-depth which API client to use for which use cases.

</Callout>

Open **src/app/page.tsx** and replace it with the following code:

```tsx
import { generateServerClientUsingCookies } from '@aws-amplify/adapter-nextjs/api';
import { revalidatePath } from 'next/cache';
import { cookies } from 'next/headers';

import * as mutations from '@/src/graphql/mutations';
import * as queries from '@/src/graphql/queries';
import config from '@/amplifyconfiguration.json';

import config from '@/src/amplifyconfiguration.json';
export const cookiesClient = generateServerClientUsingCookies({
config,
cookies
});

export default async function Home() {
return (
<div
style={{
maxWidth: '500px',
margin: '0 auto',
textAlign: 'center',
marginTop: '100px'
}}
>
<form>
<input name="name" placeholder="Add a todo" />
<button type="submit">Add</button>
</form>
</div>
);
}
```

### Create a form for submitting the todos

In Next.js you can use a [Server Action](https://nextjs.org/docs/app/api-reference/functions/server-actions) to handle form submission server-side. Let's add a Server Action which submits its data to the `createTodo` function. When called, `createTodo` should send a GraphQL mutation via `cookiesClient.graphql(...)` to the GraphQL API, then call `revalidatePath` from the Next.js cache to invalidate the page cache and fetch the latest todos.

Update the **src/app/page.tsx** with the following code:

```tsx
import { generateServerClientUsingCookies } from '@aws-amplify/adapter-nextjs/api';
import { cookies } from 'next/headers';
// 1. Add the following two imports
import { revalidatePath } from 'next/cache';
import * as mutations from '@/graphql/mutations';

import config from '@/amplifyconfiguration.json';

export const cookiesClient = generateServerClientUsingCookies({
config,
cookies
});

// 2. Create a new Server Action
async function createTodo(formData: FormData) {
'use server';
"use server"
const { data } = await cookiesClient.graphql({
query: mutations.createTodo,
variables: {
input: {
name: formData.get('name')?.toString() ?? ''
}
}
});

await cookiesClient.graphql({
console.log("Created Todo: ", data?.createTodo )

revalidatePath('/');
}

export default async function Home() {
return (
<div
style={{
maxWidth: '500px',
margin: '0 auto',
textAlign: 'center',
marginTop: '100px'
}}
>
{/* 3. Update the form's action to use the
new create Todo Server Action*/}
<form action={createTodo}>
<input name="name" placeholder="Add a todo" />
<button type="submit">Add</button>
</form>
</div>
);
}
```

### List todos

Using `cookiesClient.graphql(...)` we make GraphQL queries as well. Pass in the `listTodos` query and assign the items returned to `todos` then iterate over them to display in a `<ul>` tag. If there are no todos, we display the message "No todos, please add one".

Update the **src/app/page.tsx** with the following code:

```tsx
import { generateServerClientUsingCookies } from '@aws-amplify/adapter-nextjs/api';
import { cookies } from 'next/headers';
import { revalidatePath } from 'next/cache';
import * as mutations from '@/graphql/mutations';
// 1. Add the queries as an import
import * as queries from '@/graphql/queries';

import config from '@/amplifyconfiguration.json';

export const cookiesClient = generateServerClientUsingCookies({
config,
cookies
});

async function createTodo(formData: FormData) {
"use server"
const { data } = await cookiesClient.graphql({
query: mutations.createTodo,
variables: {
input: {
Expand All @@ -1188,10 +1290,13 @@ async function createTodo(formData: FormData) {
}
});

console.log("Created Todo: ", data?.createTodo )

revalidatePath('/');
}

export default async function Home() {
// 2. Fetch additional todos
const { data, errors } = await cookiesClient.graphql({
query: queries.listTodos
});
Expand All @@ -1212,12 +1317,14 @@ export default async function Home() {
<button type="submit">Add</button>
</form>

{/* 3. Handle edge cases & zero state & error states*/}
{(!todos || todos.length === 0 || errors) && (
<div>
<p>No todos, please add one.</p>
</div>
)}

{/* 4. Display todos*/}
<ul>
{todos.map((todo) => {
return <li style={{ listStyle: 'none' }}>{todo.name}</li>;
Expand All @@ -1228,14 +1335,6 @@ export default async function Home() {
}
```

Let's walk through some of this file:
- First we import `generateServerClientUsingCookies` from `@aws-amplify/adapter-nextjs/api` and use it to generate a `cookiesClient` using the `amplifyconfiguration.json` in our project as `config` and `cookies` from `@next/headers`.
- Using `cookiesClient.graphql` we call `queries.listTodos` and await the response and assign the items returned to `todos` then iterate over them to display. If there are no todos, display the message "No todos, please add one".
- The form above the list of todos is a [Server-only form in Next.js](https://nextjs.org/docs/app/building-your-application/data-fetching/forms-and-mutations#server-only-forms) which submits it's data to the `createTodo` async function. When called, `createTodo` sends a mutation via `cookiesClient.graphql` to the GraphQL API, then calls `revalidatePath` from the Next.js cache to invalidate the page cache and fetch the latest todos.
</InlineFilter>

## Run locally
Expand Down
29 changes: 16 additions & 13 deletions src/pages/[platform]/start/getting-started/setup/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ To set up the project, you'll first create a new Next.js app with [Create Next A
From your projects directory, run the following commands:

```bash
npm create next-app@14 -- next-amplified --ts --no-eslint --no-src-dir --import-alias '@/*' --no-tailwind --app
npm create next-app@14 -- next-amplified --ts --no-eslint --src-dir --import-alias '@/*' --no-tailwind --app
cd next-amplified
```

Expand All @@ -223,7 +223,7 @@ This command will bootstrap the project with the following options:
- App Router
- TypeScript
- No ESLint
- No `src` directory
- With the `src` directory
- No Tailwind CSS
- imports aliased to `@/` (the default)

Expand Down Expand Up @@ -465,11 +465,8 @@ For more information on AWS Profiles, see:
https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html

? Please choose the profile you want to use: default

```

> Where possible the CLI will infer the proper configuration based on the type of project Amplify is being initialized in. In this case it knew you are using Create Next App and provided the proper configuration for type of app, framework, source, distribution, build, and start options.
</InlineFilter>

<InlineFilter filters={['react-native']}>
Expand Down Expand Up @@ -542,9 +539,8 @@ Select AWS profile and then choose the profile you configured in the [Prerequisi
When you initialize a new Amplify project, a few things happen:

- It creates a top level directory called `amplify` that stores your backend definition. During the tutorial you'll add cloud capabilities, such as GraphQL API and web hosting. As you add these features, the `amplify` folder will grow with infrastructure-as-code templates that define your backend stack. Infrastructure-as-code is a best practice way to create a replicable backend stack.
- It creates a file called `aws-exports.js` in the `src` directory that holds all the configuration for the services you create with Amplify. This is how the Amplify JavaScript client library is able to get the necessary information to connect to your backend services.
- It creates a file called `amplifyconfiguration.json` in your designated **Source Directory Path** that holds all the configuration for the services you create with Amplify. This is how the Amplify JavaScript client library is able to get the necessary information to connect to your backend services.
- It modifies the `.gitignore` file, adding some generated files to the ignore list
- An Amplify project is created for you that can be accessed within the AWS Console by running `amplify console`. The AWS Amplify console provides a list of backend environments, deep links to provisioned resources per Amplify category, deployment statuses, and instructions on how to promote, clone, pull, and delete backend resources.

## Install Amplify Libraries

Expand Down Expand Up @@ -613,11 +609,11 @@ Angular CLI output warnings: if you see CommonJS or AMD dependencies optimizatio

</InlineFilter>

<InlineFilter filters={['react','javascript','angular','vue']}>
## Set up frontend

Next, configure Amplify so it can interact with backend services.
Next, configure the Amplify libraries client-side so it can interact with backend services.

<InlineFilter filters={['react','javascript','angular','nextjs','vue']}>

<InlineFilter filters={['react']}>
Open **src/main.jsx** and add the following code below the last import:
Expand All @@ -639,7 +635,7 @@ Next, configure Amplify so it can interact with backend services.
Open **src/main.js** and add the following code below the last import:
</InlineFilter>

<InlineFilter filters={["angular", "nextjs", "react", "vue"]}>
<InlineFilter filters={["angular", "react", "vue"]}>

```javascript
import { Amplify } from 'aws-amplify';
Expand All @@ -649,9 +645,6 @@ Next, configure Amplify so it can interact with backend services.

</InlineFilter>

<InlineFilter filters={["nextjs"]}>
If you are using Next.js App Router, refer to the [Server Side Rendering](/[platform]/build-a-backend/server-side-rendering/) document for details on configuring Amplify on both the server and client sides.
</InlineFilter>

<InlineFilter filters={["javascript"]}>

Expand Down Expand Up @@ -712,8 +705,18 @@ In order to import `amplifyconfiguration.json` files in Typescript, you may need

</InlineFilter>

<InlineFilter filters={["javascript", "react", "angular", "react-native", "vue"]}>

And that's all it takes to configure Amplify. As you add or remove categories and make updates to your backend configuration using the Amplify CLI, the configuration in **amplifyconfiguration.json** will update automatically.

Now that your app is set up and Amplify is initialized, you can add an API in the next step.

</InlineFilter>

<InlineFilter filters={["nextjs"]}>

That's all it takes to install Amplify libraries with your Next.js application. Next, we'll create a new backend API and database tables. Then, we'll use the Amplify library's Next.js adapter to make API requests to your new backend.

</InlineFilter>

</InlineFilter>

0 comments on commit 735ce21

Please sign in to comment.