From a29d8c9eaf693f6022ef4b0584fa3bc51fb57dfc Mon Sep 17 00:00:00 2001 From: Kyle Marshall Date: Wed, 21 Apr 2021 23:06:26 -0400 Subject: [PATCH 1/4] Ensure locale key not duplicated when navigating back to root path with a query or hash value (#24323) This ensures that a duplicate locale key is not prepended to the path when navigating back (using browsers back button) to the root path containing query parameters or a hash value. Current behaviour: * `/fr?value=1` -> `/fr/another` -> click browser back button -> `/fr/fr?value=1` * `/fr?value=1` -> `/fr?value=2` -> click browser back button -> `/fr/fr?value=1` * `/fr#section` -> `/fr/another` -> click browser back button -> `/fr/fr#section` * `/fr#section` -> `/fr#another` -> click browser back button -> `/fr/fr#section` Fix: Remove query string or hash value from path before determining whether to add the locale to the path in `addLocale` function. ## Bug - [x] Related issues linked using `fixes #number` - [x] Integration tests added Fixes: #24287 --- .../next/next-server/lib/router/router.ts | 3 +- test/integration/i18n-support/test/shared.js | 78 +++++++++++++++++++ 2 files changed, 80 insertions(+), 1 deletion(-) diff --git a/packages/next/next-server/lib/router/router.ts b/packages/next/next-server/lib/router/router.ts index 785cbf0bbaf8b..45379d17937d0 100644 --- a/packages/next/next-server/lib/router/router.ts +++ b/packages/next/next-server/lib/router/router.ts @@ -113,7 +113,8 @@ export function addLocale( defaultLocale?: string ) { if (process.env.__NEXT_I18N_SUPPORT) { - const pathLower = path.toLowerCase() + const pathname = pathNoQueryHash(path) + const pathLower = pathname.toLowerCase() const localeLower = locale && locale.toLowerCase() return locale && diff --git a/test/integration/i18n-support/test/shared.js b/test/integration/i18n-support/test/shared.js index f183ee1b9dc0b..f0dac7dafbad3 100644 --- a/test/integration/i18n-support/test/shared.js +++ b/test/integration/i18n-support/test/shared.js @@ -53,6 +53,84 @@ export function runTests(ctx) { expect(await res.text()).toContain('index page') }) + it('should not add duplicate locale key when navigating back to root path with query params', async () => { + const basePath = ctx.basePath || '' + const queryKey = 'query' + const queryValue = '1' + const browser = await webdriver( + ctx.appPort, + `${basePath}/fr?${queryKey}=${queryValue}` + ) + + expect(await browser.eval(() => document.location.pathname)).toBe( + `${basePath}/fr` + ) + expect(await browser.elementByCss('#router-pathname').text()).toBe('/') + expect( + JSON.parse(await browser.elementByCss('#router-query').text()) + ).toEqual({ [queryKey]: queryValue }) + expect(await browser.elementByCss('#router-locale').text()).toBe('fr') + + await browser + .elementByCss('#to-another') + .click() + .waitForElementByCss('#another') + + expect(await browser.eval(() => document.location.pathname)).toBe( + `${basePath}/fr/another` + ) + expect(await browser.elementByCss('#router-pathname').text()).toBe( + '/another' + ) + expect(await browser.elementByCss('#router-locale').text()).toBe('fr') + + await browser.back().waitForElementByCss('#index') + + expect(await browser.eval(() => document.location.pathname)).toBe( + `${basePath}/fr` + ) + expect(await browser.elementByCss('#router-pathname').text()).toBe('/') + expect( + JSON.parse(await browser.elementByCss('#router-query').text()) + ).toEqual({ [queryKey]: queryValue }) + expect(await browser.elementByCss('#router-locale').text()).toBe('fr') + }) + + it('should not add duplicate locale key when navigating back to root path with hash', async () => { + const basePath = ctx.basePath || '' + const hashValue = '#anchor-1' + const browser = await webdriver(ctx.appPort, `${basePath}/fr${hashValue}`) + + expect(await browser.eval(() => document.location.pathname)).toBe( + `${basePath}/fr` + ) + expect(await browser.eval(() => document.location.hash)).toBe(hashValue) + expect(await browser.elementByCss('#router-pathname').text()).toBe('/') + expect(await browser.elementByCss('#router-locale').text()).toBe('fr') + + await browser + .elementByCss('#to-another') + .click() + .waitForElementByCss('#another') + + expect(await browser.eval(() => document.location.pathname)).toBe( + `${basePath}/fr/another` + ) + expect(await browser.elementByCss('#router-pathname').text()).toBe( + '/another' + ) + expect(await browser.elementByCss('#router-locale').text()).toBe('fr') + + await browser.back().waitForElementByCss('#index') + + expect(await browser.eval(() => document.location.pathname)).toBe( + `${basePath}/fr` + ) + expect(await browser.eval(() => document.location.hash)).toBe(hashValue) + expect(await browser.elementByCss('#router-pathname').text()).toBe('/') + expect(await browser.elementByCss('#router-locale').text()).toBe('fr') + }) + it('should handle navigating back to different casing of locale', async () => { const browser = await webdriver( ctx.appPort, From 9edb1ef16d55f1de694a050eac396335cc260f48 Mon Sep 17 00:00:00 2001 From: Janicklas Ralph Date: Wed, 21 Apr 2021 20:35:38 -0700 Subject: [PATCH 2/4] Adding script loader files to package.json (#24326) Adding script loader files to package.json --- packages/next/package.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/next/package.json b/packages/next/package.json index 23e3436d1c1b2..366c470a826e9 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -28,6 +28,8 @@ "dynamic.d.ts", "error.js", "error.d.ts", + "experimental-script.js", + "experimental-script.d.ts", "head.js", "head.d.ts", "image.js", From 1caa7f4971c9f524df75439099f64f8866a2e025 Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Thu, 22 Apr 2021 03:26:07 -0500 Subject: [PATCH 3/4] Bump next-babel-loader cache key (#24335) This bumps the babel cache key since we modified the `react-loadable` babel plugin and we don't want any cached versions used since the module names generated in previous cached version won't match the newly expected values. x-ref: https://github.com/vercel/next.js/pull/24281 ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. ## Documentation / Examples - [ ] Make sure the linting passes --- packages/next/build/webpack/loaders/next-babel-loader.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/next/build/webpack/loaders/next-babel-loader.js b/packages/next/build/webpack/loaders/next-babel-loader.js index 4314e1bfdf777..9386fdabf67f2 100644 --- a/packages/next/build/webpack/loaders/next-babel-loader.js +++ b/packages/next/build/webpack/loaders/next-babel-loader.js @@ -2,9 +2,9 @@ import { join } from 'path' import * as Log from '../../output/log' import babelLoader from './babel-loader/src/index' -// increment 'o' to invalidate cache +// increment 'p' to invalidate cache // eslint-disable-next-line no-useless-concat -const cacheKey = 'babel-cache-' + 'o' + '-' +const cacheKey = 'babel-cache-' + 'p' + '-' const nextBabelPreset = require('../../babel/preset') const customBabelLoader = babelLoader((babel) => { From c481147b865d5d34afafb89c863a4e68a6cfac84 Mon Sep 17 00:00:00 2001 From: Jarrod Watts <35651410+jarrodwatts@users.noreply.github.com> Date: Thu, 22 Apr 2021 20:01:27 +1000 Subject: [PATCH 4/4] Update with-aws-amplify-typescript example (#24292) - Update the Readme to use the latest Amplify CLI versions' prompts - Update the example to use the [SSR features of AWS Amplify ](https://aws.amazon.com/blogs/mobile/ssr-support-for-aws-amplify-javascript-libraries/) - Update to use Next version `10` instead of `9` - Correctly use the types produced by the Amplify CLI in `API.ts` - Add `amplify auth` and auth rules to the GraphQL model as suggested by the Amplify CLI. --- .../with-aws-amplify-typescript/.gitignore | 10 +- .../with-aws-amplify-typescript/README.md | 82 ++++++-- .../with-aws-amplify-typescript/package.json | 21 +-- .../pages/index.tsx | 177 ------------------ .../pages/todo/[id].tsx | 48 ----- .../public/favicon.ico | Bin 0 -> 15086 bytes .../public/vercel.svg | 4 + .../schema.graphql | 15 -- .../src/pages/index.tsx | 118 ++++++++++++ .../src/pages/todo/[id].tsx | 100 ++++++++++ .../src/styles/Home.module.css | 123 ++++++++++++ .../src/styles/globals.css | 16 ++ 12 files changed, 445 insertions(+), 269 deletions(-) delete mode 100644 examples/with-aws-amplify-typescript/pages/index.tsx delete mode 100644 examples/with-aws-amplify-typescript/pages/todo/[id].tsx create mode 100644 examples/with-aws-amplify-typescript/public/favicon.ico create mode 100644 examples/with-aws-amplify-typescript/public/vercel.svg delete mode 100644 examples/with-aws-amplify-typescript/schema.graphql create mode 100644 examples/with-aws-amplify-typescript/src/pages/index.tsx create mode 100644 examples/with-aws-amplify-typescript/src/pages/todo/[id].tsx create mode 100644 examples/with-aws-amplify-typescript/src/styles/Home.module.css create mode 100644 examples/with-aws-amplify-typescript/src/styles/globals.css diff --git a/examples/with-aws-amplify-typescript/.gitignore b/examples/with-aws-amplify-typescript/.gitignore index 04be752dd476c..0519a706d5cb0 100644 --- a/examples/with-aws-amplify-typescript/.gitignore +++ b/examples/with-aws-amplify-typescript/.gitignore @@ -36,9 +36,17 @@ yarn-error.log* # Amplify amplify/\#current-cloud-backend amplify/.config/local-* +amplify/logs +amplify/mock-data amplify/backend/amplify-meta.json amplify/backend/awscloudformation -build/ +amplify/backend/.temp dist/ aws-exports.js awsconfiguration.json +amplifyconfiguration.json +amplifyconfiguration.dart +amplify-build-config.json +amplify-gradle-config.json +amplifytools.xcconfig +.secret-* diff --git a/examples/with-aws-amplify-typescript/README.md b/examples/with-aws-amplify-typescript/README.md index 55fae18242010..a147f679d8dc7 100644 --- a/examples/with-aws-amplify-typescript/README.md +++ b/examples/with-aws-amplify-typescript/README.md @@ -6,9 +6,9 @@ This example shows how to build a server rendered web application with NextJS an Two routes are implemented : -- `/` : A static route that uses getStaticProps to load data from AppSync and renders it on the server (Code in [pages/index.tsx](pages/index.tsx)) +- `/` : A server-rendered route that uses `getServersideProps` to load data from AppSync and renders it on the server (Code in [pages/index.tsx](src/pages/index.tsx)) -- `/todo/[id]` : A dynamic route that uses `getStaticProps` and the id from the provided context to load a single todo from AppSync and render it on the server. (Code in [pages/todo/[id].tsx](pages/todo/[id].tsx)) +- `/todo/[id]` : A dynamic route that uses `getStaticPaths`, `getStaticProps` and the id from the provided context to load a single todo from AppSync and render it on the server. (Code in [pages/todo/[id].tsx](src/pages/todo/[id].tsx)) ## How to use @@ -59,15 +59,17 @@ $ amplify init ❯ javascript ? What javascript framework are you using react ? Source Directory Path: src -? Distribution Directory Path: out -? Build Command: (npm run-script build) -? Start Command: (npm run-script start) +? Distribution Directory Path: build +? Build Command: npm run build +? Start Command: npm run start ? Do you want to use an AWS profile? Y +? Select the authentication method you want to use: AWS Profile +? Please choose the profile you want to use: ``` -#### Add the API +#### Add the API and the Auth ```sh $ amplify add api @@ -76,14 +78,66 @@ $ amplify add api ❯ GraphQL REST ? Provide API name: -? Choose an authorization type for the API (Use arrow keys) +? Choose the default authorization type for the API (Use arrow keys) ❯ API key Amazon Cognito User Pool -? Do you have an annotated GraphQL schema? (y/N) y -? Provide your schema file path: ./schema.graphql + IAM + OpenID Connect +? Enter a description for the API key: +? After how many days from now the API key should expire (1-365): 7 +? Do you want to configure advanced settings for the GraphQL API: + No, I am done. +❯ Yes, I want to make some additional changes. +? Configure additional auth types? y +? Choose the additional authorization types you want to configure for the API +❯(*) Amazon Cognito User Pool + ( ) IAM + ( ) OpenID Connect +Do you want to use the default authentication and security configuration? (Use arrow keys) +❯ Default configuration + Default configuration with Social Provider (Federation) + Manual configuration + I want to learn more. +How do you want users to be able to sign in? (Use arrow keys) + Username +❯ Email + Phone Number + Email or Phone Number + I want to learn more. +Do you want to configure advanced settings? (Use arrow keys) +❯ No, I am done. + Yes, I want to make some additional changes. +? Enable conflict detection? N +? Do you have an annotated GraphQL schema? N +? Choose a schema template: (Use arrow keys) +❯ Single object with fields (e.g., “Todo” with ID, name, description) + One-to-many relationship (e.g., “Blogs” with “Posts” and “Comments”) + Objects with fine-grained access control (e.g., a project management app with owner-based authorization) +? Do you want to edit the schema now? Y # ``` +#### Edit GraphQL Schema + +Open [`amplify/backend/api/nextjswithamplifyts/schema.graphql`](amplify/backend/api/nextjswithamplifyts/schema.graphql) and change it to the following: + +``` +type Todo + @model + @auth( + rules: [ + { allow: owner } # Allow the creator of a todo to perform Create, Update, Delete operations. + { allow: public, operations: [read] } # Allow public (guest users without an account) to Read todos. + { allow: private, operations: [read] } # Allow private (other signed in users) to Read todos. + ] + ) { + id: ID! + name: String! + description: String +} + +``` + #### Deploy infrastructure ```sh @@ -95,10 +149,10 @@ $ amplify push javascript ❯ typescript flow -? Enter the file name pattern of graphql queries, mutations and subscriptions (src/graphql/**/*.js) +? Enter the file name pattern of graphql queries, mutations and subscriptions (src/graphql/**/*.ts) ? Do you want to generate/update all possible GraphQL operations - queries, mutations and subscriptions (Y/n) Y ? Enter maximum statement depth [increase from default if your schema is deeply nested] (2) - +? Enter the file name for the generated code: src\API.ts # ``` @@ -111,9 +165,3 @@ npm run dev yarn yarn dev ``` - -### Edit GraphQL Schema - -1. Open [`amplify/backend/api/nextjswithamplifyts/schema.graphql`](amplify/backend/api/nextjswithamplifyts/schema.graphql) and change what you need to. -2. Run `amplify push` -3. 👍 diff --git a/examples/with-aws-amplify-typescript/package.json b/examples/with-aws-amplify-typescript/package.json index 7bd19b1d7a763..1cbdae6375a08 100644 --- a/examples/with-aws-amplify-typescript/package.json +++ b/examples/with-aws-amplify-typescript/package.json @@ -3,7 +3,7 @@ "version": "1.0.0", "description": "", "scripts": { - "dev": "next", + "dev": "next dev", "build": "next build", "start": "next start" }, @@ -11,17 +11,16 @@ "author": "", "license": "MIT", "dependencies": { - "aws-amplify": "2.1.0", - "immer": "3.1.3", - "nanoid": "2.0.3", - "next": "latest", - "react": "16.13.1", - "react-dom": "16.13.1" + "@aws-amplify/ui-react": "^1.0.7", + "aws-amplify": "^3.3.27", + "next": "10.1.3", + "react": "17.0.2", + "react-dom": "17.0.2" }, "devDependencies": { - "@types/node": "12.6.8", - "@types/react": "16.9.36", - "@types/react-dom": "16.9.8", - "typescript": "4.0" + "@types/node": "^14.14.41", + "@types/react": "^17.0.3", + "@types/react-dom": "^17.0.3", + "typescript": "^4.2.4" } } diff --git a/examples/with-aws-amplify-typescript/pages/index.tsx b/examples/with-aws-amplify-typescript/pages/index.tsx deleted file mode 100644 index d2aeb25c590f1..0000000000000 --- a/examples/with-aws-amplify-typescript/pages/index.tsx +++ /dev/null @@ -1,177 +0,0 @@ -import { Reducer, useReducer, Dispatch } from 'react' -import { API, graphqlOperation } from 'aws-amplify' -import nanoid from 'nanoid' -import produce from 'immer' - -import { ListTodosQuery, GetTodoListQuery } from '../src/API' -import config from '../src/aws-exports' -import { - createTodo, - deleteTodo, - createTodoList, -} from '../src/graphql/mutations' -import { getTodoList } from '../src/graphql/queries' - -const MY_ID = nanoid() -API.configure(config) - -type Todo = Omit< - ListTodosQuery['listTodos']['items'][0], - '__typename' | 'todoList' -> - -type Props = { - todos: Todo[] -} - -type State = { - todos: Todo[] - currentName: string -} - -type Action = - | { - type: 'add-todo' - payload: Todo - } - | { - type: 'delete-todo' - payload: string - } - | { - type: 'reset-current' - } - | { type: 'set-current'; payload: string } - -const reducer: Reducer = (state, action) => { - switch (action.type) { - case 'add-todo': { - return produce(state, (draft) => { - draft.todos.push(action.payload) - }) - } - case 'delete-todo': { - const index = state.todos.findIndex(({ id }) => action.payload === id) - if (index === -1) return state - return produce(state, (draft) => { - draft.todos.splice(index, 1) - }) - } - case 'reset-current': { - return produce(state, (draft) => { - draft.currentName = '' - }) - } - case 'set-current': { - return produce(state, (draft) => { - draft.currentName = action.payload - }) - } - default: { - return state - } - } -} - -const createToDo = async (dispatch: Dispatch, currentToDo) => { - const todo = { - id: nanoid(), - name: currentToDo, - createdAt: `${Date.now()}`, - completed: false, - todoTodoListId: 'global', - userId: MY_ID, - } - dispatch({ type: 'add-todo', payload: todo }) - dispatch({ type: 'reset-current' }) - try { - await API.graphql(graphqlOperation(createTodo, { input: todo })) - } catch (err) { - dispatch({ type: 'set-current', payload: todo.name }) - console.warn('Error adding to do ', err) - } -} -const deleteToDo = async (dispatch: Dispatch, id: string) => { - dispatch({ type: 'delete-todo', payload: id }) - try { - await API.graphql({ - ...graphqlOperation(deleteTodo), - variables: { input: { id } }, - }) - } catch (err) { - console.warn('Error deleting to do ', err) - } -} -const App = (props: Props) => { - const [state, dispatch] = useReducer(reducer, { - todos: props.todos, - currentName: '', - }) - return ( -
-

Add a Todo

-
{ - ev.preventDefault() - createToDo(dispatch, state.currentName) - }} - > - { - dispatch({ type: 'set-current', payload: e.target.value }) - }} - /> - -
-

Todos List

- {state.todos.map((todo, index) => ( -

- {todo.name} - -

- ))} -
- ) -} - -export const getStaticProps = async () => { - let result = (await API.graphql( - graphqlOperation(getTodoList, { id: 'global' }) - )) as { data: GetTodoListQuery; errors: any[] } - - if (result.errors) { - console.error('Failed to fetch todolist.', result.errors) - throw new Error(result.errors[0].message) - } - if (result.data.getTodoList !== null) { - return { - props: { - todos: result.data.getTodoList.todos.items, - }, - } - } - - await API.graphql( - graphqlOperation(createTodoList, { - input: { - id: 'global', - createdAt: `${Date.now()}`, - }, - }) - ) - - return { - props: { - todos: [], - }, - } -} - -export default App diff --git a/examples/with-aws-amplify-typescript/pages/todo/[id].tsx b/examples/with-aws-amplify-typescript/pages/todo/[id].tsx deleted file mode 100644 index c68c8768635f5..0000000000000 --- a/examples/with-aws-amplify-typescript/pages/todo/[id].tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { API, graphqlOperation } from 'aws-amplify' - -import { GetTodoQuery, GetTodoListQuery } from '../../src/API' -import { getTodo, getTodoList } from '../../src/graphql/queries' -import config from '../../src/aws-exports' - -API.configure(config) - -const TodoPage = (props: { todo: GetTodoQuery['getTodo'] }) => { - return ( -
-

Individual Todo {props.todo.id}

-
{JSON.stringify(props.todo, null, 2)}
-
- ) -} - -export const getStaticPaths = async () => { - let result = (await API.graphql( - graphqlOperation(getTodoList, { id: 'global' }) - )) as { data: GetTodoListQuery; errors: any[] } - if (result.errors) { - console.error('Failed to fetch todos paths.', result.errors) - throw new Error(result.errors[0].message) - } - const paths = result.data.getTodoList.todos.items.map(({ id }) => ({ - params: { id }, - })) - return { paths, fallback: false } -} - -export const getStaticProps = async ({ params: { id } }) => { - const todo = (await API.graphql({ - ...graphqlOperation(getTodo), - variables: { id }, - })) as { data: GetTodoQuery; errors: any[] } - if (todo.errors) { - console.error('Failed to fetch todo.', todo.errors) - throw new Error(todo.errors[0].message) - } - return { - props: { - todo: todo.data.getTodo, - }, - } -} - -export default TodoPage diff --git a/examples/with-aws-amplify-typescript/public/favicon.ico b/examples/with-aws-amplify-typescript/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..4965832f2c9b0605eaa189b7c7fb11124d24e48a GIT binary patch literal 15086 zcmeHOOH5Q(7(R0cc?bh2AT>N@1PWL!LLfZKyG5c!MTHoP7_p!sBz0k$?pjS;^lmgJ zU6^i~bWuZYHL)9$wuvEKm~qo~(5=Lvx5&Hv;?X#m}i|`yaGY4gX+&b>tew;gcnRQA1kp zBbm04SRuuE{Hn+&1wk%&g;?wja_Is#1gKoFlI7f`Gt}X*-nsMO30b_J@)EFNhzd1QM zdH&qFb9PVqQOx@clvc#KAu}^GrN`q5oP(8>m4UOcp`k&xwzkTio*p?kI4BPtIwX%B zJN69cGsm=x90<;Wmh-bs>43F}ro$}Of@8)4KHndLiR$nW?*{Rl72JPUqRr3ta6e#A z%DTEbi9N}+xPtd1juj8;(CJt3r9NOgb>KTuK|z7!JB_KsFW3(pBN4oh&M&}Nb$Ee2 z$-arA6a)CdsPj`M#1DS>fqj#KF%0q?w50GN4YbmMZIoF{e1yTR=4ablqXHBB2!`wM z1M1ke9+<);|AI;f=2^F1;G6Wfpql?1d5D4rMr?#f(=hkoH)U`6Gb)#xDLjoKjp)1;Js@2Iy5yk zMXUqj+gyk1i0yLjWS|3sM2-1ECc;MAz<4t0P53%7se$$+5Ex`L5TQO_MMXXi04UDIU+3*7Ez&X|mj9cFYBXqM{M;mw_ zpw>azP*qjMyNSD4hh)XZt$gqf8f?eRSFX8VQ4Y+H3jAtvyTrXr`qHAD6`m;aYmH2zOhJC~_*AuT} zvUxC38|JYN94i(05R)dVKgUQF$}#cxV7xZ4FULqFCNX*Forhgp*yr6;DsIk=ub0Hv zpk2L{9Q&|uI^b<6@i(Y+iSxeO_n**4nRLc`P!3ld5jL=nZRw6;DEJ*1z6Pvg+eW|$lnnjO zjd|8>6l{i~UxI244CGn2kK@cJ|#ecwgSyt&HKA2)z zrOO{op^o*- + + \ No newline at end of file diff --git a/examples/with-aws-amplify-typescript/schema.graphql b/examples/with-aws-amplify-typescript/schema.graphql deleted file mode 100644 index 9deab31388242..0000000000000 --- a/examples/with-aws-amplify-typescript/schema.graphql +++ /dev/null @@ -1,15 +0,0 @@ -type Todo @model { - # ! means non-null GraphQL fields are allowed to be null by default - id: ID! - name: String! - createdAt: String! - completed: Boolean! - todoList: TodoList! @connection(name: "SortedList") - userId: String! -} - -type TodoList @model { - id: ID! - createdAt: String! - todos: [Todo] @connection(name: "SortedList", sortField: "createdAt") -} diff --git a/examples/with-aws-amplify-typescript/src/pages/index.tsx b/examples/with-aws-amplify-typescript/src/pages/index.tsx new file mode 100644 index 0000000000000..dae49052ea681 --- /dev/null +++ b/examples/with-aws-amplify-typescript/src/pages/index.tsx @@ -0,0 +1,118 @@ +import { AmplifyAuthenticator } from '@aws-amplify/ui-react' +import { Amplify, API, Auth, withSSRContext } from 'aws-amplify' +import Head from 'next/head' +import awsExports from '../aws-exports' +import { createTodo } from '../graphql/mutations' +import { listTodos } from '../graphql/queries' +import { + CreateTodoInput, + CreateTodoMutation, + ListTodosQuery, + Todo, +} from '../API' +import { GRAPHQL_AUTH_MODE } from '@aws-amplify/api' +import { useRouter } from 'next/router' +import { GetServerSideProps } from 'next' +import styles from '../styles/Home.module.css' + +Amplify.configure({ ...awsExports, ssr: true }) + +export default function Home({ todos = [] }: { todos: Todo[] }) { + const router = useRouter() + + async function handleCreateTodo(event) { + event.preventDefault() + + const form = new FormData(event.target) + + try { + const createInput: CreateTodoInput = { + name: form.get('title').toString(), + description: form.get('content').toString(), + } + + const request = (await API.graphql({ + authMode: GRAPHQL_AUTH_MODE.AMAZON_COGNITO_USER_POOLS, + query: createTodo, + variables: { + input: createInput, + }, + })) as { data: CreateTodoMutation; errors: any[] } + + router.push(`/todo/${request.data.createTodo.id}`) + } catch ({ errors }) { + console.error(...errors) + throw new Error(errors[0].message) + } + } + + return ( +
+ + Amplify + Next.js + + + +
+

Amplify + Next.js

+ +

+ {todos.length} + Todos +

+ +
+ {todos.map((todo) => ( + +

{todo.name}

+

{todo.description}

+
+ ))} + +
+

New Todo

+ + +
+
+ Title + +
+ +
+ Content +