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!
Build forms with GraphQL - React Europe 2019
By Charly Poly
Build forms with GraphQL - React Europe 2019
- 1,692