As part of the #AmplifyHashnode hackathon, I've decided to build a restaurant review app that leverages React-Native and Amplify. This is day 2 of my journey. A summary of what was accomplished today:
- Initialize app with Expo
- Setup Git repo
- Initialize with Amplify
- Add authentication
- Set up continuous deployment and dev environment
- Create GraphQL schema that leverages Amplify directives
I'll also link some video sources that I'll upload to youtube that include a live coding session. Here's a rough breakdown from the terminal.
Step 1:
$: expo init restaurant-review-app
> blank (a minimal app...)
$: cd restaurant-review-app
Step 2:
$: git init
$: git add .
$: git commit -m "Initial commit"
$: git push
Step 3:
$: amplify init
? Enter a name for the project: restaurantreviewapp
? Enter a name for the environment: dev
? Choose your default editor: Visual Studio Code
? Choose the type of app that you're building: javascript
? What javascript framework are you using: react-native
? Source Directory Path: /
? Distribution Directory Path: /
? Build Command: npm run-script build
? Start Command: npm run-scrip-start
? Please choose the profile you want to use
? Choose environment: dev
Step 4:
Link to video for details but quick breakdown:$: amplify add auth
- Choose Manual Configuration
- Choose AWS IAM for signup
- Allow unauthenticated logins
- Signup with email and phone number
- Choose optional MFA Configuration
- Required Attributes for signup: email and phone_number
I made other adjustments which you can see in the video. This will also get it's own dedicated article after the hackathon.
Step 5:
Link to video for details but quick breakdown: Amplify does everything for you, lol it's really just me getting excited that they have an automatic integration with Expo's React-Native for web for front end deployment ;)Step 6:
Link to video for details. There's a lot going on here that I will breakdown into it's own article in the future, but for the time being, I'll layout the schema here and I recommend the video for getting an idea of how I arrived there.type GPS {
lon: Float
lat: Float
}
type Establishment
@model
@auth(rules: [{ allow: public, provider: iam }])
@key(fields: ["ownerId", "id"]) {
id: ID!
ownerId: ID!
owner: User
name: String!
gps: GPS
street: String
city: String
state: String
zipcode: String
neighborhood: String
phone: String
email: String
placeID: String!
phoneNumber: String
likeCount: Int
reviews: [Review]
userLikes: [EstablishmentLike] @connection(fields: ["id"])
}
type EstablishmentLike
@model(subscriptions: { level: public })
@auth(rules: [{ allow: public, provider: iam }])
@key(fields: ["establishmentId", "createdAt"])
@key(
name: "establishmentLikesByUser"
fields: ["userId", "createdAt"]
queryField: "getEstablishmentLikesByUser"
) {
establishmentId: ID!
userId: ID!
@auth(rules: [{ allow: owner, ownerField: "userId", identityClaim: "sub" }])
createdAt: AWSTimestamp!
establishment: Establishment @connection(fields: ["establishmentId"])
user: User @connection(fields: ["userId"])
establishmentName: String
}
type CheckIn
@model(subscriptions: { level: public })
@auth(rules: [{ allow: public, provider: iam }])
@key(fields: ["establishmentId", "createdAt"])
@key(
name: "userCheckins"
fields: ["userId", "createdAt"]
queryField: "getUserCheckins"
) {
establishmentId: ID!
userId: ID!
@auth(rules: [{ allow: owner, ownerField: "userId", identityClaim: "sub" }])
createdAt: AWSTimestamp!
establishmentName: String
establishment: Establishment @connection(fields: ["establishmentId"])
user: User @connection(fields: ["userId"])
}
type EstablishmentFollow
@model
@key(fields: ["establishmentId", "userId"])
@key(
name: "establishmentsFollowedByUser"
fields: ["userId", "establishmentId"]
queryField: "getEstablishmentsFollowedByUser"
) {
establishmentId: ID!
userId: ID! # user.id
establishment: Establishment @connection(fields: ["establishmentId"])
user: User @connection(fields: ["userId"])
}
type Review
@model(subscriptions: { level: public })
@auth(rules: [{ allow: public, provider: iam }])
@key(fields: ["establishmentId", "createdAt"])
@key(
name: "userCheckins"
fields: ["userId", "createdAt"]
queryField: "getUserCheckins"
) {
establishmentId: ID!
userId: ID!
@auth(rules: [{ allow: owner, ownerField: "userId", identityClaim: "sub" }])
createdAt: AWSTimestamp!
establishmentName: String
comments: String
image: [String]
rating: Int
establishment: Establishment @connection(fields: ["establishmentId"])
user: User @connection(fields: ["userId"])
}
type User
@model(mutations: { update: "updateUser", delete: "deleteUser" })
@auth(
rules: [
{
allow: owner
ownerField: "id"
identityClaim: "sub"
operations: [update, delete, read]
}
]
)
@key(fields: ["pk_id", "id"]) {
pk_id: ID!
id: ID!
firstName: String
lastName: String
userName: String
birthdate: String
gender: String
description: String
ownerOf: [Establishment]
createdAt: AWSDateTime
lastUpdated: AWSDateTime
profilePicture: String
followers: [UserFollow] @connection(keyName: "followers", fields: ["id"])
followed: [UserFollow] @connection(fields: ["id"])
establishmentLikes: [EstablishmentLike]
@connection(keyName: "establishmentLikesByUser", fields: ["id"])
}
type UserFollow
@model
@key(fields: ["id", "userFollowedId"])
@key(
name: "followers"
fields: ["userFollowedId", "id"]
queryField: "getFollowers"
) {
id: ID! # user.id
userFollower: User @connection(fields: ["id"])
userFollowedId: ID! # user.id I'm following
userFollowed: User @connection(fields: ["userFollowedId"])
}
enum UserType {
consumer
owner
}
Day 1 in the books and we've already set up Authentication, an API with all our queries, mutations and subscriptions connected to DynamoDB and our CI/CD pipeline. Here's the kicker, it works on iOS, Android, and Web, schwaaaaat 🤯 🤯 🤯. All praise to Amplify, Expo, & React-Native, y'all the real MVPs.
Github repo for day one can be found here
Check me out on twitter @andthensumm and follow the journey