Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add 2 AWS Amplify SSR examples one with JS and one with TS #8073

Merged
merged 16 commits into from
Jul 29, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions examples/with-aws-amplify-typescript/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
node_modules
.next
out

#amplify
amplify/\#current-cloud-backend
amplify/.config/local-*
amplify/backend/amplify-meta.json
amplify/backend/awscloudformation
build/
dist/
node_modules/
aws-exports.js
awsconfiguration.json
133 changes: 133 additions & 0 deletions examples/with-aws-amplify-typescript/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# AWS Amplify and Typescript with NextJS

[![amplifybutton](https://oneclick.amplifyapp.com/button.svg)](https://console.aws.amazon.com/amplify/home#/deploy?repo=https://github.com/zeit/next.js/tree/canary/examples/with-aws-amplify-typescript)

## Setup

### Using `create-next-app`

Execute [`create-next-app`](https://open.segment.com/create-next-app/) with [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/) or [npx](https://github.com/zkat/npx#readme) to bootstrap the example:

```bash
npx create-next-app --example with-aws-amplify-typescript nextjs-aws-amplify-typescript-app
# or
yarn create next-app --example with-aws-amplify-typescript nextjs-aws-amplify-typescript-app
```

### Download manually

Download the example:

```bash
curl https://codeload.github.com/zeit/next.js/tar.gz/canary | tar -xz --strip=2 next.js-canary/examples/api-routes
cd with-aws-amplify-typescript
```

### Initialize and deploy the Amplify project

<details>
<summary>If you've never used amplify before </summary>

#### Install & Configure Amplify

1. [Sign up](https://portal.aws.amazon.com/billing/signup#/start) for an AWS account
2. Install the AWS Amplify cli:
```sh
npm install -g @aws-amplify/cli
```
3. Configure the Amplify cli
```sh
amplify configure
```
[Read More](https://aws-amplify.github.io/docs/cli-toolchain/quickstart?sdk=js)
</details>

#### Initialize Amplify

```bash
$ amplify init

# <Interactive>
? Enter a name for the project <PROJECT_NAME>
? Enter a name for the environment: dev (or whatever you would like to call this env)
? Choose your default editor: <YOUR_EDITOR_OF_CHOICE>
? Choose the type of app that you're building (Use arrow keys)
android
ios
❯ 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)
? Do you want to use an AWS profile? Y

# </Interactive>
```

#### Add the API

```sh
$ amplify add api
# <Interactive>
? Please select from one of the below mentioned services (Use arrow keys)
❯ GraphQL
REST
? Provide API name: <API_NAME>
? Choose an 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
# </Interactive>
```

#### Deploy infrastructure

```sh
$ amplify push
# <Interactive>
? Are you sure you want to continue? Y
? Do you want to generate code for your newly created GraphQL API? Y
? Choose the code generation language target (Use arrow keys)
javascript
❯ typescript
flow
? Enter the file name pattern of graphql queries, mutations and subscriptions (src/graphql/**/*.js)
? 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)

# </Interactive>
```

### Install & Run

```bash
npm install
npm run dev
# or
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. 👍

## The idea behind the example

This example shows how to build a server rendered web application with NextJS and AWS Amplify.

We use AWS Amplify to generate code and to manage and consume the AWS cloud resources needed for our app.

The NextJS app has dynamic and static routes to demonstrate how to load data on the server based on the incoming request.

Two routes are implemented :

- `/` : A static route that uses getInitialProps to load data from AppSync and renders it on the server (Code in [pages/index.tsx](/pages/index.tsx))

- `/todo/[id]` : A dynamic route that uses getInitialProps 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))


2 changes: 2 additions & 0 deletions examples/with-aws-amplify-typescript/next-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/// <reference types="next" />
/// <reference types="next/types/global" />
27 changes: 27 additions & 0 deletions examples/with-aws-amplify-typescript/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"name": "with-aws-amplify-typescript",
"version": "1.0.0",
"description": "",
"scripts": {
"dev": "next",
"build": "next build",
"start": "next start"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"aws-amplify": "1.1.32",
"immer": "3.1.3",
"nanoid": "2.0.3",
"next": "^9.0.2",
"react": "^16.8.6",
"react-dom": "^16.8.6"
},
"devDependencies": {
"@types/node": "12.6.8",
"@types/react": "16.8.23",
"@types/react-dom": "16.8.4",
"typescript": "3.5.3"
}
}
166 changes: 166 additions & 0 deletions examples/with-aws-amplify-typescript/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
import * as React 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: React.Reducer<State, Action> = (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
})
}
}
}

const createToDo = async (dispatch: React.Dispatch<Action>, 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: React.Dispatch<Action>, 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] = React.useReducer(reducer, {
todos: props.todos,
currentName: '',
})
return (
<div>
<h3>Add a Todo</h3>
<form
onSubmit={ev => {
ev.preventDefault()
createToDo(dispatch, state.currentName)
}}
>
<input
value={state.currentName}
onChange={e => {
dispatch({ type: 'set-current', payload: e.target.value })
}}
/>
<button type="submit">Create Todo</button>
</form>
<h3>Todos List</h3>
{state.todos.map((todo, index) => (
<p key={index}>
<a href={`/todo/${todo.id}`}>{todo.name}</a>
<button
onClick={() => {
deleteToDo(dispatch, todo.id)
}}
>
delete
</button>
</p>
))}
</div>
)
}
App.getInitialProps = async () => {
let result: { data: GetTodoListQuery; errors: {}[] } = await API.graphql(
graphqlOperation(getTodoList, { id: 'global' })
)
if (result.errors) {
console.log('Failed to fetch todolist. ', result.errors)
return { todos: [] }
}
if (result.data.getTodoList !== null) {
return { todos: result.data.getTodoList.todos.items }
}

try {
await API.graphql(
graphqlOperation(createTodoList, {
input: {
id: 'global',
createdAt: `${Date.now()}`,
},
})
)
} catch (err) {
console.warn(err)
}
return { todos: [] }
}
export default App
37 changes: 37 additions & 0 deletions examples/with-aws-amplify-typescript/pages/todo/[id].tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import * as React from 'react'
import { API, graphqlOperation } from 'aws-amplify'

import { GetTodoQuery } from '../../src/API'
import { getTodo } from '../../src/graphql/queries'
import config from '../../src/aws-exports'

API.configure(config)

const TodoPage = (props: { todo: GetTodoQuery['getTodo'] }) => {
return (
<div>
<h2>Individual Todo {props.todo.id}</h2>
<pre>{JSON.stringify(props.todo, null, 2)}</pre>
</div>
)
}

TodoPage.getInitialProps = async context => {
const { id } = context.query
try {
const todo = (await API.graphql({
...graphqlOperation(getTodo),
variables: { id },
})) as { data: GetTodoQuery; errors: {}[] }
if (todo.errors) {
console.log('Failed to fetch todo. ', todo.errors)
return { todo: {} }
}
return { todo: todo.data.getTodo }
} catch (err) {
console.warn(err)
return { todo: {} }
}
}

export default TodoPage
Loading