All Projects → pjay79 → BarsAppAmplify

pjay79 / BarsAppAmplify

Licence: other
React Native Bars App: AWS Amplify, AWS AppSync, AWS Cognito, Google Places, Mapbox

Programming Languages

javascript
184084 projects - #8 most used programming language
ruby
36898 projects - #4 most used programming language
objective c
16641 projects - #2 most used programming language
java
68154 projects - #9 most used programming language
python
139335 projects - #7 most used programming language

Projects that are alternatives of or similar to BarsAppAmplify

Amplify Cli
The AWS Amplify CLI is a toolchain for simplifying serverless web and mobile development.
Stars: ✭ 2,399 (+8172.41%)
Mutual labels:  aws-amplify, aws-appsync
react-relay-appsync
AppSync for Relay
Stars: ✭ 19 (-34.48%)
Mutual labels:  appsync, aws-appsync
Awesome Aws Amplify
Curated list of AWS Amplify Resources
Stars: ✭ 1,650 (+5589.66%)
Mutual labels:  aws-amplify, aws-appsync
serverless-appsync-simulator
A simple wrapper around Amplify AppSync Simulator to test serverless AppSync Apis
Stars: ✭ 106 (+265.52%)
Mutual labels:  appsync, aws-appsync
Reactnativeauth
Mobile user authentication flow with React Native, Expo, and AWS Amplify: Sign In, Sign Up, Confirm Sign Up, Forget Password, Reset Password.
Stars: ✭ 108 (+272.41%)
Mutual labels:  appsync, aws-amplify
ausg-2020-handson-appsync
AWS AppSync로 만드는 서버리스 GraphQL 서비스 (ft. AWS Amplify)
Stars: ✭ 13 (-55.17%)
Mutual labels:  aws-amplify, aws-appsync
nextjs-with-aws-appsync
Demo of a working Next.js with AWS AppSync example
Stars: ✭ 31 (+6.9%)
Mutual labels:  appsync, aws-appsync
aws-reinvent-2019-mobile-workshops
AWS re:Invent 2019 Mobile Workshops
Stars: ✭ 72 (+148.28%)
Mutual labels:  aws-amplify, aws-appsync
Aws Mobile Appsync Chat Starter Angular
GraphQL starter progressive web application (PWA) with Realtime and Offline functionality using AWS AppSync
Stars: ✭ 449 (+1448.28%)
Mutual labels:  appsync, aws-amplify
react-appsync-graphql-recipe-app
Example application using React + AWS AppSync + GraphQL
Stars: ✭ 71 (+144.83%)
Mutual labels:  appsync, aws-appsync
aws-mobile-appsync-sdk-android
Android SDK for AWS AppSync.
Stars: ✭ 101 (+248.28%)
Mutual labels:  appsync, aws-appsync
Aws Amplify Workshop React
Building Serverless React Applications with AWS Amplify
Stars: ✭ 155 (+434.48%)
Mutual labels:  appsync, aws-amplify
Aws Amplify Workshop React Native
Building Cloud-enabled Mobile Applications with React Native & AWS Amplify
Stars: ✭ 124 (+327.59%)
Mutual labels:  appsync, aws-amplify
Heard
React Native Enterprise Social Messaging App
Stars: ✭ 234 (+706.9%)
Mutual labels:  appsync, aws-amplify
Mapbox Gl Directions
Directions plugin for mapbox-gl-js using Mapbox Directions API.
Stars: ✭ 154 (+431.03%)
Mutual labels:  mapbox
Maps
🌍🌏🌎 The whole world fits inside your cloud!
Stars: ✭ 253 (+772.41%)
Mutual labels:  mapbox
Uber React
Uber-like project in React Native
Stars: ✭ 151 (+420.69%)
Mutual labels:  mapbox
Airmapview
A view abstraction to provide a map user interface with various underlying map providers
Stars: ✭ 1,824 (+6189.66%)
Mutual labels:  mapbox
svelte-mapbox
MapBox Map and Autocomplete components for Svelte (or Vanilla JS)
Stars: ✭ 267 (+820.69%)
Mutual labels:  mapbox
Delaunator Cpp
A really fast C++ library for Delaunay triangulation of 2D points
Stars: ✭ 244 (+741.38%)
Mutual labels:  mapbox

BarsAppAmplify

React Native, AWS Amplify, AWS AppSync, AWS Cognito, Google Places, Mapbox. Please note: this is a work still in progress, and many features are not fully developed yet.

Update 1st Dec 2018, MapBox has been removed from this app as Google Places API terms require data to be place on Google Maps only

This app is being prepared for deployment

ToDo

  • enable offline support
  • add pagination

Folder structure:

folder1-small folder2-small

Screenshots

iOS

img_3560 img_3561 img_3562 img_3563 img_3644 img_3616 img_3617 img_3596 img_3598 img_3599 img_3600 img_3601 img_3602 img_3603 img_3604 img_3606 img_3607 img_3608

Technology stack:

  • @mapbox/react-native-mapbox-gl
  • aws-amplify
  • aws-amplify-react-native
  • aws-appsync
  • aws-appsync-react
  • aws-sdk
  • axios
  • babel-plugin-transform-remove-console
  • geolib
  • graphql-tag
  • lodash
  • moment
  • react-apollo
  • react-native-app-intro-slider
  • react-native-collapsible
  • react-native-config
  • react-native-elements
  • react-native-geolocation-service
  • react-native-keyboard-aware-scroll-view"
  • react-native-map-link
  • react-native-modal
  • react-native-splash-screen
  • react-native-swipeout
  • react-native-vector-icons
  • react-navigation
  • uuid

Installation

React Native setup:

Install Node.js:
https://nodejs.org/en/download/

brew install watchman
npm install -g react-native-cli

And also install Xcode for iOS simulator + Android Studio / Genymotion for Android simulator. Preferably connect up a hardware device for this particular app to access geolocation, maps + directions, and phone connection.

Project setup:

Clone the repo: git clone https://github.com/pjay79/BarsAppAmplify.git
Change to the project folder: cd BarsAppAmplify
Add dependencies: npm install or yarn

Amazon

Sign up to AWS Free Tier:
https://aws.amazon.com/free/

AWS Amplify CLI setup

npm install -g @aws-amplify/cli

amplify configure

This command will direct you to create a new IAM user, when prompted enter the accessKeyId and secretAccessKey, store these in a safe place, you can also assign this user an AWS Profile Name:

amplify-cropped

amplify init (in the project folder)

amplify-init-cropped

amplify add auth (update: I have now set MFA to optional to get the password reset functionality working)

amplify-auth

amplify add api

amplify-api-setup

The base schema.graphql file looks like this:

type Bar @model {
	id: ID!
	title: String!
	content: String!
	price: Int
	rating: Float
}

This app will have a many-to-many connection between type Bar and type User. Currently AWS Amplify does not yet support many-to-many connections, hence the @connection directive which is used for specifying relationships between @model object types cannot be used. Update the schema.graphql file to look as follows.

type Bar @model {
  id: ID!
  createdAt: String
  updatedAt: String
  name: String!
  phone: String
  location: String
  lat: String
  lng: String
  url: AWSURL
  addedBy: ID!
  users(first: Int, after: String): [Bar]
}

type BarMember @model {
  id: ID!
  createdAt: String
  updatedAt: String
  userId: ID!
  barId: ID!
}

type User @model {
  id: ID!
  createdAt: String
  updatedAt: String
  username: String!
  bars(first: Int, after: String): [Bar]
}

Important Step

amplify push

This command will update your cloud resources and add an aws-exports.js file to your project root directory. In your App.js file make sure this file is imported from the correct location.

Other directives

Note: AWS Amplify has has the following directives that can be used with AppSync:

@model: Used for storing types in Amazon DynamoDB.

@connection: Used to define different authorization strategies.

@auth: Used for specifying relationships between @model object types.

@searchable: Used for streaming the data of an @model object type to Amazon ElasticSearch Service.

AWS AppSync Codegen

AWS Amplify can generate types, as well as query, mutation, and subscription files based on your schema. In this project you will not need to do this as the relevant files have already been created in this repository. See the video below for an example:

https://www.youtube.com/watch?v=r0PbwDoNMcY

AWS AppSync Schema

Go to the AWS Console and AWS AppSync under Services. Select the API that has been generated API for this app and go to the schema.

schema

The schema that has been created needs some modification to allow for the many-to-many relationship between Bars and Users to work. Modify the schema as follows:

type Bar {
	id: ID!
	createdAt: String
	updatedAt: String
	name: String!
	phone: String
	location: String
	lat: String
	lng: String
	url: AWSURL
	website: AWSURL
	addedBy: ID!
	users(first: Int, after: String): BarUsersConnection
}

type BarMember {
	id: ID
	createdAt: String
	updatedAt: String
	userId: ID!
	barId: ID!
}

type BarUsersConnection {
	items: [User]
	nextToken: String
}

input CreateBarInput {
	id: ID!
	name: String!
	phone: String
	location: String
	lat: String
	lng: String
	url: AWSURL
	website: AWSURL
	addedBy: ID!
}

input CreateBarMemberInput {
	userId: ID!
	barId: ID!
}

input CreateUserInput {
	id: ID!
	username: String!
}

input DeleteBarInput {
	id: ID
}

input DeleteBarMemberInput {
	id: ID
}

input DeleteUserInput {
	id: ID
}

type ModelBarConnection {
	items: [Bar]
	nextToken: String
}

input ModelBarFilterInput {
	id: ModelIDFilterInput
	createdAt: ModelStringFilterInput
	name: ModelStringFilterInput
	phone: ModelStringFilterInput
	location: ModelStringFilterInput
	lat: ModelStringFilterInput
	lng: ModelStringFilterInput
	url: ModelStringFilterInput
	website: ModelStringFilterInput
	addedBy: ModelIDFilterInput
	and: [ModelBarFilterInput]
	or: [ModelBarFilterInput]
	not: ModelBarFilterInput
}

type ModelBarMemberConnection {
	items: [BarMember]
	nextToken: String
}

input ModelBarMemberFilterInput {
	id: ModelIDFilterInput
	createdAt: ModelStringFilterInput
	userId: ModelIDFilterInput
	barId: ModelIDFilterInput
	and: [ModelBarMemberFilterInput]
	or: [ModelBarMemberFilterInput]
	not: ModelBarMemberFilterInput
}

input ModelBooleanFilterInput {
	ne: Boolean
	eq: Boolean
}

input ModelFloatFilterInput {
	ne: Float
	eq: Float
	le: Float
	lt: Float
	ge: Float
	gt: Float
	contains: Float
	notContains: Float
	between: [Float]
}

input ModelIDFilterInput {
	ne: ID
	eq: ID
	le: ID
	lt: ID
	ge: ID
	gt: ID
	contains: ID
	notContains: ID
	between: [ID]
	beginsWith: ID
}

input ModelIntFilterInput {
	ne: Int
	eq: Int
	le: Int
	lt: Int
	ge: Int
	gt: Int
	contains: Int
	notContains: Int
	between: [Int]
}

enum ModelSortDirection {
	ASC
	DESC
}

input ModelStringFilterInput {
	ne: String
	eq: String
	le: String
	lt: String
	ge: String
	gt: String
	contains: String
	notContains: String
	between: [String]
	beginsWith: String
}

type ModelUserConnection {
	items: [User]
	nextToken: String
}

input ModelUserFilterInput {
	id: ModelIDFilterInput
	createdAt: ModelStringFilterInput
	username: ModelStringFilterInput
	and: [ModelUserFilterInput]
	or: [ModelUserFilterInput]
	not: ModelUserFilterInput
}

type Mutation {
	createBar(input: CreateBarInput!): Bar
	updateBar(input: UpdateBarInput!): Bar
	deleteBar(input: DeleteBarInput!): Bar
	createBarMember(input: CreateBarMemberInput!): BarMember
	updateBarMember(input: UpdateBarMemberInput!): BarMember
	deleteBarMember(input: DeleteBarMemberInput!): BarMember
	createUser(input: CreateUserInput!): User
	updateUser(input: UpdateUserInput!): User
	deleteUser(input: DeleteUserInput!): User
}

type Query {
	getBar(id: ID!): Bar
	listBars(filter: ModelBarFilterInput, limit: Int, nextToken: String): ModelBarConnection
	getBarMember(userId: ID!, barId: ID!): BarMember
	listBarMembers(filter: ModelBarMemberFilterInput, limit: Int, nextToken: String): ModelBarMemberConnection
	getUser(id: ID!): User
	listUsers(filter: ModelUserFilterInput, limit: Int, nextToken: String): ModelUserConnection
}

type Subscription {
	onCreateBar: Bar
		@aws_subscribe(mutations: ["createBar"])
	onUpdateBar: Bar
		@aws_subscribe(mutations: ["updateBar"])
	onDeleteBar: Bar
		@aws_subscribe(mutations: ["deleteBar"])
	onCreateBarMember: BarMember
		@aws_subscribe(mutations: ["createBarMember"])
	onUpdateBarMember: BarMember
		@aws_subscribe(mutations: ["updateBarMember"])
	onDeleteBarMember: BarMember
		@aws_subscribe(mutations: ["deleteBarMember"])
	onCreateUser: User
		@aws_subscribe(mutations: ["createUser"])
	onUpdateUser: User
		@aws_subscribe(mutations: ["updateUser"])
	onDeleteUser: User
		@aws_subscribe(mutations: ["deleteUser"])
}

input UpdateBarInput {
	id: ID!
	name: String
	phone: String
	location: String
	lat: String
	lng: String
	url: AWSURL
	website: AWSURL
}

input UpdateBarMemberInput {
	id: ID!
	userId: ID
	barId: ID
}

input UpdateUserInput {
	id: ID!
	username: String
}

type User {
	id: ID!
	createdAt: String
	updatedAt: String
	username: String!
	bars(first: Int, after: String): UserBarsConnection
}

type UserBarsConnection {
	items: [Bar]
	nextToken: String
}

AWS AppSync Resolvers

Resolver for Bar.users: BarMemberTable

## Request

{
    "version" : "2017-02-28",
    "operation" : "Query",
    "query" : {
        "expression": "barId = :id",
        "expressionValues" : {
            ":id" : {
                "S" : "${ctx.source.id}"
            }
        }
    },
    "index": "barId-index",
    "limit": $util.defaultIfNull(${ctx.args.first}, 20),
    "nextToken": $util.toJson($util.defaultIfNullOrBlank($ctx.args.after, null))
}
## Response

{
    "items": $util.toJson($ctx.result.items),
    "nextToken": $util.toJson($util.defaultIfNullOrBlank($context.result.nextToken, null))
}

Resolver for BarUsersConnection.items: UserTable

## Request
## Please remember to replace the hyphenated table name below with the one that was created for your app

#set($ids = [])
#foreach($user in ${ctx.source.items})
    #set($map = {})
    $util.qr($map.put("id", $util.dynamodb.toString($user.get("userId"))))
    $util.qr($ids.add($map))
#end

{
    "version" : "2018-05-29",
    "operation" : "BatchGetItem",
    "tables" : {
        "User-rndmxxybyjfv5lvzou3767zbte": {
               "keys": $util.toJson($ids),
               "consistentRead": true
       }
    }
}
## Response
## Please remember to replace the hyphenated table name below with the one that was created for your app

#if( ! ${ctx.result.data} )
  $util.toJson([])
#else
  $util.toJson($ctx.result.data.User-uq7n63nywrc4tku2tzgx4mx75u)
#end

Resolver for User.bars: BarMemberTable

## Request

{
    "version" : "2017-02-28",
    "operation" : "Query",
    "query" : {
        "expression": "userId = :id",
        "expressionValues" : {
            ":id" : {
                "S" : "${ctx.source.id}"
            }
        }
    },
    "index": "userId-index",
    "limit": $util.defaultIfNull(${ctx.args.first}, 20),
    "nextToken": $util.toJson($util.defaultIfNullOrBlank($ctx.args.after, null))
}
## Response

{
    "items": $util.toJson($ctx.result.items),
    "nextToken": $util.toJson($util.defaultIfNullOrBlank($context.result.nextToken, null))
}

Resolver for UserBarsConnection.items: BarTable

## Request
## Please remember to replace the hyphenated table name below with the one that was created for your app


#set($ids = [])
#foreach($bar in ${ctx.source.items})
    #set($map = {})
    $util.qr($map.put("id", $util.dynamodb.toString($bar.get("barId"))))
    $util.qr($ids.add($map))
#end

{
    "version" : "2018-05-29",
    "operation" : "BatchGetItem",
    "tables" : {
        "Bar-rndmxxybyjfv5lvzou3767zbte": {
               "keys": $util.toJson($ids),
               "consistentRead": true
       }
    }
}
## Response
## Please remember to replace the hyphenated table name below with the one that was created for your app

#if( ! ${ctx.result.data} )
  $util.toJson([])
#else
  $util.toJson($ctx.result.data.Bar-uq7n63nywrc4tku2tzgx4mx75u)
#end

Resolver for Query.getBarMember: BarMember

## Request

{
    "version" : "2017-02-28",
    "operation" : "Query",
    "index" : "userId-index",
    "query" : {
        ## Provide a query expression. **
        "expression": "userId = :userId",
        "expressionValues" : {
            ":userId" : $util.dynamodb.toDynamoDBJson($ctx.args.userId)
        }
    },
    "filter" : {
    	"expression" : "barId = :barId",
        "expressionValues" : {
            ":barId" : $util.dynamodb.toDynamoDBJson($ctx.args.barId)
        }
    },
}
## Response

#if($ctx.result.items.size() > 0)
  $util.toJson($ctx.result.items[0])
#else
  null
#end

Resolver for Mutation.createBar: BarTable

Update the key only, leave the rest as it is.

"key": {
  "id": $util.dynamodb.toDynamoDBJson($ctx.args.input.id),
},

Resolver for Mutation.createUser: UserTable

Update the key only, leave the rest as it is.

"key": {
  "id": $util.dynamodb.toDynamoDBJson($ctx.args.input.id),
},

DynamoDB

From the AWS AppSync console select Data Sources and find the BarMember table. Create 2 indexes for this table, barId-index, and userId-index, with no sort keys and default settings. See example below:

create-index

Google Places API

Sign up to Google Places and get an API key.

google-places

Mapbox API

Sign up to Mapbox and get an API key.

mapbox

Add API keys

This project uses react-native-config to store API keys in an environment file. Creata a .env file in the project root directory, then add your Google Places API and Mapbox API keys here.

GOOGLE_PLACES_API_KEY=YOUR_KEY_GOES_HERE
MAPBOX_ACCESS_TOKEN=YOUR_KEY_GOES_HERE

Launch

Run on ios device:
react-native run-ios --device "iPhone X"

Run on android device:
adb devices
react-native run-android --deviceId "myDeviceId"

Run on ios: react-native run-ios
Run on android: react-native run-android

If you are getting build errors try the following:

  • rebuild
  • delete app from simulator or device and rebuild
  • erase all content and settings from simulator and rebuild
  • clean build folder in xcode and rebuild
  • rm -rf ~/.rncache
  • rm -rf node_modules && rm -rf ~/.rncache && yarn

If you are getting any yellow box warnings when entering text into the SearchBar, disable remote debugging.

Flow

I am in the process of migrating from PropTypes to using Flow. To check for Flow errors:

yarn run flow start
yarn run flow status

Testing with Jest and Enzyme

I have started adding tests using Jest and Enzyme. To check the current tests are working:

yarn run test

Additional information

React Apollo

In this app I have chosen to primarily use React Apollo's graphql higher order component to connect queries, mutations, and subscriptions to the app. With React Apollo 2.1 you can use the new Query, Mutation, and Subscription components instead.

AWS Amplify API

In the Auth section of this app I have used AWS Amplify's API and graphqlOperation helper. This API is effectively an alternative GraphQL client for working with queries, mutations, and subscriptions. It is great to use when you do not need offline support and the more advanced features of React Apollo.

AWS Appsync

With AWS AppSync you can combine React Apollo's graphql higher order component with the graphqlMutation (offline support) and buildSubscription helpers. These take away some of the boilerplate code normally required to implement mutations and subscriptions. I have used the buildSubscription helper in this app.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].