When a CMS is not enough

When a CMS is not enough

22725c2d3eb331146549bf0d5d3c050c?s=128

stefan judis

October 29, 2018
Tweet

Transcript

  1. 1.

    When a CMS is not enough Tales from a content

    infrastructure @stefanjudis
  2. 2.

    Stefan Judis Frontend Developer, Occasional Teacher, Meetup Organizer ❤ Open

    Source, Performance and Accessibility ❤ @stefanjudis
  3. 22.
  4. 23.

    App development today 50% use 2-5 APIs 9% use 25+

    APIs Global Development Survey Vol. 1 © 2017 Evans Data
  5. 26.

    Platform as a Service (PaaS) Specialized Services on top of

    PaaS and IaaS Infrastructure as a Service (IaaS)
  6. 30.

    Platform as a Service (PaaS) Specialized Services on top of

    PaaS and IaaS Infrastructure as a Service (IaaS)
  7. 31.

    Platform as a Service (PaaS) Specialized Services on top of

    PaaS and IaaS Infrastructure as a Service (IaaS)
  8. 32.

    Sascha Konietzke Contentful Content Infrastructure is architected to deliver content

    to any channel or digital product, and allows frequent adjustments and iteration, by fitting into modern agile development practices.
  9. 36.

    A JSON API { "sys": { "contentType": { "sys": {

    "id": "page" } } }, "fields": { "title": "Page", "body"; "This is an article about ..." } }
  10. 38.
  11. 39.

    SCALABLE {} {} {} {} {} {} {} {} {}

    {} {} {} {} {} {} CONTENT DELIVERED BY FAST CDNs
  12. 44.

    STRUCTURED CONTENT GET JSON ? ? ? GET JSON Break

    down your content into reusable components!
  13. 48.

    Title Title Artist Artist Name Image Sigh no more Babel

    Mumford & Sons Mumford & Sons Mumford & Sons
  14. 49.

    Title Title Artist Artist Name Image Sigh no more Babel

    Mumford & Sons & Me Mumford & Sons & Me Mumford & Sons & Me
  15. 50.

    Title Title Artist Artist Name Image Sigh no more Babel

    Mumford & Sons & Me Mumford & Sons & Me Mumford & Sons & Me
  16. 51.

    Title Title Artist Artist Name Image Sigh no more Babel

    Mumford & Sons & Me Mumford & Sons & Me Mumford & Sons & Me
  17. 52.

    Title Title Artist Artist Name Image Sigh no more Babel

    Mumford & Sons & Me Mumford & Sons & Me Mumford & Sons & Me Every node of the graph has to be accessible (together & separate)
  18. 55.

    Name Image Mumford & Sons Description Mumford & Sons are

    a British band formed in 2007. The band consists of Marcus Mumford (lead vocals, electric guitar, acoustic guitar, drums), Ben Lovett (vocals, keyboard, piano, synthesizer), Winston Marshall (vocals, electric guitar, banjo) and Ted Dwane (vocals, bass guitar, double bass).
  19. 56.
  20. 57.
  21. 58.
  22. 60.
  23. 61.
  24. 63.

    DISADVANTAGES OF WYSIWYG Too flexible 01 Mixes content and looks

    02 03 Too easy to mess up 04 HTML is the goal
  25. 65.
  26. 66.

    # Heading ## Sub-heading Text attributes _italic_, **bold**, `monospace`. Bullet

    list: * apples * oranges * pears A [link](http://example.com). ![Image](duck.png) Heading Sub-heading Text attributes italic, bold, monospace. Bullet list: - apple - oranges - pears A link.
  27. 75.
  28. 77.

    THE BIGGEST PROBLEM Name Mumford & Sons Name Marcus Mumford

    Slug marcus-mumford Description Mumford & Sons are a British band formed in 2007. The band consists of Marcus Mumford (lead vocals, electric guitar, acoustic guitar, drums), Ben Lovett (vocals, keyboard, piano, synthesizer), Winston Marshall (vocals, electric guitar, banjo) and Ted Dwane (vocals, bass guitar, double bass). [Markus Mumford](http://.../artist/marcus-mumford)
  29. 78.

    THE BIGGEST PROBLEM Name Mumford & Sons Name Marcus Mumford

    Slug marcus-mumford-jr Description Mumford & Sons are a British band formed in 2007. The band consists of Marcus Mumford (lead vocals, electric guitar, acoustic guitar, drums), Ben Lovett (vocals, keyboard, piano, synthesizer), Winston Marshall (vocals, electric guitar, banjo) and Ted Dwane (vocals, bass guitar, double bass). [Markus Mumford](https://.../artist/marcus-mumford-jr) ?
  30. 79.

    THE BIGGEST PROBLEM Name Mumford & Sons Name Marcus Mumford

    Slug marcus-mumford-jr Description Mumford & Sons are a British band formed in 2007. The band consists of Marcus Mumford (lead vocals, electric guitar, acoustic guitar, drums), Ben Lovett (vocals, keyboard, piano, synthesizer), Winston Marshall (vocals, electric guitar, banjo) and Ted Dwane (vocals, bass guitar, double bass). [Markus Mumford](https://.../artist/marcus-mumford-jr) ? References are not possible in Markdown!
  31. 80.
  32. 81.

    Rich text JSON Name Mumford & Sons Description Mumford &

    Sons are a British band formed in 2007. The band consists of Marcus Mumford (lead vocals, electric guitar, acoustic guitar, drums), Ben Lovett (vocals, keyboard, piano, synthesizer), Winston Marshall (vocals, electric guitar, banjo) and Ted Dwane (vocals, bass guitar, double bass). { "data": {}, "content": [ ], "nodeType": "document" }
  33. 82.

    Rich text JSON Name Mumford & Sons Description Mumford &

    Sons are a British band formed in 2007. The band consists of Marcus Mumford (lead vocals, electric guitar, acoustic guitar, drums), Ben Lovett (vocals, keyboard, piano, synthesizer), Winston Marshall (vocals, electric guitar, banjo) and Ted Dwane (vocals, bass guitar, double bass). { "data": {}, "content": [ { "data": {}, "content": [ ], "nodeType": "paragraph" } ], "nodeType": "document" }
  34. 83.

    Rich text JSON Name Mumford & Sons Description Mumford &

    Sons are a British band formed in 2007. The band consists of Marcus Mumford (lead vocals, electric guitar, acoustic guitar, drums), Ben Lovett (vocals, keyboard, piano, synthesizer), Winston Marshall (vocals, electric guitar, banjo) and Ted Dwane (vocals, bass guitar, double bass). { "data": {}, "content": [ { "data": {}, "content": [ { "data": {}, "marks": [], "value": "Mumford & Sons ...", "nodeType": "text" } ], "nodeType": "paragraph" } ], "nodeType": "document" }
  35. 84.

    Rich text JSON const COMPONENT_RENDER_MAP = { paragraph: item =>

    <p>{item.content.map(item => renderItem(item))}</p>, "heading-1": item => <h1>{item.content.map(item => renderItem(item))}</h1>, "heading-2": item => <h2>{item.content.map(item => renderItem(item))}</h2>, // ... }; export const renderItem = item => { return COMPONENT_RENDER_MAP[item.nodeType](item); };
  36. 92.

    Name Image Mumford & Sons Name Image Mumford & Sons

    & Me JSON JSON DELIVERY API PREVIEW API
  37. 93.
  38. 97.
  39. 98.

    Name Date Contentful Certification Workshop Description Get your Contentful Certification.

    Join our one-day event, packed with workshops, practice sessions, and only a few, short presentations. October 29th, 2018 City Berlin Country Germany
  40. 99.

    Name Date Contentful Certification Workshop Description Get your Contentful Certification.

    Join our one-day event, packed with workshops, practice sessions, and only a few, short presentations. October 29th, 2018 City Berlin Country Germany Setting up your content infrastructure will take you two minutes!
  41. 100.

    Name Date Contentful Certification Workshop ... Name Date JAMstack Conf

    ... Name Mumford & Sons Name Slug Blog page blog Name Date How to do Contentful? ... Name Date O'reilly Software architecture October 29th Name Lucy Rose
  42. 101.

    Name Date Contentful Certification Workshop ... Name Date JAMstack Conf

    ... Name Mumford & Sons Name Slug Blog page blog Name Date How to do Contentful? ... Name Date O'reilly Software architecture October 29th Name Lucy Rose At some point it will be time to refactor
  43. 109.

    cdn.contentful.com/spaces/{space_id}/environments/{environment_id}/entries const contentful = require('contentful'); const client = contentful.createClient({ space:

    '<space_id>', environment: '<environment_id>', accessToken: '<content_delivery_api_key>' }); client.getEntries();
  44. 110.

    COPIED Content types Entries Assets Locales UI-extensions Saved views SHARED

    Users & space memberships Roles & permissions API keys Web hooks
  45. 112.
  46. 115.

    MIGRATION CLI CONTENT TYPE OPERATIONS Create a content type 01

    02 03 04 05 Delete a content type Edit a content type Create/edit/delete fields Change a field ID
  47. 116.
  48. 117.

    Add new field module.exports = function (migration) { const tilPost

    = migration.editContentType('tilPost') tilPost.createField( 'tags', { name: 'Tags', type: 'Array', items: { type: 'Symbol' } } ); }
  49. 118.
  50. 119.

    Transform an entry in place 01 02 Derive an entry

    from another MIGRATION CLI CONTENT TRANSFORMATIONS
  51. 120.
  52. 121.
  53. 122.

    DERIVE ENTRIES AND LINK Name Fronttrends 2017 City Warsaw Country

    Poland Name Fronttrends 2018 City Warsaw Country Poland
  54. 123.

    DERIVE ENTRIES AND LINK Name Fronttrends 2017 City Warsaw Country

    Poland Name Fronttrends 2018 City Warsaw Country Poland Name Fronttrends City Warsaw Country Poland
  55. 124.

    DERIVE ENTRIES AND LINK Name Fronttrends 2017 Conference Name Fronttrends

    2018 Conference Name Fronttrends City Warsaw Country Poland Fronttrends Fronttrends
  56. 125.

    module.exports = function (migration) { const conference = migration.createContentType('conference') .name('Conference/Meetup')

    .displayField('name') conference.createField('name').type('Symbol').required(true).name('Conference/Meetup name') conference.createField('country').type('Symbol').required(true).name('Country Code') conference.createField('city').type('Symbol').required(true).name('City') const event = migration.editContentType('event') event.createField('conference') .name('Conference') .type('Link') .linkType('Entry') .validations([ { "linkContentType": ['conference'] } ]) migration.deriveLinkedEntries({ contentType: 'event', from: ['name', 'country', 'city'], toReferenceField: 'conference', derivedContentType: 'conference', derivedFields: ['name', 'country', 'city'], identityKey: async (from) => { return from.name['en-US'] // remove year .replace(/\s(\d{2,4}|#\d+)/g, '') // clear spaces .replace(/\s/g, '-') // clear "weird characters" .replace(/(,|\/|\\|:|\.|\(|\))/g, '') .toLowerCase() }, deriveEntryForLocale: async (inputFields, locale) => { return { name: inputFields.name[locale].replace(/\s(\d{2,4}|#\d+)/g, ''), country: inputFields.country[locale] || 'N/A', city: inputFields.city[locale] || 'N/A' } } }) event.moveField('conference').afterField('name') event.deleteField('country') event.deleteField('city') } LOC 48 REQUESTS 382 ENTRIES CREATED 88 ENTRIES UPDATED 96
  57. 126.

    MIGRATION CLI ADVANTAGES Repeatable 01 02 03 04 Can be

    kept in VC Includes sanity checks Perfect for CI
  58. 131.

    Deluan Quintao ThoughtWorks We ran 500 migrations this year. Our

    content structure is tightly coupled to the code changes we make.
  59. 132.
  60. 135.
  61. 136.

    Required APIs Content Delivery API Content Preview API staging production

    Content Management API automation Images API assets
  62. 145.

    Platform as a Service (PaaS) Specialized Services on top of

    PaaS and IaaS Infrastructure as a Service (IaaS)
  63. 150.

    POST ✅ Headers: content-type: application/json X-Algolia-Application-Id: xxx X-Algolia-API-key Body: {

    title: 'hello world', content: ' ...' } contentType.sys.id === 'post' + custom payload
  64. 151.

    POST ✅ Headers: content-type: application/json X-Algolia-Application-Id: xxx X-Algolia-API-key Body: {

    title: 'hello world', content: ' ...' } contentType.sys.id === 'post' + custom payload
  65. 152.

    ...

  66. 155.

    ...

  67. 156.
  68. 157.

    UI Extensions <!DOCTYPE html> <html> <head> <link rel="stylesheet" href="https://contentful.github.io/.../cf-extension.css"> <script

    src="https://unpkg.com/contentful-ui-extensions-sdk@3"></script> </head> <body> </body> </html>
  69. 158.

    UI Extensions <!DOCTYPE html> <html> <head> <link rel="stylesheet" href="https://contentful.github.io/.../cf-extension.css"> <script

    src="https://unpkg.com/contentful-ui-extensions-sdk@3"></script> </head> <body> <script> window.contentfulExtension.init(function ({ entry, field, window, parameters }) { // connect to other APIs // add your own logic and appearance // ... }); </script> </body> </html>
  70. 161.
  71. 167.

    167 1,6 % 3,4 % 5,5 % 6,5 % 7,4

    % 10 % 14 % 16 % 17 % 18 % HOW DO DEVELOPERS ASSESS POTENTIAL JOBS? insights.stackoverflow.com/survey/2018/#technology-and-society 1 1. Compensation and Benefits 2 2. Language, Frameworks and Technology 3 3. Professional Development
  72. 168.

    168 1,6 % 3,4 % 5,5 % 6,5 % 7,4

    % 10 % 14 % 16 % 17 % 18 % HOW DO DEVELOPERS ASSESS POTENTIAL JOBS? insights.stackoverflow.com/survey/2018/#technology-and-society 1 1. Compensation and Benefits 2 2. Language, Frameworks and Technology 3 3. Professional Development
  73. 169.

    169 1,6 % 3,4 % 5,5 % 6,5 % 7,4

    % 10 % 14 % 16 % 17 % 18 % HOW DO DEVELOPERS ASSESS POTENTIAL JOBS? insights.stackoverflow.com/survey/2018/#technology-and-society 1 1. Compensation and Benefits 2 2. Language, Frameworks and Technology 3 3. Professional Development Your CMS is part of that decision!
  74. 171.
  75. 172.

    “ 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…] medium.com/airbnb-engineering/reconciling-graphql-and-thrift-at-airbnb-a97e8d290712
  76. 173.
  77. 175.
  78. 176.

    POST query { course (id: "1toEOumnkEksWakieoeC6M") { fields { title

    slug skillLevel } } ... } { "data": { "course": { "fields": { "title": "Hello Contentful", "slug": "hello-contentful", "skillLevel": "beginner" } } ... } }
  79. 177.

    POST query { course (id: "1toEOumnkEksWakieoeC6M") { fields { title

    slug skillLevel } } ... } { "data": { "course": { "fields": { "title": "Hello Contentful", "slug": "hello-contentful", "skillLevel": "beginner" } } ... } } One request for everything
  80. 178.
  81. 189.
  82. 191.