Unleash the power

     of JAMstack and

            GraphQL

Unleash the power

of JAMstack and

GraphQL

Coimbatore Front End

User Group (June 2022)

Coimbatore Front End

User Group (June2022)

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

👨‍🎨 Front-End developers ?

👨‍💻 Back-End developers ?

🦸‍♂️ Something else ? something in between ?

☁️ Cloud developers ?

Who are you?

About this Session

👍A “Beginner session"

 

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

 

 

 

 

 

 

👍Be ready to Unmute and ask questions

Introduction to JAMstack and why it's important

Graphql and it's core concepts

Get our hands dirty

Kahoot Quiz and QnA

Today's challenge

If you ask "What will I learn Today?"

How to Build JAM stack App with Graphql

A quick Time travel

through the history of web development

So it all began..

Server

1. Request

2. HTML

Dynamic Websites

Backend

1. Request

4. Dynamically generated HTML

DB

2. Query

3. Results

But: "Click and wait" for every page

AJAX and JavaScript

Server

1. Request

2. HTML

3. AJAX Request

4. XML (oder JSON)

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

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

And now?

Traditional SSR

"Old" Static pages

Single Page Apps

Server Side Rendering

JAMstack

CDN

Client

DB

JAMstack

J

A

M

avascript

PIs

arkup

Static, won't be modified by server

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

Usually compiled/built via Static Site Generator (SSG)

Markup

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

Example (Angular)

Responsible for the website's dynamics

Only on client-side

Independent of frameworks or libraries

Javascript

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

Called through Javascript and HTTPS

Good fit: Microservices, Serverless Functions or similar

APIs

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 (_) { /* */}
}

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

But why JAMstack?

Performance

Simplicity

Security

Cost

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

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

Performance

Improved Time to first Byte due to CDN

Easy Cache Invalidation

Quick Response Time as it is just HTML

Ideal for SEO

Cost

No server, no server fees!

Free Hosting (via Netlify or GH Pages)

Serverless APIs are billed by usage

When not to go for JAMstack?

Highly dynamic (and SEO-related) Data

Better go for "modern" SSR (e.g. with Nuxt)

We need to talk about graphQL

Once upon a time..

a sweet little Web developer...

Yep, this stickman 

He was happy

writing desktop &

Web apps...

Like normal happy

Then Mobile Apps came

took over the world
and he was like...

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

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

Then he discovered

Graphql

Sajee

Sachin

Dhoni

"CRUD pls"

Sajee

Sachin

"small change pls"

release

"small change pls"

"small change pls"

"small change pls"

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

Sajee

Sachin

Dhoni

"CRUD pls"

Sajee

Sachin

"small change pls"

release

"small change pls"

"small change pls"

"small change pls"

Sajee

My3

MR

 

GraphQL

main features

QUERY

MUTATION

SUBSCRIPTION

main features

Sanga Mahela Quit Cricket

Backend support

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": "Madhan",
      "alias":madhang
      "Comments": {
        "name": ""
      },
      "Sessions": [
        { "title": "Database Internals" },
        { "title": "Graphql" }
    }
  }
}

Who is using GraphQL

{
  "me": {
    "name": "Dinesh Kuamr"
  }
}
{
  me {
    name
  }
}

Basic concepts

Query

Mutation

Subscription

Entry points

Basic concepts

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

Query

Mutation

Subscription

Basic concepts

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














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

Mutation : Example

Query

Mutation

Subscription

Basic concepts

subscription FEED_UPDATES {
 postAdded {










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

Query : Subscription

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

Basic concepts : Directive

Schema

Basic concepts

schema: {
  query: Query
  mutation: Mutation
  subscription: Subscription
}
Query: {
  user(obj, args, context, info) {
    return context.db.loadUserByID(args.id)
  }
}

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

Resolvers

🔮 GraphiQL 🔮

An in-browser IDE for exploring GraphQL.

⚙️ 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

Let's do!

Download

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

Install

npm install -g @angular/cli@latest

Create Folders

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

# Create sub-folder for our server
mkdir graphql-todo-server && cd graphql-todo-server
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

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.

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

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"
  },
// ...

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`)
);

Add "Hello World" GraphQL Schema

// src/schema.graphql

type Query {
  helloWorld: String!
}

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
});

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


http://localhost:8080/graphql

"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

Install Angular CLI and Create an App


# Create todo app (in the graphql-todo folder!)
ng new graphql-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 graphql-todo-app 

#start the application
npm start

Let's add Angular Material

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

# ? 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

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 { }

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.title }}
      </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.title }}
      </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.title }}
      </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.title }}
      </mat-checkbox>
    </mat-list-item>
  </mat-list>
</main>

Implement ToDO logic

// src/app/app.component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  todoItems = ['Go for a walk and  gym', 'Buy some bread and butter', 'Join Graphql session'];

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

Install Graphql Angular Client

# Install Apollo Angular
ng add apollo-angular

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 {}

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!
}

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
});

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
  }
}

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

Kahoot Quiz and QnA

Let's do a Recap

@kokkisajee

Github Repository of the Workshop: sajeetharan/cmbt-frontend-graphql (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."

 

I'm open to questions!

@kokkisajee

Thank you!

https://slides.com/sajeetharan/jamstack/

Unleash the power of JAMstack and GraphQL

By Sajeetharan Sinnathurai

Unleash the power of JAMstack and GraphQL

  • 1,183