WITH
Build a real-time PWA

AND


Ouadie LAHDIOUI
WHO ARE WE?


IT consultant @
CHIHAB OTMANI
Software Engineer | Trainer
Node.js Foundation Member
Co-Founder @
@lahdiouiouadie
@chihabotmani


Rabat.js

WHO ARE you ?
👨🎨 Front-End developers ?
👨💻 Back-End developers ?
🦸♂️ Something else ? something in between ?
☁️ Cloud developers ?
- Architecture
- Core components
- Code samples
- Goodies!
WHAT WE'RE GONNA SEE
Text
Architecture
Text

service
REST API
SOAP API

db



A PWA IS A WEB APP THAT USES MODERN WEB CAPABILITIES TO DELIVER AN
APP-LIKE USER EXPERIENCE.
WHY Not A NATIVE APP? 🤔
AVERAGE OF INSTALLED APP PER MONTH
0
NATIVE APPS
USER EXPERIENCE | DEVELOPER EXPERIENCE |
---|---|
Installable | Hard to multiplatform |
Available Offline | Ineffective workstyle |
Engaging | Store approval |
Download required | Expensive to develop |
Updates required | |
No Google | |
No Multiplatform |
HOW ABOUT A WEB APP? 🤔
WEB APPS
USER EXPERIENCE | DEVELOPER EXPERIENCE |
---|---|
No install / updates | Web technologies |
Works on all devices | Code Once Run Everywhere |
Known by search engines | Huge ecosystem |
No offline Support | 'Cheap' to develop |
Not installable | Secured over TLS/SSL layer |
Not engaging | |
NATIVE APPS UX
PROGESSIVE WEB APPS
WEB APPS DX
PROGREsSIVE WEB APPS
USER EXPERIENCE | DEVELOPER EXPERIENCE |
---|---|
Offline Support | Web technologies |
Engaging | Code Once Run Everywhere |
Installable | Huge ecosystem |
No install / updates | 'Cheap' to develop |
Works on all devices | Secured over TLS/SSL layer |
Known by search engines | |
With a home screen icon
With Offline Support
Receives notifications
A PWA is a Web app
Secured over HTTPS
HOME SCREEN ICON

WEB MANIFEST
WEB MANIFEST
{
"short_name": "Twitter",
"name": "Twitter App",
"icons": [
{
"src": "/images/icons-192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "/images/icons-512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": "/?source=pwa",
"background_color": "#3367D6",
"display": "standalone",
"scope": "/",
"theme_color": "#3367D6"
}
<link rel="manifest" href="/manifest.json">
manifest.json
index.html
OFFLINE SUPPORT
AVAILABLE OFFLINE
Install/update application
Launch application while offline
SERVICE WORKER
A Service Worker is a script that the browser runs in the background.
It acts as a proxy between the application and the network.
AVAILABLE OFFLINE
No access to sync storage
Promise-based
Requires HTTPS

Service worker - REGISTRATION
if ('serviceWorker' in navigator) { navigator.serviceWorker.register('./sw-test/sw.js') .then((reg) => { // registration worked console.log('Registration succeeded. Scope is ' + reg.scope); }).catch((error) => { // registration failed console.log('Registration failed with ' + error); }); }
if ('serviceWorker' in navigator) { navigator.serviceWorker.register('./sw-test/sw.js') .then((reg) => { // registration worked console.log('Registration succeeded. Scope is ' + reg.scope); }).catch((error) => { // registration failed console.log('Registration failed with ' + error); }); }
main.js
Service worker - INSTALL
self.addEventListener('install', (event) => { event.waitUntil( caches.open('v1').then((cache) => { return cache.addAll([ '/sw-test/index.html', '/sw-test/style.css', '/sw-test/app.js', '/sw-test/logo.jpg', ]); }) ); });
self.addEventListener('install', (event) => { event.waitUntil( caches.open('v1').then((cache) => { return cache.addAll([ '/sw-test/index.html', '/sw-test/style.css', '/sw-test/app.js', '/sw-test/logo.jpg', ]); }) ); });
Service worker - FETCH
self.addEventListener('fetch', (event) => { event.respondWith( caches.match(event.request).then((response) => { return response || fetch(event.request); }) ); });
self.addEventListener('fetch', (event) => { event.respondWith( caches.match(event.request).then((response) => { return response || fetch(event.request); }) ); });
Service worker - PUSH
self.addEventListener('push', function(event) { const title = 'PWA man!'; const options = { body: 'Hey Devoxxians!', icon: 'images/devoxx.png' }; event.waitUntil(self.registration.showNotification(title, options)); });
DO I HAVE TO DO ALL OF THAT?


@angular/PWA SCHEMATICS
ng add @angular/pwa
Installed packages for tooling via yarn. CREATE ngsw-config.json (585 bytes) CREATE src/manifest.webmanifest (1063 bytes) CREATE src/assets/icons/icon-128x128.png (1253 bytes) CREATE src/assets/icons/icon-144x144.png (1394 bytes) CREATE src/assets/icons/icon-152x152.png (1427 bytes) CREATE src/assets/icons/icon-192x192.png (1790 bytes) CREATE src/assets/icons/icon-384x384.png (3557 bytes) CREATE src/assets/icons/icon-512x512.png (5008 bytes) CREATE src/assets/icons/icon-72x72.png (792 bytes) CREATE src/assets/icons/icon-96x96.png (958 bytes) UPDATE angular.json (3574 bytes) UPDATE package.json (1350 bytes) UPDATE src/app/app.module.ts (604 bytes)
Installed packages for tooling via yarn. CREATE ngsw-config.json (585 bytes) CREATE src/manifest.webmanifest (1063 bytes) CREATE src/assets/icons/icon-128x128.png (1253 bytes) CREATE src/assets/icons/icon-144x144.png (1394 bytes) CREATE src/assets/icons/icon-152x152.png (1427 bytes) CREATE src/assets/icons/icon-192x192.png (1790 bytes) CREATE src/assets/icons/icon-384x384.png (3557 bytes) CREATE src/assets/icons/icon-512x512.png (5008 bytes) CREATE src/assets/icons/icon-72x72.png (792 bytes) CREATE src/assets/icons/icon-96x96.png (958 bytes) UPDATE angular.json (3574 bytes) UPDATE package.json (1350 bytes) UPDATE src/app/app.module.ts (604 bytes)
SPA + ng add @angular/pwa = PWA
ANGULAR - WEB MANIFEST
{
"name": "my-pwa",
"short_name": "pwa",
"theme_color": "#1976d2",
"background_color": "#fafafa",
"display": "standalone",
"scope": "/",
"start_url": "/",
"icons": [
{
"src": "assets/icons/icon-72x72.png",
"sizes": "72x72",
"type": "image/png"
},
{
"src": "assets/icons/icon-96x96.png",
"sizes": "96x96",
"type": "image/png"
}
...
]
}



SERVICE WORKERS
self.addEventListener('install', (event) => { event.waitUntil( caches.open('v1').then((cache) => { return cache.addAll([ '/sw-test/index.html', '/sw-test/style.css', '/sw-test/app.js', '/sw-test/logo.jpg', ]); }) ); });
@angular/PWA SCHEMATIC
ng add @angular/pwa
Installed packages for tooling via yarn. CREATE ngsw-config.json (585 bytes) CREATE src/manifest.webmanifest (1063 bytes) CREATE src/assets/icons/icon-128x128.png (1253 bytes) CREATE src/assets/icons/icon-144x144.png (1394 bytes) CREATE src/assets/icons/icon-152x152.png (1427 bytes) CREATE src/assets/icons/icon-192x192.png (1790 bytes) CREATE src/assets/icons/icon-384x384.png (3557 bytes) CREATE src/assets/icons/icon-512x512.png (5008 bytes) CREATE src/assets/icons/icon-72x72.png (792 bytes) CREATE src/assets/icons/icon-96x96.png (958 bytes) UPDATE angular.json (3574 bytes) UPDATE package.json (1350 bytes) UPDATE src/app/app.module.ts (604 bytes)
Installed packages for tooling via yarn. CREATE ngsw-config.json (585 bytes) CREATE src/manifest.webmanifest (1063 bytes) CREATE src/assets/icons/icon-128x128.png (1253 bytes) CREATE src/assets/icons/icon-144x144.png (1394 bytes) CREATE src/assets/icons/icon-152x152.png (1427 bytes) CREATE src/assets/icons/icon-192x192.png (1790 bytes) CREATE src/assets/icons/icon-384x384.png (3557 bytes) CREATE src/assets/icons/icon-512x512.png (5008 bytes) CREATE src/assets/icons/icon-72x72.png (792 bytes) CREATE src/assets/icons/icon-96x96.png (958 bytes) UPDATE angular.json (3574 bytes) UPDATE package.json (1350 bytes) UPDATE src/app/app.module.ts (604 bytes)
ANGULAR - SERVICE WORKER
{ "$schema": "./node_modules/@angular/service-worker/config/schema.json", "index": "/index.html", "assetGroups": [ { "name": "app", "installMode": "prefetch", "resources": { "files": [ "/favicon.ico", "/index.html", "/*.css", "/*.js" ] } }, { "name": "assets", "installMode": "lazy", "updateMode": "prefetch", "resources": { "files": [ "/assets/**", "/*.(eot|svg|cur|jpg|png|webp|gif|otf|ttf|woff|woff2|ani)" ] } } ] }
ngsw-config.json
"installMode": "prefetch",
"files": [
"/favicon.ico",
"/index.html",
"/*.css",
"/*.js"
]
ANGULAR - SERVICE WORKER
ng build --prod
{
"configVersion": 1,
"timestamp": 1572297590111,
"index": "/index.html",
"assetGroups": [
{
"name": "app",
"installMode": "prefetch",
"updateMode": "prefetch",
"urls": [
"/index.html",
"/main-es5.55c7be03cd19bb680a35.js",
"/polyfills-es5.98f7268495936fc0a233.js",
"/runtime-es5.d3647fbfa3de00cd0bdf.js",
"/styles.3ff695c00d717f2d2a11.css"
...
]
}
]
...
}
ngsw-config.json
ngsw-worker.js + ngsw.json
ANGULAR - SERVICE WORKER
@NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule, AppRoutingModule, ServiceWorkerModule.register( 'ngsw-worker.js', { enabled: environment.production } ) ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
app.module.ts


ANGULAR - SWUPDATE
@Injectable() export class updateService { constructor(updates: SwUpdate, toastr: ToastrService) { updates.available.subscribe(event => { toastr.refresh('A new version is available'); }); } }
@Injectable() export class updateService { constructor(updates: SwUpdate, toastr: ToastrService) { updates.available.subscribe(event => { toastr.refresh('A new version is available'); }); } }

ANGULAR - SWPUSH
constructor(private swPush: SwPush) {} subscribeToNotification() { this.swPush.requestSubscription({ serverPublicKey: this.VAPID_PUBLIC_KEY }) .then(sub => this.sendSubcriptionToBackend(sub)) } handleNotification() { this.swPush.notificationClicks.subscribe( event => { const dataFromBackend = event.notification.data; doSemthing(dataFromBackend); }); }
constructor(private swPush: SwPush) {} subscribeToNotification() { this.swPush.requestSubscription({ serverPublicKey: this.VAPID_PUBLIC_KEY }) .then(sub => this.sendSubcriptionToBackend(sub)) } handleNotification() { this.swPush.notificationClicks.subscribe( event => { const dataFromBackend = event.notification.data; doSemthing(dataFromBackend); }); }
constructor(private swPush: SwPush) {} subscribeToNotification() { this.swPush.requestSubscription({ serverPublicKey: this.VAPID_PUBLIC_KEY }) .then(sub => this.sendSubcriptionToBackend(sub)) } handleNotification() { this.swPush.notificationClicks.subscribe( event => { const dataFromBackend = event.notification.data; doSemthing(dataFromBackend); }); }

Progressive Web Apps ON PRODUCTION

ALIBABA
UBER

progressivewebapproom.com



AIRBNB
HOW TO GET DATA? 🤔


recommendation
APP become more complex 😨
invest
Sale
...
AI

Bot
IOT
...
service
service
service
service

🎉 DATA GRAPH LAYER 🎉

Bot
IOT
...
service
service
service
service
data GRAPH
APPs describe their requirement 🗣
services describe their capabilities 💪

🙅♂️ GraphQL is not a FramEwork
A query language for your API
And a server-side runtime for fulfilling those queries with your existing data

service
REST API
SOAP API
service

request
POST /graphQL
RESPONSE
{
pikatchu: pokemon(id: "1") {
name
type
}
}
{
"data": {
"pokemon": {
"name": "Pikachu",
"type": "Electric"
}
}
}
db

GRAPHQL INTEREST OVER TIME
*source: Google search envolution
Graphql key concepts
*some of them
types & fields
TO DESCRIBE YOUR DATA

POKEMON {
}
NAME
TYPE
HEALTH
THIS IS A TYPE
these are fields
NAME
TYPE
HEALTH
Fields have functions (resolvers)
function pokemon_name(pokemon) {
return pokemon.getName();
}
function pokemon_type(pokemon) {
return pokemon.getType();
}
function pokemon_healthname(pokemon) {
return pokemon.getHealth();
}
to get data frOm server
YOU SHOULD USE QUERIES
{
pikatchu: pokemon(id: "1") {
name
type
}
}
{
"data": {
"pokemon": {
"name": "Pikachu",
"type": "Electric"
}
}
}

GRAPHQL SERVER
REQUEST
RESPONSE

THIS IS an argument
THIS IS an alias
query BestPokemon {
pikatchu: pokemon(id: "1") {
name
type
}
}
To modify server-side data
YOU NEED TO USE MUTATIONS
mutation CreatePokemonMutation() {
createPokemon(name: "Pikachu") {
id
type
}
}
{
"data": {
"pokemon": {
"id" : 1
"name": "Pikachu",
"type": "Electric"
}
}
}

GRAPHQL SERVER
REQUEST
RESPONSE

TO PUSH DATA FROM SERVER TO CLIENTS
GRAPHQL SPEC SUPPORTS SUBSCRIPTIONS

Bot
IOT
service
service
service
service
data GRAPH

subscription onPokemonCreateSubscription(){
onPokemonCreate(){
id
content
}
}
subscription onPokemonCreateSubscription(){
onPokemonCreate(){
id
content
}
}
subscription onPokemonCreateSubscription(){
onPokemonCreate(){
id
content
}
}
Defining a Schema
type Query {
allPokemons(last: Int): [Pokemon!]!
}
type Query { ... }
type Mutation { ... }
type Subscription { ... }
type Mutation {
createPokemons(name: String!, type: String!): Pokemon!
}
type Subscription {
newPokemon: Pokemon!
}
special root types
And a server-side runtime for fulfilling those queries with your existing data
GraphQL is served over HTTP
VIA A SINGLE ENDPOINT


who's the best 🤔
GRAPHQL vs OTHER API Styles
Both GRAPHQL vs REST vs SOAP “movements” are clearly fueled by unhappy, overlooked and over-served API consumers

colonial architecture

CHIMNEY
gable roof
colonial houses
had colonial constraints
Listen to your constraints
I really like colonial houses
I think i'll build one ... 😍
I really like colonial houses
REST APIS
I think i'll build one ... 😨
I really like colonial houses
GRAPHQL APIS
I think i'll build one ... 😰
TWO types of constraints
API Product constraints
API Style constraints
+



APOLLO CLIENT
Apollo Client is a Feature-rich GraphQL client that manages data and state in an application.
APOLLO CACHE
Apollo Cache is a Reactive store for your GraphQL requests
APOLLO LINK
A sort of middleware for your GraphQL Requests
APOLLO TOOLS

APOLLO ANGULAR

APOLLO ANGULAR - Schematics
ng add apollo-graphql CREATE src/app/graphql.module.ts (628 bytes) UPDATE package.json (1728 bytes) UPDATE tsconfig.json (572 bytes) UPDATE src/app/app.module.ts (1702 bytes)
ng add apollo-graphql CREATE src/app/graphql.module.ts (628 bytes) UPDATE package.json (1728 bytes) UPDATE tsconfig.json (572 bytes) UPDATE src/app/app.module.ts (1702 bytes)
apollo ANGULAR - GRAPHQL MODULE
const uri = 'https://myapp.io/graphql'; export function createApollo(httpLink: HttpLink) { return { link: httpLink.create({uri}), cache: new InMemoryCache(), }; } @NgModule({ exports: [ApolloModule, HttpLinkModule], providers: [ { provide: APOLLO_OPTIONS, useFactory: createApollo, deps: [HttpLink], }, ], }) export class GraphQLModule {}
const uri = 'https://myapp.io/graphql'; export function createApollo(httpLink: HttpLink) { return { link: httpLink.create({uri}), cache: new InMemoryCache(), }; } @NgModule({ exports: [ApolloModule, HttpLinkModule], providers: [ { provide: APOLLO_OPTIONS, useFactory: createApollo, deps: [HttpLink], }, ], }) export class GraphQLModule {}
const uri = 'https://myapp.io/graphql'; export function createApollo(httpLink: HttpLink) { return { link: httpLink.create({uri}), cache: new InMemoryCache(), }; } @NgModule({ exports: [ApolloModule, HttpLinkModule], providers: [ { provide: APOLLO_OPTIONS, useFactory: createApollo, deps: [HttpLink], }, ], }) export class GraphQLModule {}
const uri = 'https://myapp.io/graphql'; export function createApollo(httpLink: HttpLink) { return { link: httpLink.create({uri}), cache: new InMemoryCache(), }; } @NgModule({ exports: [ApolloModule, HttpLinkModule], providers: [ { provide: APOLLO_OPTIONS, useFactory: createApollo, deps: [HttpLink], }, ], }) export class GraphQLModule {}
apollo ANGULAR - QUERIES
const GET_USERS = gql`users { email firstName lastName }`; constructor(private apollo: Apollo) { } getUsers() { this.apollo .watchQuery({ query: GET_USERS, }) .valueChanges .subscribe(result => { this.data = result.data && result.data.users; this.loading = result.loading; this.error = result.error; }); }
const GET_USERS = gql`users { email firstName lastName }`; constructor(private apollo: Apollo) { } getUsers() { this.apollo .watchQuery({ query: GET_USERS, }) .valueChanges .subscribe(result => { this.data = result.data && result.data.users; this.loading = result.loading; this.error = result.error; }); }
const GET_USERS = gql`users { email firstName lastName }`; constructor(private apollo: Apollo) { } getUsers() { this.apollo .watchQuery({ query: GET_USERS, }) .valueChanges .subscribe(result => { this.data = result.data && result.data.users; this.loading = result.loading; this.error = result.error; }); }
const GET_USERS = gql`users { email firstName lastName }`; constructor(private apollo: Apollo) { } getUsers() { this.apollo .watchQuery({ query: GET_USERS, }) .valueChanges .subscribe(result => { this.data = result.data && result.data.users; this.loading = result.loading; this.error = result.error; }); }
const GET_USERS = gql`users { email firstName lastName }`; constructor(private apollo: Apollo) { } getUsers() { this.apollo .watchQuery({ query: GET_USERS, }) .valueChanges .subscribe(result => { this.data = result.data && result.data.users; this.loading = result.loading; this.error = result.error; }); }
apollo ANGULAR - MUTATIONS
const CREATE_USER = gql`mutation createUser(user: CreateUserInput!) { createUser(user: $user) {id email firstName lastName} }`; constructor(private apollo: Apollo) { } createUser({ email, firstName, lastName }) { this.apollo.mutate({ mutation: CREATE_USER, variables: { user: { email, firstName, lastName } } }) .subscribe( user => this.user = user ); }
const CREATE_USER = gql`mutation createUser(user: CreateUserInput!) { createUser(user: $user) {id email firstName lastName} }`; constructor(private apollo: Apollo) { } createUser({ email, firstName, lastName }) { this.apollo.mutate({ mutation: CREATE_USER, variables: { user: { email, firstName, lastName } } }) .subscribe( user => this.user = user ); }
const CREATE_USER = gql`mutation createUser(user: CreateUserInput!) { createUser(user: $user) {id email firstName lastName} }`; constructor(private apollo: Apollo) { } createUser({ email, firstName, lastName }) { this.apollo.mutate({ mutation: CREATE_USER, variables: { user: { email, firstName, lastName } } }) .subscribe( user => this.user = user ); }
const CREATE_USER = gql`mutation createUser(user: CreateUserInput!) { createUser(user: $user) {id email firstName lastName} }`; constructor(private apollo: Apollo) { } createUser({ email, firstName, lastName }) { this.apollo.mutate({ mutation: CREATE_USER, variables: { user: { email, firstName, lastName } } }) .subscribe( user => this.user = user ); }
apollo ANGULAR - SUBSCRIPTIONS
const CREATE_USER = gql`subscription {onUpdateTopic { id name active }}`; constructor(private apollo: Apollo) { } subscribeToUpdate({ email, firstName, lastName }) { return this.apollo .subscribe({ query: gql`${subscription}`, }) .subscribe( topic => this.topic = topic ); }
const CREATE_USER = gql`subscription {onUpdateTopic { id name active }}`; constructor(private apollo: Apollo) { } subscribeToUpdate({ email, firstName, lastName }) { return this.apollo .subscribe({ query: gql`${subscription}`, }) .subscribe( topic => this.topic = topic ); }
const CREATE_USER = gql`subscription {onUpdateTopic { id name active }}`; constructor(private apollo: Apollo) { } subscribeToUpdate({ email, firstName, lastName }) { return this.apollo .subscribe({ query: gql`${subscription}`, }) .subscribe( topic => this.topic = topic ); }
const CREATE_USER = gql`subscription {onUpdateTopic { id name active }}`; constructor(private apollo: Apollo) { } subscribeToUpdate({ email, firstName, lastName }) { return this.apollo .subscribe({ query: gql`${subscription}`, }) .subscribe( topic => this.topic = topic ); }
AWS AppSync Client
AppSync Client is an Apollo Client
- Offline Support
- Authorization
- Subscription Handshaking
AppSync Client - OFFLINE

AppSync Client - REAL-TIME

AppSync Client
export const environment = {
production: true,
aws: {
aws_project_region: 'eu-west-1',
aws_appsync_graphqlEndpoint: 'https://xxx.region.amazonaws.com/graphql',
aws_appsync_apiKey: 'da2-xxx',
}
};
npm install aws-appsync
AppSync Client
import { AUTH_TYPE, AWSAppSyncClient } from 'aws-appsync'; import { environment } from 'src/environments/environment'; @NgModule({ exports: [ApolloModule, HttpLinkModule], }) export class GraphQLModule { constructor(apollo: Apollo) { const client: any = new AWSAppSyncClient({ url: environment.aws.aws_appsync_graphqlEndpoint, region: environment.aws.aws_project_region, auth: { type: AUTH_TYPE.API_KEY, apiKey: environment.aws.aws_appsync_apiKey } }); apollo.setClient(client); } }
import { AUTH_TYPE, AWSAppSyncClient } from 'aws-appsync'; import { environment } from 'src/environments/environment'; @NgModule({ exports: [ApolloModule, HttpLinkModule], }) export class GraphQLModule { constructor(apollo: Apollo) { const client: any = new AWSAppSyncClient({ url: environment.aws.aws_appsync_graphqlEndpoint, region: environment.aws.aws_project_region, auth: { type: AUTH_TYPE.API_KEY, apiKey: environment.aws.aws_appsync_apiKey } }); apollo.setClient(client); } }
import { AUTH_TYPE, AWSAppSyncClient } from 'aws-appsync'; import { environment } from 'src/environments/environment'; @NgModule({ exports: [ApolloModule, HttpLinkModule], }) export class GraphQLModule { constructor(apollo: Apollo) { const client: any = new AWSAppSyncClient({ url: environment.aws.aws_appsync_graphqlEndpoint, region: environment.aws.aws_project_region, auth: { type: AUTH_TYPE.API_KEY, apiKey: environment.aws.aws_appsync_apiKey } }); apollo.setClient(client); } }
bonus
useful GraphQL tools
Graphiql
$ npm install -g @aws-amplify/cli $ amplify init $ amplify add api ? Please select from one of the below mentioned services: GraphQL .. ? Do you have an annotated GraphQL schema? Yes ? Provide your schema file path: src/schema.graphql GraphQL schema compiled successfully. $ amplify push √ Downloaded the schema √ Generated GraphQL operations successfully and saved at src\graphql √ Code generated successfully and saved in file src\app\API.service.ts GraphQL endpoint: https://xxxx.appsync-api.us-east-1.amazonaws.com/graphql GraphQL API KEY: da2-xxxxx
$ npm install -g @aws-amplify/cli $ amplify init $ amplify add api ? Please select from one of the below mentioned services: GraphQL .. ? Do you have an annotated GraphQL schema? Yes ? Provide your schema file path: src/schema.graphql GraphQL schema compiled successfully. $ amplify push √ Downloaded the schema √ Generated GraphQL operations successfully and saved at src\graphql √ Code generated successfully and saved in file src\app\API.service.ts GraphQL endpoint: https://xxxx.appsync-api.us-east-1.amazonaws.com/graphql GraphQL API KEY: da2-xxxxx
$ npm install -g @aws-amplify/cli $ amplify init $ amplify add api ? Please select from one of the below mentioned services: GraphQL .. ? Do you have an annotated GraphQL schema? Yes ? Provide your schema file path: src/schema.graphql GraphQL schema compiled successfully. $ amplify push √ Downloaded the schema √ Generated GraphQL operations successfully and saved at src\graphql √ Code generated successfully and saved in file src\app\API.service.ts GraphQL endpoint: https://xxxx.appsync-api.us-east-1.amazonaws.com/graphql GraphQL API KEY: da2-xxxxx
AWS AMPLIFY
const awsmobile = {
"aws_project_region": "us-east-1",
"aws_appsync_graphqlEndpoint": "https://xxxx.appsync-api.us-east-1.amazonaws.com/graphql",
"aws_appsync_region": "us-east-1",
"aws_appsync_authenticationType": "API_KEY",
"aws_appsync_apiKey": "da2-xxxxxx",
"aws_content_delivery_bucket": "ngx-pwa-201911081804-hostingbucket-dev",
"aws_content_delivery_bucket_region": "us-east-1",
"aws_content_delivery_url": "https://xxxxxx.cloudfront.net"
};
export default awsmobile;
Thank you
lahdiouiouadie

CHIHABOTMANI

Build a real-time PWA with Angular and GraphQL
By Ouadie LAHDIOUI
Build a real-time PWA with Angular and GraphQL
Build a real-time PWA with Angular and GraphQL
- 2,235