Content Management in the JAMStack

A presentation at JAMstack Conf in October 2018 in San Francisco, CA, USA by Shy Ruparel

Slide 1

Slide 1

Content Management in the JAMStack By Shy Ruparel

Slide 2

Slide 2

Shy Ruparel Developer Evangelist | Contentful @ShyRuparel He/Him

Slide 3

Slide 3

3 Let’s talk about Content Management

Slide 4

Slide 4

COMMON CMS PROBLEMS PAGE-CENTRIC MODEL MONOLYTHIC SOLUTION SLOW ITERATIONS

Slide 5

Slide 5

COMMON CMS PROBLEMS WYSIWYG

Slide 6

Slide 6

Slide 7

Slide 7

Slide 8

Slide 8

DON'T BREAK SEPARATION OF CONCERNS Content vs. Look

Slide 9

Slide 9

Html

Slide 10

Slide 10

Html

Slide 11

Slide 11

Whenever you put HTML in your content you're Html breaking portability!

Slide 12

Slide 12

DISADVANTAGES OF WYSIWYG 01 02 03 04 Too flexible Mixes content and looks Too easy to mess up HTML is the goal

Slide 13

Slide 13

COMMON CMS PROBLEMS You can build your own CMS, but...

Slide 14

Slide 14

I don't want to do that!

Slide 15

Slide 15

I don't want to do that! I don't have time for that!

Slide 16

Slide 16

Don’t let a CMS get in the way of shipping software. Contentful provides a content infrastructure that enables teams to power content in any digital product.

Slide 17

Slide 17

CONTENT AT SCALE STRUCTURED CONTENT GOOD EDITING EXPERIENCE SPEEDY DEVELOPMENT

Slide 18

Slide 18

Our APIs Content Delivery API production Content Preview API staging Content Management API Images API automation assets

Slide 19

Slide 19

Let’s make a Website

Slide 20

Slide 20

So it’s time for a small tangent

Slide 21

Slide 21

Slide 22

Slide 22

www.ti4.nyc TI4.NYC

Slide 23

Slide 23

Slide 24

Slide 24

www.ti4.nyc Let’s dig into the About Page

Slide 25

Slide 25

I define a Content Model

Slide 26

Slide 26

Sean creates content

Slide 27

Slide 27

A JSON API { } "sys": { "contentType": { "sys": { "id": "component" } } }, "fields": { "title": "About", "slug": "about", "pageOrder": 1, "image": { "sys": { "id": "6BwngCuyHKkS28sKIimomM" } }, "copy": "Twilight Imperium is a monstrous game..." }

Slide 28

Slide 28

Our APIs Content Delivery API production Content Preview API staging Content Management API Images API automation assets

Slide 29

Slide 29

Our APIs Content Delivery API production

Slide 30

Slide 30

GATSBY-CONFIG.JS require("dotenv").config({ path: .env, }) module.exports = { plugins: [{ resolve: gatsby-source-contentful, options: { spaceId: process.env.spaceId, accessToken: process.env.accessToken, host: process.env.host }, }, ], }

Slide 31

Slide 31

Pull in the data

Slide 32

Slide 32

RENDER THE PAGE { this.props.cfData.map(componentEntry => ( <article id={componentEntry.node.slug} className={${this.props.article === componentEntry.node.slug ? "active" : ""} ${ this.props.articleTimeout ? "timeout" : "" }} style={{ display: "none" }} > <h2 className="major">{componentEntry.node.title}</h2> <span className="image main"> <img src={componentEntry.node.image.file.url} alt="" /> </span> <div dangerouslySetInnerHTML={{ __html: componentEntry.node.copy.childMarkdownRemark.html }} /> {close} </article> )); }

Slide 33

Slide 33

Build the site

Slide 34

Slide 34

Slide 35

Slide 35

Slide 36

Slide 36

Webhooks

Slide 37

Slide 37

Slide 38

Slide 38

Browser Static Site Host Build Server Content Infrastructure Sean Source Control Shy

Slide 39

Slide 39

Browser Static Site Host Build Server Content Infrastructure Editors Source Control Developers

Slide 40

Slide 40

www.ti4.nyc GITHUB.COM/SHY/ TI4.NYC-WEBSITE

Slide 41

Slide 41

Shy Ruparel Developer Evangelist | Contentful @ShyRuparel He/Him Slides: https://noti.st/shy Background Images: wocintechchat.com

Slide 42

Slide 42

Editor Workflows Developer Workflows

Slide 43

Slide 43

Production Preview Approval Editor Workflow QA Local Dev Dev Workflow Staging Draft Word / Google Docs

Slide 44

Slide 44

Production Preview Approval Editor Workflow QA Local Dev Dev Workflow Staging Draft Word / Google Docs

Slide 45

Slide 45

Improving the Editor Experience

Slide 46

Slide 46

Improving Sean’s Experience

Slide 47

Slide 47

My Editors want A Custom Field Type

Slide 48

Slide 48

Slide 49

Slide 49

Contentful doesn’t have a built in TI4 game tracker

Slide 50

Slide 50

So I built one

Slide 51

Slide 51

Let’s take a look at UI-Extensions

Slide 52

Slide 52

Slide 53

Slide 53

Let’s dig into The extension

Slide 54

Slide 54

STANDARD JSON OBJECT FIELD UI EXTENSION

Slide 55

Slide 55

Inspect in GraphiQL

Slide 56

Slide 56

Slide 57

Slide 57

CONFIGURE THE EXTENSION { } "id": "ti4MatchLog", "name": "Twilight Imperium 4th Edition Match Log", "srcdoc": "./index.html", "fieldTypes": ["Object"]

Slide 58

Slide 58

BUILD A FORM <body> <div id="content"> <div class="player"> <div class="cf-form-field inline"> <label>Players</label> <input type="text" value="Player 1 Name" class="cf-form-input inline" id="player1Name"> </div> <div class="cf-form-field inline"> <label>Points</label> <input type="integer" value="1" class="cf-form-input inline" id="player1Score"> </div> <div class="cf-form-field inline"> <label>Factions</label> <select class="cf-form-input" id="player1Faction"> <option value="Arborec" selected>Arborec</option> <option value="Barony">Barony of Letnev</option> </select> </div> </div>

Slide 59

Slide 59

SET THE DATA $(".cf-form-input").on("input", function() { extension.field.setValue(getGameData()); });

Slide 60

Slide 60

SET THE DATA function getGameData() { var data = {}; data["Players"] = []; var innerData = {}; var i = 0; for (; i < document.getElementsByClassName("cf-form-input").length; i += 1) { switch (i % 3) { case 0: innerData = {}; innerData["Name"] = document.getElementsByClassName( "cf-form-input")[i].value; break; case 1: innerData["Score"] = document.getElementsByClassName( "cf-form-input")[i].value; break; case 2: innerData["Faction"] = document.getElementsByClassName( "cf-form-input")[i].value; data["Players"].push(innerData); break; } } return (data); }

Slide 61

Slide 61

Push the code up to Contentful

Slide 62

Slide 62

Set a custom apparence for the JSON Object

Slide 63

Slide 63

And that’s it

Slide 64

Slide 64

RENDER THAT DATA {outerElement.node.games.map(innerElement => ( <React.Fragment> <h4>{innerElement.signTableNumber}</h4> <table> <thead> <tr> <th>Name</th> <th>Faction</th> <th>Score</th> </tr> </thead> <tbody> {innerElement.game.Players.map(playerElement => ( <tr> <td>{playerElement.Name}</td> <td> {playerElement.Faction} </td> <td>{playerElement.Score}</td> </tr> ))} </tbody> </table> </React.Fragment> ))}

Slide 65

Slide 65

www.ti4.nyc Game Log

Slide 66

Slide 66

Slide 67

Slide 67

www.ti4.nyc GITHUB.COM/SHY/ TI4.NYC-UI-EXTENSION

Slide 68

Slide 68

Slide 69

Slide 69

Slide 70

Slide 70

BE FRIENDS WITH OTHER VENDORS!

Slide 71

Slide 71

Slide 72

Slide 72

A rich ecosystem is indispensable in the world we live in

Slide 73

Slide 73

My Editors want Preview

Slide 74

Slide 74

COMMON CMS PROBLEMS WYSIWYG

Slide 75

Slide 75

Slide 76

Slide 76

Our APIs Content Delivery API production Content Preview API staging Content Management API Images API automation assets

Slide 77

Slide 77

Our APIs Content Preview API staging

Slide 78

Slide 78

Let’s take a look at Contentful Preview API

Slide 79

Slide 79

www.contentful.com PREVIEW.CONTENTFUL.COM

Slide 80

Slide 80

Preview API Key

Slide 81

Slide 81

Slide 82

Slide 82

Slide 83

Slide 83

Slide 84

Slide 84

Slide 85

Slide 85

Webhooks For Production

Slide 86

Slide 86

Webhooks For Preview

Slide 87

Slide 87

Create Draft Content

Slide 88

Slide 88

www.preview.ti4.nyc PREVIEW.TI4.NYC

Slide 89

Slide 89

Slide 90

Slide 90

YOUR EDITORS HAVE TO BE HAPPY (AND SAFE)

Slide 91

Slide 91

Slide 92

Slide 92

Improving the Developer Experience

Slide 93

Slide 93

Improving My Experience

Slide 94

Slide 94

www.ti4.nyc LET’S START WITH THE BACKGROUND IMAGE

Slide 95

Slide 95

Slide 96

Slide 96

Dealing with Assets

Slide 97

Slide 97

Our APIs Content Delivery API production Content Preview API staging Content Management API Images API automation assets

Slide 98

Slide 98

Our APIs Images API assets

Slide 99

Slide 99

3.2MB https:!//images.ctfassets.net/!!.../bg.jpg

Slide 100

Slide 100

140KB https:!//images.ctfassets.net/!!.../bg.jpg?w=800

Slide 101

Slide 101

30KB https:!//images.ctfassets.net/!!.../bg.jpg?w=800&fm=webp

Slide 102

Slide 102

17KB https:!//images.ctfassets.net/!!.../bg.jpg?w=800&fm=jpg&q=10

Slide 103

Slide 103

https:!//images.ctfassets.net/!!.../bg.jpg?w=800&h=200&fit=crop

Slide 104

Slide 104

Slide 105

Slide 105

Let’s talk about one of the laws of Software Engineering

Slide 106

Slide 106

Nobody gets it right the first time

Slide 107

Slide 107

When you can’t have perfection, the next best thing is change

Slide 108

Slide 108

Slide 109

Slide 109

Let's talk about the Contentful Migrations

Slide 110

Slide 110

Our APIs Content Delivery API production Content Preview API staging Content Management API Images API automation assets

Slide 111

Slide 111

Our APIs Content Management API automation

Slide 112

Slide 112

MIGRATION CLI CONTENT TYPE OPERATIONS 01 02 03 04 05 Create a content type Delete a content type Edit a content type Create/edit/delete fields Change a field ID

Slide 113

Slide 113

ALL IN JAVASCRIPT module.exports = function runMigration(migration) { const table = migration.editContentType("table"); Table .createField("winner") .name("winner") .type("Symbol") .required(false); return; };

Slide 114

Slide 114

CALLABLE VIA THE CONTENTFUL CLI

Slide 115

Slide 115

Slide 116

Slide 116

Let's do something A little more complicated

Slide 117

Slide 117

TRANSFORMING ENTRIES module.exports = function runMigration(migration) { const table = migration.editContentType("table"); table .createField("winner") .name("winner") .type("Symbol") .required(false); migration.transformEntries({ contentType: 'table', from: ['game'], to: ['winner'], transformEntryForLocale: function(fromFields, currentLocale) { var winner = ""; fromFields.game[currentLocale].Players.forEach(function(player) { if (player['Score'] == 10) { winner = player['Name']; } }); console.log(winner); return { winner: winner }; } }); return; };

Slide 118

Slide 118

Slide 119

Slide 119

Programatic Content Model Changes

Slide 120

Slide 120

MIGRATION CLI ADVANTAGES 01 Can be kept in VC 02 Includes sanity checks 03 Perfect for CI 04 Repeatable

Slide 121

Slide 121

CMS as Code (automation like a boss)

Slide 122

Slide 122

Slide 123

Slide 123

That’s Great

Slide 124

Slide 124

But what if I want to Discard Test or Preview a Migration?

Slide 125

Slide 125

125 Space Environments

Slide 126

Slide 126

Slide 127

Slide 127

MIGRATIONS + ENVIRONMENTS

Slide 128

Slide 128

GATSBY-CONFIG.JS require("dotenv").config({ path: .env, }) module.exports = { plugins: [{ resolve: gatsby-source-contentful, options: { spaceId: process.env.spaceId, accessToken: process.env.accessToken, host: process.env.host }, }, ], }

Slide 129

Slide 129

GATSBY-CONFIG.JS WITH ENVIRONMENTS require("dotenv").config({ path: .env, }) module.exports = { plugins: [{ resolve: gatsby-source-contentful, options: { spaceId: process.env.spaceId, accessToken: process.env.accessToken, host: process.env.host, environments: process.env.environments }, }, ], }

Slide 130

Slide 130

www.contentful.com COMBINE WITH NETLIFY FOR DEV PREVIEW

Slide 131

Slide 131

Combine with Netlify for Dev Preview

Slide 132

Slide 132

COMMON USAGE FOR SPACE ENVIRONMENTS Local development • Staging / QA • Continuous integration •

Slide 133

Slide 133

Let’s briefly dig into Continuous Integration

Slide 134

Slide 134

BASIC CI PIPELINE Build Test Deploy

Slide 135

Slide 135

Slide 136

Slide 136

MIGRATIONS + CI PIPELINE Build Test Deploy

Slide 137

Slide 137

MIGRATIONS + CI PIPELINE Build Create a new environment Test Deploy

Slide 138

Slide 138

MIGRATIONS + CI PIPELINE Build Create a new environment Migrate new environment Test Deploy

Slide 139

Slide 139

MIGRATIONS + CI PIPELINE Build Create a new environment Migrate new environment Test Deploy Migrate master environment

Slide 140

Slide 140

Slide 141

Slide 141

Slide 142

Slide 142

Slide 143

Slide 143

www.ti4.nyc GITHUB.COM/CONTENTFUL-LABS/ CONTINOUS-DELIVERYENVIRONMENTS-EXAMPLE

Slide 144

Slide 144

Slide 145

Slide 145

Editor Workflows Developer Workflows

Slide 146

Slide 146

Slide 147

Slide 147

Slide 148

Slide 148

Shy Ruparel Developer Evangelist | Contentful @ShyRuparel He/Him Slides: GitHub.com/shy/talks Background Images: wocintechchat.com