diff --git a/examples/cms-strapi/README.md b/examples/cms-strapi/README.md index 2e2c23377c470..2ef34cf7d1ce5 100644 --- a/examples/cms-strapi/README.md +++ b/examples/cms-strapi/README.md @@ -179,7 +179,7 @@ You should now be able to see the draft post. To exit the preview mode, you can ### Step 10. Deploy Strapi -To deploy to production, you must first deploy your Strapi app. The Strapi app for our demo at https://next-blog-strapi.vercel.app/ is deployed to Heroku ([here’s the documentation](https://strapi.io/documentation/v3.x/deployment/heroku.html)) and uses Cloudinary for image hosting ([see this file](https://github.com/strapi/strapi-starter-next-blog/blob/master/backend/extensions/upload/config/settings.js)). +To deploy to production, you must first deploy your Strapi app. The Strapi app for our demo at https://next-blog-strapi.vercel.app/ is deployed to Heroku ([here’s the documentation](https://strapi.io/documentation/developer-docs/latest/setup-deployment-guides/deployment/hosting-guides/heroku.html)) and uses Cloudinary for image hosting ([see this file](https://github.com/strapi/strapi-starter-next-blog/blob/23b184781a3f219ad472f6a2c3a3d239a3d16513/backend/extensions/upload/config/settings.js)). ### Step 11. Deploy on Vercel 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 0000000000000..4965832f2c9b0 Binary files /dev/null and b/examples/with-aws-amplify-typescript/public/favicon.ico differ diff --git a/examples/with-aws-amplify-typescript/public/vercel.svg b/examples/with-aws-amplify-typescript/public/vercel.svg new file mode 100644 index 0000000000000..fbf0e25a651c2 --- /dev/null +++ b/examples/with-aws-amplify-typescript/public/vercel.svg @@ -0,0 +1,4 @@ + + + \ 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 +