AWS mobile-hub が react-native
に対応したとのことなので、マニュアル (Developer Guide) を元にサンプルアプリを作成・試してみる。
今回は Dynamodb にてテーブルを作成し、CRUDアプリケーションをサンプルに沿って作成してみた。
はじめに
前回の記事から Dynamodb に登録・参照する簡単なサンプルプログラムを実行してみる。
Backend (Mobile Hub) の設定
テーブルの作成
コマンドから下記を実行
awsmobile database enable --prompt
マニュアル通りの Notes テーブルを作成
Welcome to NoSQL database wizard
You will be asked a series of questions to help determine how to best construct your NoSQL database table.
? Should the data of this table be open or restricted by user? Open
? Table name Notes
You can now add columns to the table.
? What would you like to name this column NoteId
? Choose the data type string
? Would you like to add another column Yes
? What would you like to name this column NoteTitle
? Choose the data type string
? Would you like to add another column Yes
? What would you like to name this column NoteContent
? Choose the data type string
? Would you like to add another column No
Before you create the database, you must specify how items in your table are uniquely organized. This is done by specifying a Primary key. The primary key uniquely identifies each item in the table, so that no two items can have the same key.
This could be and individual column or a combination that has "primary key" and a "sort key".
To learn more about primary key:
http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.CoreComponents.html#HowItWorks.CoreComponents.PrimaryKey
? Select primary key NoteId
? Select sort key (No Sort Key)
You can optionally add global secondary indexes for this table. These are useful when running queries defined by a different column than the primary key.
To learn more about indexes:
http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.CoreComponents.html#HowItWorks.CoreComponents.SecondaryIndexes
? Add index No
Table Notes saved
CRUD API の作成
下記コマンドにて cloud-api を作成
awsmobile cloud-api enable --prompt
先ほど作成した Notes テーブルから生成を選択。ログイン必須を選択した。
This feature will create an API using Amazon API Gateway and AWS Lambda. You can optionally have the lambda function perform CRUD operations against your Amazon DynamoDB table.
? Select from one of the choices below. Create CRUD API for an existing Amazon DynamoDB table
? Select Amazon DynamoDB table to connect to a CRUD API Notes
? Restrict API access to signed-in users Yes
Adding lambda function code on:
/Users/masanao/dev/awsmobile/awsmobilejs/backend/cloud-api/Notes/
...
Path to be used on API for get and remove an object should be like:
/Notes/object/:NoteId
Path to be used on API for list objects on get method should be like:
/Notes/:NoteId
JSON to be used as data on put request should be like:
{
"NoteTitle": "INSERT VALUE HERE",
"NoteContent": "INSERT VALUE HERE",
"NoteId": "INSERT VALUE HERE"
}
To test the api from the command line (after awsmobile push) use this commands
awsmobile cloud-api invoke NotesCRUD <method> <path> [init]
Api NotesCRUD saved
設定の反映
下記コマンドにて AWS のモジュールを作成
awsmobile push
作成に少し時間がかかるので、完了するのを待つ。
building backend
building cloud-api
zipping Notes
generating backend project content
backend project content generation successful
done, build artifacts are saved at:
/Users/masanao/dev/awsmobile/awsmobilejs/.awsmobile/backend-build
preparing for backend project update: awsmobile
uploading Notes-20180108140947.zip
upload Successful Notes-20180108140947.zip
done
updating backend project: awsmobile
awsmobile update api call returned with no error
waiting for the formation of cloud-api to complete
cloud-api update finished with status code: CREATE_COMPLETE
Successfully updated the backend awsmobile project: awsmobile
retrieving the latest backend awsmobile project information
awsmobile project's details logged at: awsmobilejs/#current-backend-info/backend-details.json
awsmobile project's access information logged at: awsmobilejs/#current-backend-info/aws-exports.js
awsmobile project's access information copied to: aws-exports.js
awsmobile project's specifications logged at: awsmobilejs/#current-backend-info/mobile-hub-project.yml
contents in #current-backend-info/ is synchronized with the latest in the aws cloud
フロントエンドの構築
App.js を編集・CRUDのコードを書く。
モジュール import
import { API } from 'aws-amplify-react-native';
CRUDコードの記述
サンプルコードをそのまま貼り付けすることとした。
state の追加
state = {
apiResponse: null,
noteId: ''
};
handleChangeNoteId = (event) => {
this.setState({noteId: event});
}
saveNote 関数の追加
// Create a new Note according to the columns we defined earlier
async saveNote() {
let newNote = {
body: {
"NoteTitle": "My first note!",
"NoteContent": "This is so cool!",
"NoteId": this.state.noteId
}
}
const path = "/Notes";
// Use the API module to save the note to the database
try {
const apiResponse = await API.put("NotesCRUD", path, newNote)
console.log("response from saving note: " + apiResponse);
this.setState({apiResponse});
} catch (e) {
console.log(e);
}
}
getNote 関数の追加
// noteId is the primary key of the particular record you want to fetch
async getNote() {
const path = "/Notes/object/" + this.state.noteId;
try {
const apiResponse = await API.get("NotesCRUD", path);
console.log("response from getting note: " + apiResponse);
this.setState({apiResponse});
} catch (e) {
console.log(e);
}
}
deleteNote 関数の追加
// noteId is the NoteId of the particular record you want to delete
async deleteNote() {
const path = "/Notes/object/" + this.state.noteId;
try {
const apiResponse = await API.del("NotesCRUD", path);
console.log("response from deleteing note: " + apiResponse);
this.setState({apiResponse});
} catch (e) {
console.log(e);
}
}
rennder 関数の変更(Viewの変更)
<View style={styles.container}>
<Text>Response: {this.state.apiResponse && JSON.stringify(this.state.apiResponse)}</Text>
<Button title="Save Note" onPress={this.saveNote.bind(this)} />
<Button title="Get Note" onPress={this.getNote.bind(this)} />
<Button title="Delete Note" onPress={this.deleteNote.bind(this)} />
<TextInput style={styles.textInput} autoCapitalize='none' onChangeText={this.handleChangeNoteId}/>
</View>
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
textInput: {
margin: 15,
height: 30,
width: 200,
borderWidth: 1,
color: 'green',
fontSize: 20,
backgroundColor: 'black'
}
});
作成したApp.js ソース
react-native のサンプルソースを元にしたため、一部不要なソースコードがありますがそのままにしています。
/**
* Sample React Native App
* https://github.com/facebook/react-native
* @flow
*/
import Amplify from 'aws-amplify-react-native';
import { API } from 'aws-amplify-react-native';
import { withAuthenticator } from 'aws-amplify-react-native';
import aws_exports from './aws-exports';
Amplify.configure(aws_exports);
import React, { Component } from 'react';
import {
Platform,
StyleSheet,
Text,
Button,
TextInput,
View
} from 'react-native';
const instructions = Platform.select({
ios: 'Press Cmd+R to reload,\n' +
'Cmd+D or shake for dev menu',
android: 'Double tap R on your keyboard to reload,\n' +
'Shake or press menu button for dev menu',
});
class App extends Component<{}> {
constructor(props) {
super(props);
this.state = {
apiResponse: null,
noteId: ''
};
}
handleChangeNoteId = (event) => {
this.setState({noteId: event});
}
// Create a new Note according to the columns we defined earlier
async saveNote() {
let newNote = {
body: {
"NoteTitle": "My first note!",
"NoteContent": "This is so cool!",
"NoteId": this.state.noteId
}
}
const path = "/Notes";
// Use the API module to save the note to the database
try {
const apiResponse = await API.put("NotesCRUD", path, newNote)
console.log("response from saving note: " + apiResponse);
this.setState({apiResponse});
} catch (e) {
console.log(e);
}
}
// noteId is the primary key of the particular record you want to fetch
async getNote() {
const path = "/Notes/object/" + this.state.noteId;
try {
const apiResponse = await API.get("NotesCRUD", path);
console.log("response from getting note: " + apiResponse);
this.setState({apiResponse});
} catch (e) {
console.log(e);
}
}
// noteId is the NoteId of the particular record you want to delete
async deleteNote() {
const path = "/Notes/object/" + this.state.noteId;
try {
const apiResponse = await API.del("NotesCRUD", path);
console.log("response from deleteing note: " + apiResponse);
this.setState({apiResponse});
} catch (e) {
console.log(e);
}
}
render() {
return (
<View style={styles.container}>
<Text>Response: {this.state.apiResponse && JSON.stringify(this.state.apiResponse)}</Text>
<Button title="Save Note" onPress={this.saveNote.bind(this)} />
<Button title="Get Note" onPress={this.getNote.bind(this)} />
<Button title="Delete Note" onPress={this.deleteNote.bind(this)} />
<TextInput style={styles.textInput} autoCapitalize='none' onChangeText={this.handleChangeNoteId}/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
textInput: {
margin: 15,
height: 30,
width: 200,
borderWidth: 1,
color: 'green',
fontSize: 20,
backgroundColor: 'black'
}
});
export default withAuthenticator(App);
実行
react-native run-ios
テキストボックス内に NoteID を入力して "Save Note" を押すと、保存されます。
内容はソースコードに書いてあるように固定文字が登録されます。
あくまでサンプルソースということで・・・。
AWS 確認
Mobile Hub
の Cloud Logic には下記のように登録されています。
Lambda も確認すると、NotesCRUD-xxxx という名前で登録されているのが確認できます。
DynamoDB も同様にテーブルが作成され、データも登録されているのがわかります。
通常のCRUD であればほぼノンプログラミングでバックエンドロジックが生成できますね。