Let’s build the future of forms with GraphQL

A presentation at Meetup React.js and React Native in January 2019 in Paris, France by Charly Poly

Slide 1

Slide 1

Let’s build the future of forms with GraphQL @whereischarly

Slide 2

Slide 2

#whoami Charly POLY - Senior Software Engineer at 🔗 Writing about Software Eng. on honest.engineering Writing about TypeScript and GraphQL on Medium @whereischarly

Slide 3

Slide 3

Forms on the web ecosystems @whereischarly

Slide 4

Slide 4

Forms on the web ecosystems Subject tackled by most ecosystems Rails simple_form Symfony Forms ASP.NET MVC @whereischarly

Slide 5

Slide 5

Forms on the web ecosystems Subject tackled by most ecosystems Rails simple_form Symfony Forms ASP.NET MVC Form as a Service TypeForm Google Forms @whereischarly

Slide 6

Slide 6

Forms on the web ecosystems Subject tackled by most ecosystems Rails simple_form Symfony Forms ASP.NET MVC Form as a Service TypeForm Google Forms “ @whereischarly What about SPA, JavaScript ecosystem forms?

Slide 7

Slide 7

Role of Forms in a Web Application “ @whereischarly Forms handle the experience that user have with data

Slide 8

Slide 8

Role of Forms in a Web Application “ Forms handle the experience that user have with data Developer Experience Be flexible, customisable Avoid validation divergence with back-end Handle state @whereischarly

Slide 9

Slide 9

Role of Forms in a Web Application “ Forms handle the experience that user have with data Developer Experience User Experience Be flexible, customisable Be the most precise as possible Avoid validation divergence with Make is easy and fast to fill complex back-end informations Handle state Immediate feedback @whereischarly

Slide 10

Slide 10

Role of Forms in a Web Application “ Forms handle the experience that user have with data Developer Experience User Experience Be flexible, customisable Be the most precise as possible Avoid validation divergence with Make is easy and fast to fill complex back-end informations Handle state Immediate feedback @whereischarly In short, fast and smooth experience

Slide 11

Slide 11

Current state of JS forms building Vue.js forms @whereischarly redux-form Formik

Slide 12

Slide 12

Benchmark: Vue.js forms @whereischarly

Slide 13

Slide 13

Benchmark: Vue.js forms What it solves: smart binding state related features ( lazy, numbers and debounce attributes ) (validations using vee-validate) @whereischarly

Slide 14

Slide 14

Benchmark: Vue.js forms What it solves: smart binding state related features ( lazy, numbers and debounce attributes ) (validations using vee-validate) What it doesn’t solve: almost everything is done by hand components, fields, state no Form state management no easily reusable non-standard validation format ☠ @whereischarly ☠ ✍

Slide 15

Slide 15

Benchmark: redux-form “ @whereischarly The best way to manage your form state in Redux.

Slide 16

Slide 16

Benchmark: redux-form “ The best way to manage your form state in Redux. What it solves: Error management Form state management Built-in components and helpers @whereischarly

Slide 17

Slide 17

Benchmark: redux-form “ The best way to manage your form state in Redux. What it solves: Error management Form state management Built-in components and helpers What it doesn’t solve: ✍ ✍ lot of configuration validators by hand forms state in global state Field by Field building workflow @whereischarly ☠ ✍

Slide 18

Slide 18

Benchmark: Formik “ @whereischarly Build forms in React, without the tears 😭

Slide 19

Slide 19

Benchmark: Formik “ Build forms in React, without the tears What it solves: Object-schema based validations with Yup Form state management (not global) “Functional” state management + render props pattern Built-in components and helpers @whereischarly 😭

Slide 20

Slide 20

Benchmark: Formik “ Build forms in React, without the tears What it solves: Object-schema based validations with Yup Form state management (not global) “Functional” state management + render props pattern Built-in components and helpers What it doesn’t solve: ✍ ✍ lot of configuration validators by hand non-standard validation format Field by Field building workflow @whereischarly ☠ ✍ 😭

Slide 21

Slide 21

Benchmark results Developer Experience Vue.js forms Manage state Data validation Theming Error messages Fields conf. @whereischarly ✍ ✍ ✍ ✍ ✍ redux-form ✅ ✍/✅ ✍ ✍ ✍ Formik ✅ ✅ ✍ ✍ ✍

Slide 22

Slide 22

Benchmark results Architecture Productivity Performance Flexibility Components Reusability Validation Isomorphism @whereischarly Vue.js forms redux-form ⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐ nodejs-only nodejs-only nodejs-only ⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐ Formik

Slide 23

Slide 23

Observation No matters the solution, we have to duplicate: fields list and definition data validations @whereischarly

Slide 24

Slide 24

What about types? The JavaScript eco-system new era: - TypeScript: typed JavaScript - GraphQL: typed data exchange @whereischarly

Slide 25

Slide 25

What about types? The JavaScript eco-system new era: - TypeScript: typed JavaScript - GraphQL: typed data exchange “ Since forms are about data, why not build typed forms? @whereischarly

Slide 26

Slide 26

Feedback: Dropping redux-form class Form extends ModuleForm { fields: FieldsDefinitions = { id: ‘none’, email: ‘none’, picture_path: { type: ‘image’, transformations: ‘h_200,w_200,r_max,c_fill’ }, first_name: ‘string*’, last_name: ‘string*’, username: ‘string*’, job_title: ‘string’, company_name: ‘string’, language: { type: ‘select*’, component: LanguageSelectView, valueProperty: ‘code’, values: supportedLanguages, moduleName: ‘attachment’ // TODO: fix. } }; constructor() { super(‘UserForm’, ‘user’); } }

Slide 27

Slide 27

Feedback: Dropping redux-form class Form extends ModuleForm { fields: FieldsDefinitions = { id: ‘none’, email: ‘none’, picture_path: { type: ‘image’, transformations: ‘h_200,w_200,r_max,c_fill’ }, first_name: ‘string*’, last_name: ‘string*’, username: ‘string*’, job_title: ‘string’, company_name: ‘string’, language: { type: ‘select*’, component: LanguageSelectView, valueProperty: ‘code’, values: supportedLanguages, moduleName: ‘attachment’ // TODO: fix. } }; constructor() { super(‘UserForm’, ‘user’); } } configured redux-form

Slide 28

Slide 28

Solution: Mozilla react-jsonschema-form “ A React component for building Web forms from JSON Schema. @whereischarly

Slide 29

Slide 29

Mozilla react-jsonschema-form What is JSON Schema? “A Media Type for Describing JSON Documents” { $schema: ‘http://json-schema.org/draft-06/schema#’, properties: { Todo: { type: ‘object’, properties: { id: { type: ‘string’ }, name: { type: ‘string’ }, completed: { type: ‘boolean’ }, color: { $ref: ‘#/definitions/Color’ }, }, required: [‘id’, ‘name’] } } interface Todo { id: String; name: String; completed: Boolean; color: Color; } } @whereischarly

Slide 30

Slide 30

Mozilla react-jsonschema-form

Slide 31

Slide 31

Mozilla react-jsonschema-form @whereischarly

Slide 32

Slide 32

Mozilla react-jsonschema-form 1. Provide a JSON Schema describing the data @whereischarly

Slide 33

Slide 33

Mozilla react-jsonschema-form 1. Provide a JSON Schema describing the data 2. Provide a JSON Schema describing the UI @whereischarly

Slide 34

Slide 34

Mozilla react-jsonschema-form 1. Provide a JSON Schema describing the data 2. Provide a JSON Schema describing the UI 3. Provide initial data @whereischarly

Slide 35

Slide 35

Mozilla react-jsonschema-form 1. Provide a JSON Schema describing the data 2. Provide a JSON Schema describing the UI 3. Provide initial data @whereischarly

Slide 36

Slide 36

Mozilla react-jsonschema-form 1. Provide a JSON Schema describing the data 2. Provide a JSON Schema describing the UI 3. Provide initial data Form React component @whereischarly

Slide 37

Slide 37

Mozilla react-jsonschema-form Architecture Developer Experience react-jsonschema-form Productivity Performance Flexibility Components Reusability Validation Isomorphism @whereischarly ⭐⭐ ⭐⭐⭐⭐ ⭐⭐ ⭐⭐ ⭐⭐ react-jsonschema-form Manage state Data validation Theming Error messages Fields conf. ✅ ✍ ✅ ✅ ✍

Slide 38

Slide 38

<ApolloForm> import import import import import

  • as React from ‘react’; gql from ‘graphql-tag’; { configure } from ‘react-apollo-form’; { client } from ‘./apollo’; { applicationFormTheme } from ‘./core/forms/themes/application’; const jsonSchema = require(‘./core/apollo-form-json-schema.json’); export const ApplicationForm = configure<ApolloFormMutationNames>({ // tslint:disable-next-line:no-any client: client as any, jsonSchema, theme: applicationFormTheme }); <ApplicationForm config={{ mutation: { name: ‘create_todo’, document: gqlmutation {...} } }} data={{}} /> @whereischarly

Slide 39

Slide 39

<ApolloForm> import import import import import

  • as React from ‘react’; gql from ‘graphql-tag’; { configure } from ‘react-apollo-form’; { client } from ‘./apollo’; { applicationFormTheme } from ‘./core/forms/themes/application’; const jsonSchema = require(‘./core/apollo-form-json-schema.json’); export const ApplicationForm = configure<ApolloFormMutationNames>({ // tslint:disable-next-line:no-any client: client as any, jsonSchema, theme: applicationFormTheme }); <ApplicationForm config={{ mutation: { name: ‘create_todo’, document: gqlmutation {...} } }} data={{}} /> @whereischarly

Slide 40

Slide 40

<ApolloForm> GraphQL mutations (fields + types)

  • <ApolloForm>

@whereischarly JSON Schema (validations)

Slide 41

Slide 41

<ApolloForm> flexible API import import import import import Schema options Theming options Error messages Callbacks Render props @whereischarly

  • as React from ‘react’; gql from ‘graphql-tag’; { configure } from ‘react-apollo-form’; { client } from ‘./apollo’; { applicationFormTheme } from ‘./core/forms/themes/application’; const jsonSchema = require(‘./core/apollo-form-json-schema.json’); export const ApplicationForm = configure<ApolloFormMutationNames>({ // tslint:disable-next-line:no-any client: client as any, jsonSchema, theme: applicationFormTheme }); <ApplicationForm config={ /* schema options / } data={{ / data / }} ui={ / UI options */ } />

Slide 42

Slide 42

<ApolloForm> flexible API Schema options @whereischarly

Slide 43

Slide 43

<ApolloForm> flexible API Schema options “Manual mode” @whereischarly <ApplicationForm title={‘Todo Form’} config={{ name: ‘todo’, schema: schema({ todo: { name: types.type(‘string’, { required: true completed: types.type(‘boolean’) } }), saveData: data => { console.log(‘save !’, data); } }} data={{}} ui={{}} />

Slide 44

Slide 44

<ApolloForm> flexible API Schema options “Mutation mode” @whereischarly <ApplicationForm config={{ mutation: { name: ‘create_todo’, document: gqlmutation {...} } }} data={{}} />

Slide 45

Slide 45

<ApolloForm> flexible API Schema options “Mutation mode” @whereischarly <ApplicationForm config={{ mutation: { name: ‘create_todo’, document: gqlmutation {...} }, ignoreFields: [‘user.image’], updateFields: { ‘user.phone_number’: { pattern: ‘(^[0-9+]{5,}$)|(^[a-zA-Z0-9]{0}$)’ } }, requiredFields: [‘user.email’] }} data={{}} />

Slide 46

Slide 46

<ApolloForm> flexible API Render props export interface ApolloRenderProps { // renderers header: () => React.ReactNode; // render the form header (title by default) form: () => React.ReactNode; // render the inputs buttons: () => React.ReactNode; // render save and cancel buttons saveButton: () => React.ReactNode; // render save button cancelButton: () => React.ReactNode; // render cancel button // actions cancel: () => void; // trigger a cancel save: (args: any) => void; // trigger a save // state isDirty: boolean; isSaved: boolean; hasError: boolean; data: any; } @whereischarly <ApplicationForm /* props options */ > { form => ( <div style={{ backgroundColor: ‘#FFF’, padding: ‘20px’ }}> <h2> My form! </h2> <br /> {form.form()} {form.saveButton()} </div> ) } </ApplicationForm>

Slide 47

Slide 47

<ApolloForm> flexible API Error messages By default, <ApolloForm> do not display errors. type ApolloFormUi = { // default: false, should we display errors list at the top ? showErrorsList?: boolean; // default: true, should we display errors at field level ? showErrorsInline?: boolean; // you can provide a custom component to display error list errorListComponent?: ErrorListComponent; }; @whereischarly To enable it, you should provide some options to ui prop.

Slide 48

Slide 48

<ApolloForm> flexible API Theming interface ApolloFormConfigureTheme { templates?: ApolloFormTheme[‘templates’]; widgets?: ApolloFormTheme[‘widgets’]; fields?: ApolloFormTheme[‘fields’]; renderers?: Partial<ApolloFormTheme[‘renderers’]>; } Templates render Fields and Widgets Renderers are <ApolloForm> specific: <saveButton> <cancelButton> <header> @whereischarly

Slide 49

Slide 49

<ApolloForm> internals @whereischarly

Slide 50

Slide 50

<ApolloForm> internals Build tools At runtime @whereischarly

Slide 51

Slide 51

<ApolloForm> internals Build tools At runtime @whereischarly GraphQL Schema

Slide 52

Slide 52

<ApolloForm> internals introspection Build tools At runtime @whereischarly GraphQL Schema

Slide 53

Slide 53

<ApolloForm> internals introspection Build tools At runtime @whereischarly GraphQL Schema JSON-Schema schema

Slide 54

Slide 54

<ApolloForm> internals introspection Build tools At runtime @whereischarly GraphQL Schema JSON-Schema schema

Slide 55

Slide 55

<ApolloForm> internals introspection Build tools At runtime @whereischarly GraphQL Schema JSON-Schema schema TypeScript Mutations enum

Slide 56

Slide 56

<ApolloForm> internals introspection Build tools GraphQL Schema JSON-Schema schema TypeScript Mutations enum Files genenations At runtime @whereischarly

Slide 57

Slide 57

<ApolloForm> internals introspection Build tools GraphQL Schema JSON-Schema schema TypeScript Mutations enum Files genenations At runtime @whereischarly JSON & TS files imported at runtime

Slide 58

Slide 58

<ApolloForm> internals introspection Build tools GraphQL Schema JSON-Schema schema TypeScript Mutations enum Files genenations At runtime @whereischarly JSON & TS files imported at runtime

Slide 59

Slide 59

<ApolloForm> internals introspection Build tools GraphQL Schema JSON-Schema schema TypeScript Mutations enum Files genenations At runtime @whereischarly configure a FormFactory against a schema JSON & TS files imported at runtime

Slide 60

Slide 60

<ApolloForm> internals introspection Build tools GraphQL Schema JSON-Schema schema TypeScript Mutations enum Files genenations At runtime @whereischarly configure a FormFactory against a schema JSON & TS files imported at runtime

Slide 61

Slide 61

<ApolloForm> internals introspection Build tools GraphQL Schema JSON-Schema schema TypeScript Mutations enum Files genenations At runtime @whereischarly building a mutation form using the FormFactory configure a FormFactory against a schema JSON & TS files imported at runtime

Slide 62

Slide 62

<ApolloForm> internals : Build tools react-apollo-form fetch-mutations <graphqlEndpoint> <outpurDir> introspection GraphQL Schema JSON-Schema schema

  • schema.json - mutations.d.ts - apollo-form-json-schema.json @whereischarly TypeScript Mutations enum

Slide 63

Slide 63

<ApolloForm> internals : Build tools What is an introspection query?

Slide 64

Slide 64

<ApolloForm> internals : Build tools graphql-2-json-schema package type Todo { id: String! name: String! completed: Boolean color: Color } input TodoInputType { name: String! completed: Boolean color: Color } type Mutation { update_todo(id: String!, todo: TodoInputType!): Todo create_todo(todo: TodoInputType!): Todo } @whereischarly { $schema: ‘http://json-schema.org/draft-06/schema#’, properties: { Mutation: { type: ‘object’, properties: { update_todo: { type: ‘object’, properties: { arguments: { type: ‘object’, properties: { id: { type: ‘string’ }, todo: { $ref: ‘#/definitions/ }, required: [‘id’, ‘todo’] }, return: { $ref: ‘#/definitions/Todo’ } }, required: [] }, // … } }, }, definitions: { ‘Todo’: { type: ‘object’,

Slide 65

Slide 65

<ApolloForm> internals : at runtime building a mutation form using the FormFactory @whereischarly configure a FormFactory against a schema JSON & TS files imported at runtime

Slide 66

Slide 66

<ApolloForm> internals : at runtime import import import import import

  • as React from ‘react’; gql from ‘graphql-tag’; { configure } from ‘react-apollo-form’; { client } from ‘./apollo’; { applicationFormTheme } from ‘./core/forms/themes/application’; const jsonSchema = require(‘./core/apollo-form-json-schema.json’); export const ApplicationForm = configure<ApolloFormMutationNames>({ // tslint:disable-next-line:no-any client: client as any, jsonSchema, theme: applicationFormTheme }); <ApplicationForm config={{ mutation: { name: ‘create_todo’, document: gqlmutation {...} } }} data={{}} /> @whereischarly

Slide 67

Slide 67

<ApolloForm> internals : at runtime import import import import import

  • as React from ‘react’; gql from ‘graphql-tag’; { configure } from ‘react-apollo-form’; { client } from ‘./apollo’; { applicationFormTheme } from ‘./core/forms/themes/application’; const jsonSchema = require(‘./core/apollo-form-json-schema.json’); export const ApplicationForm = configure<ApolloFormMutationNames>({ // tslint:disable-next-line:no-any client: client as any, jsonSchema, theme: applicationFormTheme }); <ApplicationForm config={{ mutation: { name: ‘create_todo’, document: gqlmutation {...} } }} data={{}} /> @whereischarly
  1. import definitions files

Slide 68

Slide 68

<ApolloForm> internals : at runtime import import import import import

  • as React from ‘react’; gql from ‘graphql-tag’; { configure } from ‘react-apollo-form’; { client } from ‘./apollo’; { applicationFormTheme } from ‘./core/forms/themes/application’; const jsonSchema = require(‘./core/apollo-form-json-schema.json’); export const ApplicationForm = configure<ApolloFormMutationNames>({ // tslint:disable-next-line:no-any client: client as any, jsonSchema, theme: applicationFormTheme }); <ApplicationForm config={{ mutation: { name: ‘create_todo’, document: gqlmutation {...} } }} data={{}} /> @whereischarly
  1. import definitions files 2. configure a Form Builder

Slide 69

Slide 69

<ApolloForm> internals : at runtime import import import import import

  • as React from ‘react’; gql from ‘graphql-tag’; { configure } from ‘react-apollo-form’; { client } from ‘./apollo’; { applicationFormTheme } from ‘./core/forms/themes/application’; const jsonSchema = require(‘./core/apollo-form-json-schema.json’); export const ApplicationForm = configure<ApolloFormMutationNames>({ // tslint:disable-next-line:no-any client: client as any, jsonSchema, theme: applicationFormTheme }); <ApplicationForm config={{ mutation: { name: ‘create_todo’, document: gqlmutation {...} } }} data={{}} /> @whereischarly
  1. import definitions files 2. configure a Form Builder 3. Instantiate a Form

Slide 70

Slide 70

<ApolloForm> internals : at runtime @whereischarly

Slide 71

Slide 71

<ApolloForm> internals : at runtime @whereischarly

Slide 72

Slide 72

<ApolloForm> internals : at runtime Your application Provide mutation name + options @whereischarly

Slide 73

Slide 73

<ApolloForm> internals : at runtime Your application <ApolloForm> Provide mutation name + options get json-schema for mutation + transform UI props @whereischarly

Slide 74

Slide 74

<ApolloForm> internals : at runtime Your application <ApolloForm> Provide mutation name + options get json-schema for mutation + transform UI props @whereischarly react-jsonschema-form handle rendering + validations

Slide 75

Slide 75

<ApolloForm> benchmark @whereischarly

Slide 76

Slide 76

<ApolloForm> benchmark react-jsonschema-form provides: Extendable, with the fields and widgets system. State management and validation, handled with local state and local validation using ajv. @whereischarly

Slide 77

Slide 77

<ApolloForm> benchmark react-jsonschema-form provides: Extendable, with the fields and widgets system. State management and validation, handled with local state and local validation using ajv. <ApolloForm> bring the last 2 key features: API/Client Interoperability: generate the form JSON Schema from a mutation definition Simplicity: provide some helpers to avoid the complexity of JSON Schema and enhance the extensibility of react-jsonschema-form. @whereischarly

Slide 78

Slide 78

Conclusion Developer Experience Vue.js forms Manage state Data validation Theming Error messages Fields conf. @whereischarly ✍ ✍ ✍ ✍ ✍ redux-form Formik react-jsonschema-form ✅ ✍ ✍ ✍ ✍ ✅ ✅ ✍ ✍ ✍ ✅ ✅ ✅ ✅ ✍ <ApolloForm> ✅ ✅ ✅ ✅ ✅

Slide 79

Slide 79

Conclusion Architecture Vue.js forms redux-form Formik react-jsonschema-form <ApolloForm> Productivity Performance Flexibility Components Reusability Validation Isomorphism @whereischarly ⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐ nodejs-only nodejs-only nodejsonly ⭐⭐ ⭐⭐⭐⭐ ⭐⭐ ⭐⭐ ⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐

Slide 80

Slide 80

Thanks for listening! Still in beta, looking for collaborators ➡ react-apollo-form 🔗 honest.engineering @whereischarly /wittydeveloper