Unleash the power

     of Angular with

            GraphQL

Unleash the power

of Angular with

GraphQL

Workshop 

In a nutshell

Communities

Social Developer

Recognitions

Senior Product Manager Cosmosdb

GDE,MCT and former MVP

Top stackoverflow contributor

 

I'm Sajeetharan a.k.a Sajee

@sajeetharan
@kokkisajee
@sajeetharan
@sajeetharan

A BIG Thank You GDG SL

What Devfest/Angular has given me

2010

2015

2019

2018

2020

Attended first Devfest SL 2012 

Started with angular, became the top angularjs answerer on SO

Became the first google dev expert in the country

Started NG-Srilanka, First Angular Team meet at San Francisco

Book on Angular Projects,Top 4 contributors on Angular

2022

To produce 2 more GDEs from SL

👨‍🎨 Front-End developers ?

👨‍💻 Back-End developers ?

🦸‍♂️ Something else ? something in between ?

☁️ Cloud developers ?

Who are you?

About this Session

👍A “Beginner workshop"

 

👍It won’t teach you everything,  But it will help you learn what you need to know to get started

 

 

 

 

👍Be ready to  ask questions

 

👍Celebrate together!

Introduction to JAMstack, Angular and why it's important

Graphql and it's core concepts

Get our hands dirty

Today's challenge

If you ask "What will I learn Today?"

How to Build JAM stack (Angular) App with Graphql

A quick Time travel

through the history of web development

@kokkisajee

So it all began..

@kokkisajee

Server

1. Request

2. HTML

@kokkisajee

Dynamic Websites

@kokkisajee

Backend

1. Request

4. Dynamically generated HTML

DB

2. Query

3. Results

But: "Click and wait" for every page

@kokkisajee

Server

1. Request

2. HTML

3. AJAX Request

4. XML (oder JSON)

@kokkisajee

The age of SPAs and Stacks

Linux
Apache
MySQL
PHP

Mongo
E
xpress
Angular
Node

Mongo
E
xpress
React
Node

Traditional Work Flow

WebServer

Client

AppServer

DB

@kokkisajee

Advantages

No full reload on further navigation

Better UX compared to "click and wait"

Problems

Slow Time to first paint (due to parsing)

White screen / flash of content

Javascript generates HTML -> bad SEO

mediocre UX for initial page load

@kokkisajee

And now?

@kokkisajee

Traditional SSR

"Old" Static pages

Single Page Apps

Server Side Rendering

JAMstack

CDN

Client

DB

@kokkisajee

JAMstack

@kokkisajee

J

A

M

avascript

PIs

arkup

@kokkisajee

Static, won't be modified by server

Generated on build-time (before (re-)deploying)

Usually compiled/built via Static Site Generator (SSG)

Markup

@kokkisajee

<app-make-meme *ngIf="imgUrl" [imageUrl]="imgUrl"></app-make-meme>

Example (Angular)

@kokkisajee

Responsible for the website's dynamics

Only on client-side

Independent of frameworks or libraries

Javascript

@kokkisajee

Used for interacting with the website (comments, forms...)

Called through Javascript and HTTPS

Good fit: Microservices, Serverless Functions or similar

APIs

@kokkisajee

Beispiel (serverless fn)

import { promisify } from 'util'
import sendmail from 'sendmail'

const shouldSend = process.env.SEND_MAIL

exports.handler = async (event) => {
  if (event.httpMethod !== 'POST') {
    return { statusCode: 405, body: JSON.stringify({ 'error': 'Method not allowed' }) }
  }
  try {
    const params = JSON.parse(event.body)
    const attributes = ['name', 'email', 'msg']
    const sanitizedAttributes = attributes.map(n => validateAndSanitize(n, params[n]))
    const someInvalid = sanitizedAttributes.some(r => !r)

    if (someInvalid) {
      return { statusCode: 422, body: JSON.stringify({ 'error': 'Ugh.. That looks unprocessable!' }) }
    }

    const sendMail = (name, email, msg) => pSendMail({
      from: email,
      to: process.env.MAIL_TO,
      subject: 'New contact form message',
      text: msg
    })

    try {
      await sendMail(...sanitizedAttributes)
      return { statusCode: 200, body: JSON.stringify({ 'message': 'OH YEAH' }) }
    } catch (e) { /* */ }
  } catch (_) { /* */}
}

@kokkisajee

Create your page

via SSG

Upload your code

via Git

Build your page with your SSG

Deploy the static Files via CDN or similar

Done!

JAMStack(static) Apps Workflow

@kokkisajee

But why JAMstack?

@kokkisajee

Performance

Simplicity

Security

Cost

@kokkisajee

Simplicity

Only static HTML files, no .php / .py / ...

You don't need a server*

No difficult deploy pipelines anymore!

*hosting via. GitHub pages oder Netlify

@kokkisajee

Security

No content generated "on the fly". Static files!

API endpoints are small and ideally isolated

Good practice: "Content API" only available at build time

@kokkisajee

@kokkisajee

Performance

Improved Time to first Byte due to CDN

Easy Cache Invalidation

Quick Response Time as it is just HTML

Ideal for SEO

@kokkisajee

Cost

No server, no server fees!

Free Hosting (via Netlify or GH Pages)

Serverless APIs are billed by usage

@kokkisajee

@kokkisajee

@kokkisajee

We need to talk about graphQL

@kokkisajee

Once upon a time..

a sweet little Web developer...

Yep, this stickman 

@kokkisajee

He was happy

writing desktop &

Web apps...

Like normal happy

@kokkisajee

Then Mobile Apps came

took over the world
and he was like...

"Man, I know nothing
but JavaScript, HTML
and CSS!" #Meh

@kokkisajee

He had to have multiple faces

build apps
with different sources

But still he had to learn new languages and stuff that would take forever

@kokkisajee

Then he discovered

Graphql

@kokkisajee

Sajee

Sanga

Mahela

"CRUD pls"

Sajee

Sanga

"small change pls"

release

"small change pls"

"small change pls"

"small change pls"

@kokkisajee

@kokkisajee

http://facebook.github.io/graphql/draft/

@kokkisajee

Sajee

Sanga

Mahela

"CRUD pls"

Sajee

Sanga

"small change pls"

release

"small change pls"

"small change pls"

"small change pls"

@kokkisajee

Sajee

Sanga

Mahela

GraphQL

@kokkisajee

main features

@kokkisajee

QUERY

MUTATION

SUBSCRIPTION

main features

Sanga Mahela Quit Cricket

@kokkisajee

Backend support

@kokkisajee

Typical architecture of a web application using REST APIs

Designing our REST query

Get person data using an ID

{
  "name": "Maithiri Sirisena",
  "birthYear": "41.9BBY",
  "CourseId": 1
  "filmIds": [1, 2, 3, 6]
}
GET /participants/{id}

GET /participant/4

Designing our REST query

Then to read the course’s name, we ask:


GET /participants/4

GET /participants/1

Designing our REST query

And to read the attended session titles, we ask:


GET /participants/4

GET /courses/1

GET /sessions/1

GET /sessions/2

GET /sessions/3

GET /sessions/6

Takeaway points

  • We made 6 network calls to get what we needed
  • UI Developer had to tailor the responses to fit the needs
  • Alternate is create a dedicated endpoint for this view but then scaling will become difficult to manage with changing requirements

Common problems with REST

Issue#1 Multiple round trips

Issue#2 Over-fetching of data

While requesting nested data, we often have to make several round trips or define an entirely newend point

Wouldn't it be nice if client receives only the data that it requires?

Issue#3 Absence of  Self-Documentation

The onus lies with the developer to document the API and allow exploration and discovery of your APIs.

GraphQL approach

GET /graphql?query=

{
  Participant(ID: 4) {
    name,
    alias,
    comments {
      description
    },
    sessions {
      title
    }
  }
}


{
  "data": {
    "person": {
      "name": "Ranil",
      "alias":Wick
      "Comments": {
        "name": ""
      },
      "Sessions": [
        { "title": "Database Internals" },
        { "title": "Graphql" }
    }
  }
}

Who is using GraphQL

{
  "me": {
    "name": "Sajeetharan"
  }
}
{
  me {
    name
  }
}

Basic concepts

@kokkisajee

@kokkisajee

Query

Mutation

Subscription

Entry points

Basic concepts

@kokkisajee

query GET_MAIN_DATA {
  me {














  }
}
name
bio
avatarLg: avatar_url(size: 168)
avatarSm: avatar_url(size: 40)
socials {
  url
}
photos(limit: 3) {
  src
}
friends(limit: 3) {
  name
  avatar_url(size: 100)
}

Query : Example

@kokkisajee

Query

Mutation

Subscription

Basic concepts

@kokkisajee

mutation CREATE_POST($value: String!) {
 createPost(value: $value) {














 }
}
author {
  name
  avatar_url(size: 40)
}
body
id
date
...

Mutation : Example

@kokkisajee

Query

Mutation

Subscription

Basic concepts

@kokkisajee

subscription FEED_UPDATES {
 postAdded {










 }
}
author {
  name
  avatar_url(size: 40)
}
body
id
date
...

Query : Subscription

@kokkisajee

query getUser($withFriends: Boolean) {
  me {
    name
    friends @include(if: $withFriends) {
      name
    }
  }
}

Basic concepts : Directive

@kokkisajee

Schema

Basic concepts

schema: {
  query: Query
  mutation: Mutation
  subscription: Subscription
}

@kokkisajee

Query: {
  user(obj, args, context, info) {
    return context.db.loadUserByID(args.id)
  }
}

User: {
  name(obj, args, context, info) {
    return obj.name
  }
}

Resolvers

@kokkisajee

🔮 GraphiQL 🔮

An in-browser IDE for exploring GraphQL.

@kokkisajee

⚙️ GraphQL CLI ⚙️

Command line tool for common GraphQL development workflows

https://github.com/graphql-cli/graphql-cli

Command Description
graphql create [directory] Bootstrap a new GraphQL project
graphql get-schema Download schema from endpoint
graphql schema-status Show source & timestamp of local schema
graphql query <file> Run query/mutation
graphql diff Show a diff between two schemas

@kokkisajee

Let's do!

Download

https://nodejs.org/en/download/
https://code.visualstudio.com/

Install

npm install -g @angular/cli@latest | @angular/cli@14.0.10

@kokkisajee

Create Folders

# Create project folder
mkdir devfest-todo && cd devfest-todo

# Create sub-folder for our server
mkdir devfest-todo-seerv && cd devfest-todo-server

@kokkisajee

npm init

# Install typescript, nodemon and node-ts
npm i typescript nodemon ts-node --save-dev

# Initialize TypeScript project
npx tsc --init --rootDir src --outDir dist --lib dom,es6 --module commonjs --removeComments

Initialize Node.js Project

@kokkisajee

Apollo is a platform for building a data graph, a communication layer that seamlessly connects application clients (JAM stack and Mobile) to back-end services.

@kokkisajee

Install Apollo Dependencies

# Apollo Platform and Server dependencies
npm i apollo-server-express@^2 cors express graphql http ncp uuid --save

# Typing files for Node.js and dependencies
npm i @types/express @types/graphql @types/uuidv4 @types/node graphql-import graphql-import-node graphql-tools@4.x  --save-dev

@kokkisajee

Add proper start script

// package.json
// ...
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "nodemon --exec npx ts-node ./src/index.ts -e ts,graphql"
  },
// ...// package.json
// ...
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "nodemon --exec npx ts-node ./src/index.ts -e ts,graphql"
  },
// ...

@kokkisajee

Create our Apollo Graphql server

// src/index.ts

import express from 'express';
import {ApolloServer} from 'apollo-server-express';
import {createServer} from 'http';
import cors from 'cors';
import {schema} from './schema';

const host = '0.0.0.0';
const port = 8080;
const app = express();
const server = new ApolloServer({schema});
server.applyMiddleware({app, path: '/graphql'});
const httpServer = createServer(app);
httpServer.listen(
  {host, port}, 
  (): void => console.log(`\n🚀      GraphQL is now running on http://localhost:${port}/graphql`)
);

@kokkisajee

Add "Hello World" GraphQL Schema

// src/schema.graphql

type Query {
  helloWorld: String!
}

@kokkisajee

Compile Schema and Implement resolvers

// src/schema.ts

import 'graphql-import-node';
import * as typeDefs from './schema.graphql';
import {makeExecutableSchema} from 'graphql-tools';
import {GraphQLSchema} from 'graphql';
import {IResolvers} from 'graphql-tools';

export const resolvers: IResolvers = {
  Query: {
    helloWorld() {
      return `👋 Hello world! 👋`;
    }
  }
};

export const schema: GraphQLSchema = makeExecutableSchema({
  typeDefs,
  resolvers
});

@kokkisajee

Visit the Developer UI of Apollo to run queries and download the schema of your server.


http://localhost:8080/graphql

@kokkisajee

"HEROES DON'T ALWAYS WEAR CAPES

SOMETIMES THEY CODE ANGULAR"

  • A framework for web front-end app 

  • A platform for integrated development

  • An ecosystem for developers

If you are new to Angular  Watch Angular Zero to Hero From here

(1) Angular Zero to Hero by Sajeetharan Sinnathurai of Microsoft | Quarantine Tech Talk - YouTube

@kokkisajee

Building Blocks of Angular

  • Module
  • Services
  • Component
  • Pipes
  • Directives
  • Dependency Injection

Mahela & Sanga are Certified Professional Developers

Install Angular CLI and Create an App


# Create todo app (in the graphql-todo folder!)
ng new devfest-todo-app
# ? Would you like to add Angular routing? No
# ? Which stylesheet format would you like to use? CSS

# Enter folder and serve app
cd devfest-todo-app 

#start the application
npm start

@kokkisajee

Let's add Angular Material

# Using the CLI we can add ng addable libraries
ng add @angular/material@14.0

# ? Choose a prebuilt theme name, or "custom" for a custom theme: Indigo/Pink
# ? Set up HammerJS for gesture recognition? Yes
# ? Set up browser animations for Angular Material? Yes

@kokkisajee

Add Material Module

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { MatCardModule } from '@angular/material/card';
import { MatButtonModule} from '@angular/material/button';
import { MatMenuModule } from '@angular/material/menu';
import {MatListModule} from "@angular/material/list";
import { MatToolbarModule } from '@angular/material/toolbar';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatIconModule } from '@angular/material/icon';
import {MatFormFieldModule} from "@angular/material/form-field";
import { AppComponent } from './app.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { HttpClientModule } from '@angular/common/http';
import { MatInputModule } from '@angular/material/input';
@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    HttpClientModule,
    MatButtonModule,
    MatMenuModule,
    MatToolbarModule,
    MatIconModule,
    MatCardModule,
    MatListModule,
    MatFormFieldModule,
    MatCheckboxModule,
    MatInputModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

@kokkisajee

Install Graphql Angular Client

# Install Apollo Angular
ng add apollo-angular@v3  

@kokkisajee

Install Graphql Angular Client

// src/app/graphql.module.ts

import {NgModule} from '@angular/core';
import {ApolloModule, APOLLO_OPTIONS} from 'apollo-angular';
import {HttpLinkModule, HttpLink} from 'apollo-angular-link-http';
import {InMemoryCache} from 'apollo-cache-inmemory';

const uri = 'http://localhost:8080/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 {}

Install Graphql Angular Client

# Install Apollo Angular
ng update apollo-angular@v4.1

@kokkisajee

Build a Simple Hello world App

<!-- src/app/app.component.html -->

<h1>
  {{message}}
</h1><!-- src/app/app.component.html -->

<h1>
  {{message}}
</h1><!-- src/app/app.component.html -->

<h1>
  {{message}}
</h1><!-- src/app/app.component.html -->

<h1>
  {{message}}
</h1>

@kokkisajee

Obtain "Hello world" message from server

// src/app/app.component.ts

import {Component} from '@angular/core';
import {Apollo} from 'apollo-angular';
import gql from 'graphql-tag';

const helloWorld = gql`
  {
    helloWorld
  }
`;

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  todoItems = ['Go for a walk with my dog', 'Buy some bread and butter', 'Learn how to paint'];
  message: string = '';

  constructor(private apollo: Apollo) {
    this.apollo
      .watchQuery<{helloWorld: string}>({query: helloWorld})
      .valueChanges
      .subscribe(result => {
        this.message = result.data.helloWorld;
      });
  }

  addTodo(title: string) {
    this.todoItems = [title, ...this.todoItems];
  }
}

Update our Graphql Schema on the server

type Query {
  getTodos: [Todo!]!
}
  
type Todo {
  id: ID!
  title: String!
  done: Boolean!
}

@kokkisajee

Add Model and Update Resolvers for new Query

// graphql-todo-server/src/schema.ts
 
import 'graphql-import-node';
import * as typeDefs from './schema.graphql';
import {makeExecutableSchema} from 'graphql-tools';
import {GraphQLSchema} from 'graphql';
import {IResolvers} from 'graphql-tools';

export interface Todo {
  id: string;
  title: string;
  done: boolean;
}

export const inMemoryDb: {todos: {[id: string]: Todo}} = {
  todos: {
    '1': {
      id: '1',
      title: 'Todo 1',
      done: false
    }
  }
}

export const resolvers: IResolvers = {
  Query: {
    getTodos(): Todo[] {
      return Object.values(inMemoryDb.todos);
    }
  }
};

export const schema: GraphQLSchema = makeExecutableSchema({
  typeDefs,
  resolvers
});

@kokkisajee

Add Model todo.ts

export interface Todo{
    id: number;
    title: string;
    done: number;
}

@kokkisajee

Build a Simple ToDo App UI template

<!-- src/app/app.component.html -->

<mat-toolbar class="header">
  <span role="header" color='primary'>GraphQL Todo</span>
</mat-toolbar>
<main class="container">
  <div class="add-todo">
    <mat-form-field class="add-todo-field">
      <input #todoInput matInput 
             (keyup.enter)="addTodo(todoInput.value); todoInput.value = ''"
             placeholder="Today, I learn about Angular, GraphQL and TypeScript!">
    </mat-form-field>
    <button mat-mini-fab (click)="addTodo(todoInput.value); todoInput.value = ''">
      <i class="material-icons">add</i>
    </button>
  </div>
  <mat-list>
    <mat-list-item *ngFor="let todo of todoItems" class="list">
      <mat-checkbox color='primary'>
        {{ todo }}
      </mat-checkbox>
    </mat-list-item>
  </mat-list>
</main><!-- src/app/app.component.html -->

<mat-toolbar class="header">
  <span role="header" color='primary'>GraphQL Todo</span>
</mat-toolbar>
<main class="container">
  <div class="add-todo">
    <mat-form-field class="add-todo-field">
      <input #todoInput matInput 
             (keyup.enter)="addTodo(todoInput.value); todoInput.value = ''"
             placeholder="Today, I learn about Angular, GraphQL and TypeScript!">
    </mat-form-field>
    <button mat-mini-fab (click)="addTodo(todoInput.value); todoInput.value = ''">
      <i class="material-icons">add</i>
    </button>
  </div>
  <mat-list>
    <mat-list-item *ngFor="let todo of todoItems" class="list">
      <mat-checkbox color='primary'>
        {{ todo }}
      </mat-checkbox>
    </mat-list-item>
  </mat-list>
</main><!-- src/app/app.component.html -->

<mat-toolbar class="header">
  <span role="header" color='primary'>GraphQL Todo</span>
</mat-toolbar>
<main class="container">
  <div class="add-todo">
    <mat-form-field class="add-todo-field">
      <input #todoInput matInput 
             (keyup.enter)="addTodo(todoInput.value); todoInput.value = ''"
             placeholder="Today, I learn about Angular, GraphQL and TypeScript!">
    </mat-form-field>
    <button mat-mini-fab (click)="addTodo(todoInput.value); todoInput.value = ''">
      <i class="material-icons">add</i>
    </button>
  </div>
  <mat-list>
    <mat-list-item *ngFor="let todo of todoItems" class="list">
      <mat-checkbox color='primary'>
        {{ todo }}
      </mat-checkbox>
    </mat-list-item>
  </mat-list>
</main><!-- src/app/app.component.html -->

<mat-toolbar class="header">
  <span role="header" color='primary'>GraphQL Todo</span>
</mat-toolbar>
<main class="container">
  <div class="add-todo">
    <mat-form-field class="add-todo-field">
      <input #todoInput matInput 
             (keyup.enter)="addTodo(todoInput.value); todoInput.value = ''"
             placeholder="Today, I learn about Angular, GraphQL and TypeScript!">
    </mat-form-field>
    <button mat-mini-fab (click)="addTodo(todoInput.value); todoInput.value = ''">
      <i class="material-icons">add</i>
    </button>
  </div>
  <mat-list>
    <mat-list-item *ngFor="let todo of todoItems" class="list">
      <mat-checkbox color='primary'>
        {{ todo }}
      </mat-checkbox>
    </mat-list-item>
  </mat-list>
</main>

@kokkisajee

Obtain Todos in Angular App From Server

// graphql-todo-app/src/app/app.component.ts
 
import { Component } from '@angular/core';
import {Apollo} from 'apollo-angular';
import gql from 'graphql-tag';
import {Todo} from '../../../graphql-todo-server/src/schema';

const allTodosQuery = gql`
  query {
    getTodos {
      id
      title
      done
    }
  }
`;

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  todoItems: Todo[] = [];

  constructor(private apollo: Apollo) {
    this.apollo
      .watchQuery<{getTodos: Todo[]}>({
        query: allTodosQuery
      })
      .valueChanges
      .subscribe(result => {
        this.todoItems = result.data.getTodos;
      });
  }

  addTodo(title: string) {
    // TODO: This will be implemented later
  }
}

@kokkisajee

How cool is that!?

@kokkisajee

Add Mutations to Schema in the Server

type Query {
  getTodos: [Todo!]!
}

type Mutation {
  addTodo(title: String!, done: Boolean! = false): [Todo!]!
  updateTodo(id: ID!, done: Boolean!): [Todo!]!
}

type Todo {
  id: ID!
  title: String!
  done: Boolean!
}

@kokkisajee

Add Mutation Resolvers  


import 'graphql-import-node';
import * as typeDefs from './schema.graphql';
import {makeExecutableSchema} from 'graphql-tools';
import {GraphQLSchema} from 'graphql';
import {IResolvers} from 'graphql-tools';
import {v4 as uuidv4} from 'uuid';

export interface Todo {
  id: string;
  title: string;
  done: boolean;
}

export const inMemoryDb: {todos: {[id: string]: Todo}} = {
  todos: {
    '1': {
      id: '1',
      title: 'Todo 1',
      done: false
    }
  }
}

export const resolvers: IResolvers = {
  Query: {
    getTodos(): Todo[] {
      return Object.values(inMemoryDb.todos);
    }
  },
  Mutation: {
    addTodo(_, {title,  done = false}: Todo): Todo[] {
      const id: string = uuidv4();
      inMemoryDb.todos = {
        ...inMemoryDb.todos,
        [id]: {
          id,
          title,
          done
        }
      };
      return Object.values(inMemoryDb.todos);
    },
    updateTodo(_, {id, done}: {id: string, done: boolean}): Todo[] {
      inMemoryDb.todos = {
        ...inMemoryDb.todos,
        [id]: {
          ...inMemoryDb.todos[id],
          id,
          done
        }
      };
      return Object.values(inMemoryDb.todos);
    }
  }
};

export const schema: GraphQLSchema = makeExecutableSchema({
  typeDefs,
  resolvers
});

@kokkisajee

Implement add and Update Todo Methods

	
 
import { Component } from '@angular/core';
import {Apollo} from 'apollo-angular';
import gql from 'graphql-tag';
import {Todo} from '../../../graphql-todo-server/src/schema';
 
const allTodosQuery = gql`
  query {
    getTodos {
      id
      title
      done
    }
  }
`;
 
const addTodoMutation = gql`
  mutation addTodo($title: String!, $done: Boolean! = false) {
    addTodo(title: $title, done: $done) {
      id
      title
      done
    }
  }
`;
 
const updateTodoMutation = gql`
  mutation updateTodo($id: ID!, $done: Boolean!) {
    updateTodo(id: $id, done: $done) {
      id
      title
      done
    }
  }
`;
 
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  todoItems: Todo[] = [];
 
  constructor(private apollo: Apollo) {
    this.apollo
      .watchQuery<{getTodos: Todo[]}>({
        query: allTodosQuery
      })
      .valueChanges
      .subscribe(result => {
        this.todoItems = result.data.getTodos;
      });
  }
 
  addTodo(title: string) {
    this.apollo.mutate<{addTodo: Todo[]}>({
      mutation: addTodoMutation,
      variables: {
        title
      }
    }).subscribe(result => {
      if(result!=null && result.data!=null){
      this.todoItems = result.data.addTodo;
      }
    })
  }
 
  updateTodo(id: string, done: boolean) {
    this.apollo.mutate<{updateTodo: Todo[]}>({
      mutation: updateTodoMutation,
      variables: {
        id,
        done
      }
    }).subscribe(result => {
      if(result!=null && result.data!=null){
      this.todoItems = result.data.updateTodo;
      }
    })
  }
}

@kokkisajee

Update Component view to update Todos

<!-- graphql-todo-app/src/app/app.component.html -->
 
<mat-toolbar class="header">
  <span role="header" color='primary'>GraphQL Todo</span>
</mat-toolbar>
<main class="container">
  <div class="add-todo">
    <mat-form-field class="add-todo-field">
      <input #todoInput matInput 
             (keyup.enter)="addTodo(todoInput.value); todoInput.value = ''"
             placeholder="Today, I learn about Angular, GraphQL and TypeScript!">
    </mat-form-field>
    <button mat-mini-fab (click)="addTodo(todoInput.value); todoInput.value = ''">
      <i class="material-icons">add</i>
    </button>
  </div>
  <mat-list #todoist class="todolist">
    <mat-list-item *ngFor="let todo of todoItems" class="list">
      <mat-checkbox color='primary' 
                    [checked]="todo.done" 
                    (change)="updateTodo(todo.id, !todo.done)">
        {{ todo.title }}
      </mat-checkbox>
    </mat-list-item>
  </mat-list>
</main>

@kokkisajee

Hope things go smoothly

@kokkisajee

Otherwise...

@kokkisajee

Introduction to JAMstack and why it's important

Graphql and it's core concepts

Get our hands dirty

Let's do a Recap

@kokkisajee

Github Repository of the Workshop: sajeetharan/devfest2022: Devfest 2022 workshop sample (github.com)

 

Angular Website: https://angular.io/

 

Apollo Website: https://www.apollographql.com

GraphQL Website: https://graphql.org

 

 

Where to go from Here?

JAMstack.org - More about JAMstack

@kokkisajee

"Always pass on what you have learned."

 

@kokkisajee

I'm open to questions!

@kokkisajee

Thank you!

https://slides.com/sajeetharan/devfest2022workshopangular

@kokkisajee

Made with Slides.com