Slide 1

Slide 1 text

Frontend Development 2019 What's in your stack? @stefanjudis

Slide 2

Slide 2 text

STEFAN JUDIS @stefanjudis www.stefanjudis.com [email protected]

Slide 3

Slide 3 text

Frontend

Slide 4

Slide 4 text

Frontend 2011

Slide 5

Slide 5 text

Frontend today

Slide 6

Slide 6 text

Frontend today It became Engineering

Slide 7

Slide 7 text

Frontend today Engineering with new tools and practices every year...

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

What's in your stack 2019?

Slide 10

Slide 10 text

What's in your stack 2020?

Slide 11

Slide 11 text

What's in your stack 2021?

Slide 12

Slide 12 text

Highly opinionated...

Slide 13

Slide 13 text

I'm not covering religious questions...

Slide 14

Slide 14 text

Not covering religious questions It doesn't matter!

Slide 15

Slide 15 text

No content

Slide 16

Slide 16 text

No content

Slide 17

Slide 17 text

“ Nobody loves what prettier does to their syntax. Everyone loves what prettier does to their coworkers' syntax. "Grensley" on Reddit

Slide 18

Slide 18 text

No content

Slide 19

Slide 19 text

Ask yourself "Why?"

Slide 20

Slide 20 text

Solve a problem Keep you productive Make you happy A tool should...

Slide 21

Slide 21 text

medium.com/@dan_abramov/you-might-not-need-redux-be46360cf367

Slide 22

Slide 22 text

The web platform is a tool, too!

Slide 23

Slide 23 text

A big ecosystem change

Slide 24

Slide 24 text

No content

Slide 25

Slide 25 text

No content

Slide 26

Slide 26 text

Chromium Webkit Gecko

Slide 27

Slide 27 text

No content

Slide 28

Slide 28 text

We got more cross-browser supported features...

Slide 29

Slide 29 text

... but let's see where this goes.

Slide 30

Slide 30 text

The good stuff

Slide 31

Slide 31 text

Grid

Slide 32

Slide 32 text

www.stefanjudis.com/today-i-learned/

Slide 33

Slide 33 text

.o-list-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(20em, 1fr)); }

Slide 34

Slide 34 text

.o-list-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(20em, 1fr)); }

Slide 35

Slide 35 text

.o-list-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(20em, 1fr)); } .area-javascript { @media (min-width: 70em) { grid-row: 2 / 5; } }

Slide 36

Slide 36 text

.o-list-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(20em, 1fr)); } .area-javascript { @media (min-width: 70em) { grid-row-start: 2; grid-row-end: 5; } }

Slide 37

Slide 37 text

Grid support caniuse.com/#feat=css-grid

Slide 38

Slide 38 text

cssgrid.io

Slide 39

Slide 39 text

font-display

Slide 40

Slide 40 text

www.zachleat.com/foitfout/

Slide 41

Slide 41 text

@font-face { font-family: "Open Sans Regular"; font-weight: 400; font-style: normal; src: url("fonts/OpenSans-Regular-BasicLatin.woff2") format("woff2"); // browser default behaviour font-display: auto; }

Slide 42

Slide 42 text

@font-face { font-family: "Open Sans Regular"; font-weight: 400; font-style: normal; src: url("fonts/OpenSans-Regular-BasicLatin.woff2") format("woff2"); // browser default behaviour // font-display: auto; // fallback font first // -> custom font when available font-display: swap; }

Slide 43

Slide 43 text

@font-face { font-family: "Open Sans Regular"; font-weight: 400; font-style: normal; src: url("fonts/OpenSans-Regular-BasicLatin.woff2") format("woff2"); // browser default behaviour // font-display: auto; // fallback font first // -> custom font when available // font-display: swap; // invisible until custom font is loaded (FOIT deluxe) font-display: block; }

Slide 44

Slide 44 text

@font-face { font-family: "Open Sans Regular"; font-weight: 400; font-style: normal; src: url("fonts/OpenSans-Regular-BasicLatin.woff2") format("woff2"); // browser default behaviour // font-display: auto; // fallback font first // -> custom font when available // font-display: swap; // invisible until custom font is loaded (FOIT deluxe) // font-display: block; // invisible very short then fallback font // -> custom font when available font-display: fallback; }

Slide 45

Slide 45 text

font-display support caniuse.com/#feat=css-font-rendering-controls

Slide 46

Slide 46 text

No content

Slide 47

Slide 47 text

www.zachleat.com/web/comprehensive-webfonts/

Slide 48

Slide 48 text

:focus-within

Slide 49

Slide 49 text

li:focus-within { background: #ddd; }

Slide 50

Slide 50 text

No content

Slide 51

Slide 51 text

:focus-within support caniuse.com/#feat=css-focus-within

Slide 52

Slide 52 text

Observer power

Slide 53

Slide 53 text

const options = { threshold: 0 };

Slide 54

Slide 54 text

const options = { threshold: 0 }; const intersectionObserver = new IntersectionObserver((entries) => { entries.forEach((entry) => { if (entry.isIntersecting) { const {target} = entry; target.src = target.dataset.src; intersectionObserver.unobserve(target); } }); }, options);

Slide 55

Slide 55 text

const options = { threshold: 0 }; const intersectionObserver = new IntersectionObserver((entries) => { entries.forEach((entry) => { if (entry.isIntersecting) { const {target} = entry; target.src = target.dataset.src; intersectionObserver.unobserve(target); } }); }, options); [...document.querySelectorAll('.lazyload')] .forEach(elem => intersectionObserver.observe(elem));

Slide 56

Slide 56 text

const options = { threshold: 0 }; const intersectionObserver = new IntersectionObserver((entries) => { entries.forEach((entry) => { if (entry.isIntersecting) { const {target} = entry; target.src = target.dataset.src; intersectionObserver.unobserve(target); } }); }, options); [...document.querySelectorAll('.lazyload')] .forEach(elem => intersectionObserver.observe(elem)); Congratulations, that's lazy loading!

Slide 57

Slide 57 text

Intersection Observer Mutation Observer Performance Observer Resize Observer Reporting
 Observer

Slide 58

Slide 58 text

No content

Slide 59

Slide 59 text

Speaking of lazy loading...

Slide 60

Slide 60 text

addyosmani.com/blog/lazy-loading/

Slide 61

Slide 61 text

loading support caniuse.com/#feat=loading-lazy-attr * * behind a flag * ** ** ** ** polyfillable

Slide 62

Slide 62 text

I'm a carousel image! The future is bright

Slide 63

Slide 63 text

I'm a carousel image! The future is bright Images will get super powers.

Slide 64

Slide 64 text

webp the winning image format?

Slide 65

Slide 65 text

jpeg (Q65) 399KB webp 121KB

Slide 66

Slide 66 text

bitsofco.de/why-and-how-to-use-webp-images-today/

Slide 67

Slide 67 text

My Image

Slide 68

Slide 68 text

webp support caniuse.com/#feat=webp * * "experimenting"

Slide 69

Slide 69 text

Evergreen browsers evolve quickly.

Slide 70

Slide 70 text

The next JavaScript

Slide 71

Slide 71 text

ESM (EcmaScript modules)

Slide 72

Slide 72 text

ES6 modules tryout

Slide 73

Slide 73 text

'use strict'; // strict mode by default

Slide 74

Slide 74 text

'use strict'; // strict mode by default console.log(this); // undefined

Slide 75

Slide 75 text

'use strict'; // strict mode by default console.log(this); // undefined const a = 1; // local to the script

Slide 76

Slide 76 text

'use strict'; // strict mode by default console.log(this); // undefined const a = 1; // local to the script import main from './main.js';

Slide 77

Slide 77 text

// loaded asynchronous // executed after finished HTML parsing 'use strict'; // strict mode by default console.log(this); // undefined const a = 1; // local to the script import main from './main.js';

Slide 78

Slide 78 text

ESM support caniuse.com/#feat=es6-module

Slide 79

Slide 79 text

www.contentful.com/.../es6-modules-support-lands-in-browsers-is-it-time-to-rethink-bundling/

Slide 80

Slide 80 text

www.contentful.com/.../es6-modules-support-lands-in-browsers-is-it-time-to-rethink-bundling/

Slide 81

Slide 81 text

No content

Slide 82

Slide 82 text

No content

Slide 83

Slide 83 text

No content

Slide 84

Slide 84 text

The future is there!

Slide 85

Slide 85 text

nodejs.org/api/esm.html

Slide 86

Slide 86 text

The power of types

Slide 87

Slide 87 text

www.typescriptlang.org

Slide 88

Slide 88 text

www.typescriptlang.org "Who needs that?"

Slide 89

Slide 89 text

No content

Slide 90

Slide 90 text

export async function migration (migrationCreator: Function): Promise { // ... } The async function migration accepts the argument migrationCreator of type Function and returns a Promise that resolves with a collection of type Intent.

Slide 91

Slide 91 text

export async function migration (migrationCreator: Function, makeRequest: Function): Promise { // ... } test/unit/bin/lib/config.spec.ts test/unit/lib/content-types-in-chunks.spec.ts test/unit/lib/deleted-ct-entries.spec.ts test/unit/lib/fetcher.spec.ts test/unit/lib/intent-list/intent-list.spec.ts src/lib/migration-steps/index.ts ...

Slide 92

Slide 92 text

github.com/webpack/webpack/pull/6862

Slide 93

Slide 93 text

No content

Slide 94

Slide 94 text

Take all the safety and help you can get for large scale applications.

Slide 95

Slide 95 text

PWAs will become the standard

Slide 96

Slide 96 text

Reliable Fast Engaging

Slide 97

Slide 97 text

The holy "service worker"

Slide 98

Slide 98 text

jakearchibald.github.io/isserviceworkerready/

Slide 99

Slide 99 text

self.addEventListener('fetch', function(event) { event.respondWith( caches.match(event.request) .then(function(response) { // Cache hit - return response if (response) { return response; } return fetch(event.request); } ) ); });

Slide 100

Slide 100 text

developers.google.com/web/tools/workbox/

Slide 101

Slide 101 text

importScripts('https://storage.googleapis.com/.../workbox-sw.js'); workbox.routing.registerRoute( new RegExp('(/|.*\.css)'), workbox.strategies.networkFirst() );

Slide 102

Slide 102 text

importScripts('https://storage.googleapis.com/.../workbox-sw.js'); workbox.routing.registerRoute( new RegExp('(/|.*\.css)'), workbox.strategies.networkFirst() );

Slide 103

Slide 103 text

Know how it works but use what is out there.

Slide 104

Slide 104 text

No content

Slide 105

Slide 105 text

The web truly becomes an application platform.

Slide 106

Slide 106 text

GraphQL

Slide 107

Slide 107 text

“ Backend Engineer: Hmm. So you’re saying this “GraphQL” will allow any web or native engineer to arbitrarily query basically any field in any backend service, recursively, however they want, without any backend engineers involved? Frontend Engineer: Yeah, right? It’s amazing! […silence…] Backend Engineer: Guards, seize this person. medium.com/airbnb-engineering/reconciling-graphql-and-thrift-at-airbnb-a97e8d290712

Slide 108

Slide 108 text

No content

Slide 109

Slide 109 text

One request per resource

Slide 110

Slide 110 text

No content

Slide 111

Slide 111 text

POST query { course (id: "1toEOumnkEksWakieoeC6M") { fields { title slug skillLevel } } ... } { "data": { "course": { "fields": { "title": "Hello Contentful", "slug": "hello-contentful", "skillLevel": "beginner" } } ... } }

Slide 112

Slide 112 text

POST query { course (id: "1toEOumnkEksWakieoeC6M") { fields { title slug skillLevel } } ... } { "data": { "course": { "fields": { "title": "Hello Contentful", "slug": "hello-contentful", "skillLevel": "beginner" } } ... } } One request for everything

Slide 113

Slide 113 text

No content

Slide 114

Slide 114 text

github.com/prisma/graphql-playground

Slide 115

Slide 115 text

API

Slide 116

Slide 116 text

No content

Slide 117

Slide 117 text

No content

Slide 118

Slide 118 text

No content

Slide 119

Slide 119 text

As an API provider life becomes easier.

Slide 120

Slide 120 text

GraphQL will change the way we work completely.

Slide 121

Slide 121 text

GraphQL will lower the barrier for Frontend developers.

Slide 122

Slide 122 text

New approach to static sites

Slide 123

Slide 123 text

SSG Build tool content & templates CSS & JavaScript

Slide 124

Slide 124 text

SSG Build tool content & templates CSS & JavaScript

Slide 125

Slide 125 text

SSG Build tool content & templates CSS & JavaScript Constant context switch

Slide 126

Slide 126 text

SSG Build tool content & templates CSS & JavaScript

Slide 127

Slide 127 text

SSG Build tool content & templates CSS & JavaScript

Slide 128

Slide 128 text

SSG Build tool content & templates CSS & JavaScript API data has to be written to disk

Slide 129

Slide 129 text

SSG Build tool content & templates CSS & JavaScript

Slide 130

Slide 130 text

No content

Slide 131

Slide 131 text

Universal JavaScript Apps (completely JS driven)

Slide 132

Slide 132 text

No content

Slide 133

Slide 133 text

Rich ecosystem Unified code base Cutting edge technologies Advantages Developer tooling Static HTML

Slide 134

Slide 134 text

www.contentful.com/blog/2018/04/11/new-era-static-sites-rise-future/

Slide 135

Slide 135 text

No content

Slide 136

Slide 136 text

No content

Slide 137

Slide 137 text

www.gatsbyjs.org/blog/2018-10-04-journey-to-the-content-mesh

Slide 138

Slide 138 text

Every website is a web app and every web app is a website.

Slide 139

Slide 139 text

#0CJS (Zero-Config JavaScript)

Slide 140

Slide 140 text

Bundlers

Slide 141

Slide 141 text

Executive webpack configuration engineer Senior webpack config manager

Slide 142

Slide 142 text

No content

Slide 143

Slide 143 text

parceljs.org

Slide 144

Slide 144 text

Demo

Slide 145

Slide 145 text

No content

Slide 146

Slide 146 text

Works also with Typescript, Sass, ...

Slide 147

Slide 147 text

No content

Slide 148

Slide 148 text

Toolkits

Slide 149

Slide 149 text

Improved DX

Slide 150

Slide 150 text

Improved DX

Slide 151

Slide 151 text

{ "name": "hello-world-react", "version": "0.1.0", "private": true, "dependencies": { "react": "^16.4.0", "react-dom": "^16.4.0", "react-scripts": "3.0.0" }, "scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test --env=jsdom", "eject": "react-scripts eject" } } { "name": "hello-world-vue", "version": "0.1.0", "private": true, "scripts": { "serve": "vue-cli-service serve", "build": "vue-cli-service build", "lint": "vue-cli-service lint" }, "dependencies": { "vue": "^2.5.16" }, "devDependencies": { "@vue/cli-plugin-babel": "^3.0.0-beta.15", "@vue/cli-plugin-eslint": "^3.0.0-beta.15", "@vue/cli-service": "^3.0.0-beta.15", "vue-template-compiler": "^2.5.16" }, }

Slide 152

Slide 152 text

{ "name": "hello-world-react", "version": "0.1.0", "private": true, "dependencies": { "react": "^16.4.0", "react-dom": "^16.4.0", "react-scripts": "3.0.0" }, "scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test --env=jsdom", "eject": "react-scripts eject" } } { "name": "hello-world-vue", "version": "0.1.0", "private": true, "scripts": { "serve": "vue-cli-service serve", "build": "vue-cli-service build", "lint": "vue-cli-service lint" }, "dependencies": { "vue": "^2.5.16" }, "devDependencies": { "@vue/cli-plugin-babel": "^3.0.0-beta.15", "@vue/cli-plugin-eslint": "^3.0.0-beta.15", "@vue/cli-service": "^3.0.0-beta.15", "vue-template-compiler": "^2.5.16" }, }

Slide 153

Slide 153 text

{ "name": "hello-world-react", "version": "0.1.0", "private": true, "dependencies": { "react": "^16.4.0", "react-dom": "^16.4.0", "react-scripts": "1.1.4" }, "scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test --env=jsdom", "eject": "react-scripts eject" } } { "name": "hello-world-vue", "version": "0.1.0", "private": true, "scripts": { "serve": "vue-cli-service serve", "build": "vue-cli-service build", "lint": "vue-cli-service lint" }, "dependencies": { "vue": "^2.5.16" }, "devDependencies": { "@vue/cli-plugin-babel": "^3.0.0-beta.15", "@vue/cli-plugin-eslint": "^3.0.0-beta.15", "@vue/cli-service": "^3.0.0-beta.15", "vue-template-compiler": "^2.5.16" }, } A single dependency

Slide 154

Slide 154 text

github.com/reyronald/awesome-toolkits

Slide 155

Slide 155 text

Good tools should just work, but should also be configurable.

Slide 156

Slide 156 text

Easy to use automation tools

Slide 157

Slide 157 text

Testing

Slide 158

Slide 158 text

“ I love writing end-to-end tests! said no one ever...

Slide 159

Slide 159 text

www.cypress.io

Slide 160

Slide 160 text

No content

Slide 161

Slide 161 text

www.youtube.com/watch?v=zTHQqiu_y0Q

Slide 162

Slide 162 text

Utilities

Slide 163

Slide 163 text

Wrong or broken links

Slide 164

Slide 164 text

Does anyone remember this friend?

Slide 165

Slide 165 text

pptr.dev

Slide 166

Slide 166 text

const puppeteer = require('puppeteer'); const URL = 'https://www.stefanjudis.com/useful-talk-quotes/'; (async() => { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto(URL, {waitUntil: 'networkidle0'}); await browser.close(); })();

Slide 167

Slide 167 text

const puppeteer = require('puppeteer'); const URL = 'https://www.stefanjudis.com/useful-talk-quotes/'; (async() => { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto(URL, {waitUntil: 'networkidle0'}); // evaluate URLs let urls = await page.evaluate(_ => { }); await browser.close(); })();

Slide 168

Slide 168 text

const puppeteer = require('puppeteer'); const URL = 'https://www.stefanjudis.com/useful-talk-quotes/'; (async() => { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto(URL, {waitUntil: 'networkidle0'}); // evaluate URLs let urls = await page.evaluate(_ => { return [... [...document.querySelectorAll('a')] .reduce((acc, {href}) => acc.add(href) && acc, new Set()) ]; }); await browser.close(); })();

Slide 169

Slide 169 text

const puppeteer = require('puppeteer'); const URL = 'https://www.stefanjudis.com/useful-talk-quotes/'; (async() => { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto(URL, {waitUntil: 'networkidle0'}); // evaluate URLs let urls = await page.evaluate(_ => { return [... [...document.querySelectorAll('a')] .reduce((acc, {href}) => acc.add(href) && acc, new Set()) ]; }); await browser.close(); })();

Slide 170

Slide 170 text

const puppeteer = require('puppeteer'); const URL = 'https://www.stefanjudis.com/useful-talk-quotes/'; (async() => { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto(URL, {waitUntil: 'networkidle0'}); // evaluate URLs let urls = await page.evaluate(_ => { return [... [...document.querySelectorAll('a')] .reduce((acc, {href}) => acc.add(href) && acc, new Set()) ]; }); await browser.close(); })();

Slide 171

Slide 171 text

const puppeteer = require('puppeteer'); const URL = 'https://www.stefanjudis.com/useful-talk-quotes/'; (async() => { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto(URL, {waitUntil: 'networkidle0'}); // evaluate URLs let urls = await page.evaluate(_ => { /* ... */ }); await browser.close(); })();

Slide 172

Slide 172 text

const puppeteer = require('puppeteer'); const fetch = require('node-fetch'); const URL = 'https://www.stefanjudis.com/useful-talk-quotes/'; (async() => { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto(URL, {waitUntil: 'networkidle0'}); // evaluate URLs let urls = await page.evaluate(_ => { /* ... */ }); // map URLs to include status code urls = await Promise.all(urls.map(href => { return new Promise(async resolve => { const {status} = await fetch(href); resolve({ url: href, status }); }) })); await browser.close(); })();

Slide 173

Slide 173 text

const puppeteer = require('puppeteer'); const fetch = require('node-fetch'); const URL = 'https://www.stefanjudis.com/useful-talk-quotes/'; (async() => { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto(URL, {waitUntil: 'networkidle0'}); // evaluate URLs let urls = await page.evaluate(_ => { /* ... */ }); // map URLs to include status code urls = await Promise.all(urls.map(href => { /* ... */ })); await browser.close(); })();

Slide 174

Slide 174 text

const puppeteer = require('puppeteer'); const fetch = require('node-fetch'); const URL = 'https://www.stefanjudis.com/useful-talk-quotes/'; (async() => { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto(URL, {waitUntil: 'networkidle0'}); // evaluate URLs let urls = await page.evaluate(_ => { /* ... */ }); // map URLs to include status code urls = await Promise.all(urls.map(href => { /* ... */ })); // filter invalid URLs const invalidLinks = urls .filter(href => href.status !== 200) .map(({url}) => url) await browser.close(); })();

Slide 175

Slide 175 text

const puppeteer = require('puppeteer'); const fetch = require('node-fetch'); const URL = 'https://www.stefanjudis.com/useful-talk-quotes/'; (async() => { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto(URL, {waitUntil: 'networkidle0'}); // evaluate URLs let urls = await page.evaluate(_ => { /* ... */ }); // map URLs to include status code urls = await Promise.all(urls.map(href => { /* ... */ })); // filter invalid URLs const invalidLinks = urls .filter(href => href.status !== 200) .map(({url}) => url) if (invalidLinks.length) { console.error(`Found invalid links: \n ${invalidLinks.join('\n')}`); } else { console.log('All cool!'); } await browser.close(); })();

Slide 176

Slide 176 text

No content

Slide 177

Slide 177 text

github.com/GoogleChromeLabs/puppeteer-examples

Slide 178

Slide 178 text

No content

Slide 179

Slide 179 text

github.com/GoogleChromeLabs/puppeteer-examples/blob/master/side-by-side-pageload.js

Slide 180

Slide 180 text

Automate everything and spend your time with fun stuff.

Slide 181

Slide 181 text

"Serverless"

Slide 182

Slide 182 text

“ You can take your front-end skills and do things that typically only a back-end can do. Chris Coyier

Slide 183

Slide 183 text

No content

Slide 184

Slide 184 text

No content

Slide 185

Slide 185 text

No content

Slide 186

Slide 186 text

There's a service for that!

Slide 187

Slide 187 text

Deploy sites easily

Slide 188

Slide 188 text

No content

Slide 189

Slide 189 text

Netlify

Slide 190

Slide 190 text

Function as a Service

Slide 191

Slide 191 text

No servers to maintain Automatically upscaling No payment for idle time

Slide 192

Slide 192 text

serverlesscalc.com

Slide 193

Slide 193 text

exports.sayHello = async (event) => { return 'Hello from Lambda!'; };

Slide 194

Slide 194 text

exports.sayHello = async (event) => { return 'Hello from Lambda!'; }; Lambda

Slide 195

Slide 195 text

Lambda exports.sayHello = async (event) => { return 'Hello from Lambda!'; }; API Gateway GET /.../eu-central-1.amazonaws.com/prod/ POST /.../eu-central-1.amazonaws.com/prod/ PUT /.../eu-central-1.amazonaws.com/prod/ ...

Slide 196

Slide 196 text

Lambda API Gateway GET /.../eu-central-1.amazonaws.com/prod/ POST /.../eu-central-1.amazonaws.com/prod/ PUT /.../eu-central-1.amazonaws.com/prod/ ... exports.sayHello = async (event) => { return { statusCode: 200, body: JSON.stringify({ "msg": "Hello from Lambda!" }) }; };

Slide 197

Slide 197 text

No content

Slide 198

Slide 198 text

No content

Slide 199

Slide 199 text

Can be tedious

Slide 200

Slide 200 text

Google AWS Microsoft IBM AWS

Slide 201

Slide 201 text

Google AWS Microsoft IBM AWS

Slide 202

Slide 202 text

service: say-hello provider: name: aws runtime: nodejs8.10 functions: sayHello: handler: handler.sayHello events: - http: path: / method: get

Slide 203

Slide 203 text

service: say-hello provider: name: aws runtime: nodejs8.10 functions: sayHello: handler: handler.sayHello events: - http: path: / method: get handler.js exports.sayHello = async (event) => { return { statusCode: 200, body: JSON.stringify({ "msg": "Hello from Lambda!" }) }; }; serverless.yml

Slide 204

Slide 204 text

No content

Slide 205

Slide 205 text

Serverless weekends

Slide 206

Slide 206 text

module.exports.redirect = async ({ pathParameters }, context) => { };

Slide 207

Slide 207 text

module.exports.redirect = async ({ pathParameters }, context) => { try { } catch (e) { console.log(e); return { statusCode: 500, body: e.message }; } };

Slide 208

Slide 208 text

module.exports.redirect = async ({ pathParameters }, context) => { try { const { shortUrl } = pathParameters; const queryUrl = `https://cdn.contentful.com/spaces/${ process.env.SPACE_ID }/environments/master/entries?content_type=${ process.env.CONTENT_TYPE }&fields.shortUrl=${shortUrl}&access_token=${process.env.ACCESS_TOKEN}`; } catch (e) { console.log(e); return { statusCode: 500, body: e.message }; } };

Slide 209

Slide 209 text

const got = require('got'); module.exports.redirect = async ({ pathParameters }, context) => { try { const { shortUrl } = pathParameters; const queryUrl = `https://cdn.contentful.com/spaces/${ process.env.SPACE_ID }/environments/master/entries?content_type=${ process.env.CONTENT_TYPE }&fields.shortUrl=${shortUrl}&access_token=${process.env.ACCESS_TOKEN}`; const response = JSON.parse((await got(queryUrl)).body); const redirect = response.items[0]; return { statusCode: 301, headers: { Location: redirect.fields.targetUrl } }; } catch (e) { console.log(e); return { statusCode: 500, body: e.message }; } };

Slide 210

Slide 210 text

const got = require('got'); module.exports.redirect = async ({ pathParameters }, context) => { try { const { shortUrl } = pathParameters; const queryUrl = `https://cdn.contentful.com/spaces/${ process.env.SPACE_ID }/environments/master/entries?content_type=${ process.env.CONTENT_TYPE }&fields.shortUrl=${shortUrl}&access_token=${process.env.ACCESS_TOKEN}`; const response = JSON.parse((await got(queryUrl)).body); const redirect = response.items[0]; return { statusCode: 301, headers: { Location: redirect.fields.targetUrl } }; } catch (e) { console.log(e); return { statusCode: 500, body: e.message }; } }; www.my-links.online/ devday

Slide 211

Slide 211 text

const got = require('got'); module.exports.redirect = async ({ pathParameters }, context) => { try { const { shortUrl } = pathParameters; const queryUrl = `https://cdn.contentful.com/spaces/${ process.env.SPACE_ID }/environments/master/entries?content_type=${ process.env.CONTENT_TYPE }&fields.shortUrl=${shortUrl}&access_token=${process.env.ACCESS_TOKEN}`; const response = JSON.parse((await got(queryUrl)).body); const redirect = response.items[0]; return { statusCode: 301, headers: { Location: redirect.fields.targetUrl } }; } catch (e) { console.log(e); return { statusCode: 500, body: e.message }; } }; www.my-links.online/ devday (yes, I bought this domain)

Slide 212

Slide 212 text

twitter.com/randomMDN

Slide 213

Slide 213 text

twitter.com/randomMDN You can schedule functions!!!

Slide 214

Slide 214 text

Don't be afraid your admins won't give you enough access to be dangerous.

Slide 215

Slide 215 text

Functions are everywhere!

Slide 216

Slide 216 text

Connect all the dots (the power of web hooks)

Slide 217

Slide 217 text

“ Web hooks are the API of the web. Phil Hawksworth

Slide 218

Slide 218 text

? ?

Slide 219

Slide 219 text

? ? module.exports.handleDiscourse = (event, context, callback) => { };

Slide 220

Slide 220 text

? ? module.exports.handleDiscourse = (event, context, callback) => { const post = JSON.parse(event.body).post; };

Slide 221

Slide 221 text

? ? module.exports.handleDiscourse = (event, context, callback) => { const post = JSON.parse(event.body).post; if (post && event.headers['X-Discourse-Event'] !== 'post_edited') { const { name, topic_title, topic_slug, topic_id, post_number } = post; } };

Slide 222

Slide 222 text

? ? module.exports.handleDiscourse = (event, context, callback) => { const post = JSON.parse(event.body).post; if (post && event.headers['X-Discourse-Event'] !== 'post_edited') { const { name, topic_title, topic_slug, topic_id, post_number } = post; // send it to Slack // ... // ... } };

Slide 223

Slide 223 text

https://www.contentful.com/slack/

Slide 224

Slide 224 text

? ?

Slide 225

Slide 225 text

? ?

Slide 226

Slide 226 text

No content

Slide 227

Slide 227 text

"Just write a Lambda function"

Slide 228

Slide 228 text

8-bit-revolution.netlify.com/

Slide 229

Slide 229 text

require('dotenv').config(); const { TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN, CONTACT_NUMBERS, BOT_NUMBER, BOT_MESSAGE } = process.env; const client = require('twilio')(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN); exports.handler = function(event, context, callback) { Promise.all( CONTACT_NUMBERS.split(';').map(num => { return client.messages.create({ from: BOT_NUMBER, to: num, body: BOT_MESSAGE }); }) ) .then(() => callback(null, { statusCode: 200, body: 'Created' })) .catch(e => callback(e)); };

Slide 230

Slide 230 text

youtu.be/vXJpOHz3_sY

Slide 231

Slide 231 text

Spend your time building your business and connect all the dots.

Slide 232

Slide 232 text

Have fun!

Slide 233

Slide 233 text

thepowerofserverless.info

Slide 234

Slide 234 text

codepen.io/chriscoyier/project/editor/ZepgLg

Slide 235

Slide 235 text

codesandbox.io

Slide 236

Slide 236 text

That is VSCode running in your browser!

Slide 237

Slide 237 text

New awareness

Slide 238

Slide 238 text

No content

Slide 239

Slide 239 text

webhint.io

Slide 240

Slide 240 text

No content

Slide 241

Slide 241 text

Let's build good products!

Slide 242

Slide 242 text

My wish

Slide 243

Slide 243 text

No content

Slide 244

Slide 244 text

class AppDrawer extends HTMLElement {...} window.customElements.define('app-drawer', AppDrawer); Custom Elements

Slide 245

Slide 245 text

No content

Slide 246

Slide 246 text

No content

Slide 247

Slide 247 text

Slide 248

Slide 248 text

No content

Slide 249

Slide 249 text

Let's bet on our platform!

Slide 250

Slide 250 text

How to stay up-to-date?

Slide 251

Slide 251 text

Newsletters

Slide 252

Slide 252 text

www.stefanjudis.com/staying-up-to-date/

Slide 253

Slide 253 text

webplatform.news

Slide 254

Slide 254 text

GitHub

Slide 255

Slide 255 text

github.com/Fyrd/caniuse/

Slide 256

Slide 256 text

github.com/mdn/browser-compat-data/

Slide 257

Slide 257 text

www.chromestatus.com/features

Slide 258

Slide 258 text

Don't worry about falling behind...

Slide 259

Slide 259 text

No content

Slide 260

Slide 260 text

You can't be always cutting-edge.

Slide 261

Slide 261 text

Use what works for you!

Slide 262

Slide 262 text

THANKS FOR LISTENING www.my-links.online/devday
 @stefanjudis