Build forms with GraphQL

#whoami

Charly POLY - Senior Software Engineer at

#whoami

I

Single Page Applications

Front-end development

❤️

Single Page Applications

State management

Crypto

Offline capabilities

Routing

Rendering

PWA

Single Page Applications

Forms    =

manual and repetitive task

Forms: glue between UI and APIs

// Render Prop
import React from 'react';
import { Formik, Form, Field, ErrorMessage } from 'formik';
import { CustomInputComponent, CustomPasswordComponent } from './common/components';
import { UserService } from './services/UserService';

const Basic = () => (
    <Formik
      validate={values => {
        let errors = {};
        if (!values.email) {
          errors.email = 'Required';
        } else if (
          !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(values.email)
        ) {
          errors.email = 'Invalid email address';
        }
        return errors;
      }}
      onSubmit={(values, { setSubmitting }) => UserService.update(values, setSubmitting) }
    >
      {({ isSubmitting }) => (
        <Form>
          <Field type="email" name="email" component={CustomInputComponent} />
          <ErrorMessage name="email" component="div" />
          <Field type="password" name="password" component={CustomPasswordComponent} />
          <ErrorMessage name="password" component="div" />
          <button type="submit" disabled={isSubmitting}>
            Submit
          </button>
        </Form>
      )}
    </Formik>
);

export default Basic;

Forms: glue between UI and APIs

Forms: glue between UI and APIs

"Forms handle the experience that users have with data"

Story: Improving forms development

Speeding up form development

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

    constructor() {
        super('UserForm', 'user');
    }
}

Speeding up form development

Speeding up form development

  • Too many abstractions
  • Not flexible
  • Not the "React way"

Not the proper solution

Leveraging GraphQL
to improve form development

What is GraphQL?

HTTP requests

SPA

GraphQL API

What is GraphQL?

GraphQL Schema

Data types

Queries

Mutations

GraphQL API

How to leverage GraphQL?

GraphQL mutation design

mutation(user: UserInputType!, company: CompanyInputType) {
    create_account(user: $user, company: $company) {
        user {
            id
        }
    }
}

How to leverage GraphQL?

1. GraphQL mutations are based on business logic

mutation

Onboarding
Form

GraphQL API

User model

Company model

How to leverage GraphQL?

introspection

query

GraphQL API

2. GraphQL introspection

{
  "name": "createAccount",
  "__typename": "__Field",
  "isDeprecated": false,
  "deprecationReason": null,
  "args": [
    {
      "name": "user",
      "type": {
        "kind": "NON_NULL",
        "name": null,
         "ofType": {
            "kind": "InputType",
            "name": "UserInputType",
            "ofType": null,
            "__typename": "__Type"
          },
        "__typename": "__Type"
      },
      "defaultValue": null,
      "__typename": "__InputValue"
    },
  // ...
}

How to leverage GraphQL?

2. GraphQL introspection

introspection
query

GraphQL API

You can create an Account by providing
a mandatory User type and
an optional Company type

GraphQL for forms

Mutation               =

   similar to Form UI

Mutation name    =

     fields + requirements

GraphQL for forms

<Frontier />

<Frontier />

Simplicity

Frontier forms

Simplicity

<Frontier mutation={mutation} client={client} initialValues={{ user: { email: 'hello@charlypoly.com ' } }}>
  {
    ({ state, modifiers, form }) => {
      return (
        <form onSubmit={modifiers.save}>
          <h2>Create a user</h2>
          <p>
            <label htmlFor="name">Name*</label> <br />
            <input
              type="text"
              name="name"
              value={state.values.user.name} onChange={modifiers.user.name.change}
            />
            {
              state.errors.user && state.errors.user.name &&
              <p>
                Error: "{state.errors.user.name}"
              </p>
            }
          </p>
          <p>
            <input type="submit" value="Save" />
          </p>
        </form>
      )
    }
  }
</Frontier>

<Frontier />

Full data lifecycle management

Frontier forms

Full-data lifecycle management

GraphQL

final-form

Apollo GraphQL

Fields definitions

Form state

Save data

<Frontier />

UI-Kit

Frontier forms

UI-Kit

const mutation = gql`
  mutation ($user: UserInputType!) {
    createUser(user: $user) {
      ...User
    }
  }
`;

<Frontier mutation={mutation} client={client} uiKit={ApplicationUIkit} />

Frontier forms

UI-Kit

<Frontier />

Flexible

Frontier forms

Flexible

<Frontier client={client} mutation={mutation} uiKit={ApplicationUIkit}>
  {
    ({ form, kit }) => {
      return (
        <form className='ui form' onSubmit={(e) => { e.preventDefault(); form.submit(); }}>
          <div>
            {kit.company()}
          </div>
          <Message
            info
            header='Is my company already registered?'
            list={[
              'If your company is already registred under a Business plan, please do register using the Business form',
            ]}
          />
          <br />
          <br />
          <div>
            {kit.email()}
          </div>
          <br />
          <div>
            {kit.firstname()}
          </div>
          <br />
          <div>
            {kit.lastname()}
          </div>
          <p>
            <input type="submit" value="Save" className="ui button" />
          </p>
        </form>
      )
    }
  }
</Frontier>

Frontier forms

Flexible

Frontier forms

  • Full data lifecycle management: state, validation, save
  • UI-kit full rendering: bring a consistent experience to your users
  • Simple and iterative: choose your form development flow

A new form development experience

Frontier forms: demos

How to:

  • Define and use a UI-Kit for your application
  • Advanced use-cases
    (validations & complex types)

Frontier: built for the future

Frontier Core

Frontier React

Frontier Data

- Apollo GraphQL

- ajv
- final-form

Frontier: built for the future

  • Swagger support
  • More flavours: Vue.js, Angular and React Native
  • API improvements

frontier-forms.dev 👀

Thank you!

        We are hiring!
🔗    honest.engineering
        @whereischarly

        /wittydeveloper

Made with Slides.com