React-starter-kit: Best way to route React frontend POST request to GraphQL/SQLite3 DB

Created on 1 Feb 2018  路  5Comments  路  Source: kriasoft/react-starter-kit

I'm having trouble trying to figure out how to route a form submission from frontend through a GraphQL mutation and ultimately insert into a SQLite db table.

I've posted my question and code below. Is there any pointers someone could provide?

https://stackoverflow.com/questions/48553675/reactjs-post-with-express-graphql-and-sqlite3

Thanks

question

Most helpful comment

If you are on the feature/apollo branch, the general pattern is to use the graphql HOC with a graphql file as input. This will add a mutation function to your component props which you can call to fire your mutation.

Here's an example using ant-design's form lib + Apollo File Uploads to make it interesting + React Apollo

./CoolFormComponent.js

CoolFormComponent

import React from 'react';
import PropTypes from 'prop-types';
import withStyles from 'isomorphic-style-loader/lib/withStyles';
import { graphql, compose } from 'react-apollo';
import s from './styles.css';

import myMutation from './graphql/myMutation.graphql';

import { Form, Input, Tooltip, Icon, Row, Col, Button, Upload } from 'antd';
const FormItem = Form.Item;
const Dragger = Upload.Dragger;

class CoolFormComponent extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      isLoading: false,
      fileList: [],
    };
  }

  // Validate fields and recieve values from the form after clicking submit
  handleSubmit = (e) => {
    e.preventDefault();
    this.props.form.validateFieldsAndScroll((err, values) => {
      if (!err) {
        console.log('Received values of form: ', values);

        this.sendMutation(values);
      }
    });
  }

  // Submit the mutation 
  sendMutation = (values) => {
    this.setState({
      isLoading: true,
      fileList: []
    });

    this.props.myMutation({
      variables: {
        name: values.name,
        image: this.state.fileList[0] || null
      }
    })
    .then(({ data }) => {
      console.log(data.myMutation);

      // Reset form fields
      this.props.form.resetFields();

      // Turn off loading status on submit button
      this.setState({
        isLoading: false,
      });
    }).catch((error) => {
      // Whoopsie, there was an error
      console.log('there was an error sending this mutation', error);
      console.log(error);

      // Add an error message to the name field
      this.props.form.setFields({
        name: {
          value: values.name,
          errors: [new Error(`An error has occured!.`)],
        },
      });

      // Stop the loading status of the submit button
      this.setState({
        isLoading: false,
      });
    });
  }

  render() {
    const { getFieldDecorator } = this.props.form;

    const formItemLayout = {
      labelCol: {
        xs: { span: 8 },
        sm: { span: 8 },
      },
      wrapperCol: {
        xs: { span: 16 },
        sm: { span: 16 },
      },
    };

    const tailFormItemLayout = {
      wrapperCol: {
        xs: {
          span: 20,
        },
        sm: {
          span: 20,
        },
      },
    };

    // Just some props for handling files
    const props = {
      accept: 'image/*',
      action: '/graphql',
      onRemove: (file) => {
        this.setState(({ fileList }) => {
          const index = fileList.indexOf(file);
          const newFileList = fileList.slice();
          newFileList.splice(index, 1);
          return {
            fileList: newFileList,
          };
        });
      },
      beforeUpload: (file, { target }) => {
        this.setState(({ fileList }) => ({
          fileList: [...fileList, file],
        }));
        return false;
      },
      fileList: this.state.fileList,
    };

    return (
      <Form onSubmit={this.handleSubmit} layout='inline'>
        <FormItem
          label={(
            <span>
            {contentType} Name&nbsp;
            <Tooltip title='A name field'>
              <Icon type="question-circle-o" />
            </Tooltip>
            </span>
          )}
        >
          {getFieldDecorator('name', {
            rules: [{ required: true, message: `Please enter a name!` }],
          })(
            <Input
              spellCheck={false}
            />
          )}
        </FormItem>

        <FormItem
          label={(
            <span>
            File Upload&nbsp;
            <Tooltip title={`Upload an image`}>
              <Icon type="question-circle-o" />
            </Tooltip>
            </span>
          )}
        >
          <Upload {...props}>
            <Button>
              <Icon type="upload" /> Click to Upload
            </Button>
          </Upload>
        </FormItem>

        <FormItem>
          <Button type="primary" htmlType="submit" size="large" loading={this.state.isLoading}>Submit Form</Button>
        </FormItem>
      </Form>
    );
  }
}

export default compose(
  Form.create(),
  withStyles(s),
  graphql(myMutation, {
    name: 'myMutation'
  }),
)(CoolFormComponent);

./graphql/myMutation.graphql

myMutation.graphql

mutation myMutation($name: String!, $image: Upload){
  myMutation(name: $name, image: $image) {
    id
    name
    logoURL
    updatedAt
  }
}






For a simpler walkthrough just checkout the docs https://www.apollographql.com/docs/react/basics/mutations.html

All 5 comments

If you are on the feature/apollo branch, the general pattern is to use the graphql HOC with a graphql file as input. This will add a mutation function to your component props which you can call to fire your mutation.

Here's an example using ant-design's form lib + Apollo File Uploads to make it interesting + React Apollo

./CoolFormComponent.js

CoolFormComponent

import React from 'react';
import PropTypes from 'prop-types';
import withStyles from 'isomorphic-style-loader/lib/withStyles';
import { graphql, compose } from 'react-apollo';
import s from './styles.css';

import myMutation from './graphql/myMutation.graphql';

import { Form, Input, Tooltip, Icon, Row, Col, Button, Upload } from 'antd';
const FormItem = Form.Item;
const Dragger = Upload.Dragger;

class CoolFormComponent extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      isLoading: false,
      fileList: [],
    };
  }

  // Validate fields and recieve values from the form after clicking submit
  handleSubmit = (e) => {
    e.preventDefault();
    this.props.form.validateFieldsAndScroll((err, values) => {
      if (!err) {
        console.log('Received values of form: ', values);

        this.sendMutation(values);
      }
    });
  }

  // Submit the mutation 
  sendMutation = (values) => {
    this.setState({
      isLoading: true,
      fileList: []
    });

    this.props.myMutation({
      variables: {
        name: values.name,
        image: this.state.fileList[0] || null
      }
    })
    .then(({ data }) => {
      console.log(data.myMutation);

      // Reset form fields
      this.props.form.resetFields();

      // Turn off loading status on submit button
      this.setState({
        isLoading: false,
      });
    }).catch((error) => {
      // Whoopsie, there was an error
      console.log('there was an error sending this mutation', error);
      console.log(error);

      // Add an error message to the name field
      this.props.form.setFields({
        name: {
          value: values.name,
          errors: [new Error(`An error has occured!.`)],
        },
      });

      // Stop the loading status of the submit button
      this.setState({
        isLoading: false,
      });
    });
  }

  render() {
    const { getFieldDecorator } = this.props.form;

    const formItemLayout = {
      labelCol: {
        xs: { span: 8 },
        sm: { span: 8 },
      },
      wrapperCol: {
        xs: { span: 16 },
        sm: { span: 16 },
      },
    };

    const tailFormItemLayout = {
      wrapperCol: {
        xs: {
          span: 20,
        },
        sm: {
          span: 20,
        },
      },
    };

    // Just some props for handling files
    const props = {
      accept: 'image/*',
      action: '/graphql',
      onRemove: (file) => {
        this.setState(({ fileList }) => {
          const index = fileList.indexOf(file);
          const newFileList = fileList.slice();
          newFileList.splice(index, 1);
          return {
            fileList: newFileList,
          };
        });
      },
      beforeUpload: (file, { target }) => {
        this.setState(({ fileList }) => ({
          fileList: [...fileList, file],
        }));
        return false;
      },
      fileList: this.state.fileList,
    };

    return (
      <Form onSubmit={this.handleSubmit} layout='inline'>
        <FormItem
          label={(
            <span>
            {contentType} Name&nbsp;
            <Tooltip title='A name field'>
              <Icon type="question-circle-o" />
            </Tooltip>
            </span>
          )}
        >
          {getFieldDecorator('name', {
            rules: [{ required: true, message: `Please enter a name!` }],
          })(
            <Input
              spellCheck={false}
            />
          )}
        </FormItem>

        <FormItem
          label={(
            <span>
            File Upload&nbsp;
            <Tooltip title={`Upload an image`}>
              <Icon type="question-circle-o" />
            </Tooltip>
            </span>
          )}
        >
          <Upload {...props}>
            <Button>
              <Icon type="upload" /> Click to Upload
            </Button>
          </Upload>
        </FormItem>

        <FormItem>
          <Button type="primary" htmlType="submit" size="large" loading={this.state.isLoading}>Submit Form</Button>
        </FormItem>
      </Form>
    );
  }
}

export default compose(
  Form.create(),
  withStyles(s),
  graphql(myMutation, {
    name: 'myMutation'
  }),
)(CoolFormComponent);

./graphql/myMutation.graphql

myMutation.graphql

mutation myMutation($name: String!, $image: Upload){
  myMutation(name: $name, image: $image) {
    id
    name
    logoURL
    updatedAt
  }
}






For a simpler walkthrough just checkout the docs https://www.apollographql.com/docs/react/basics/mutations.html

Hi @ahummel25, @tim-soft's answer is nice example.
If you need real POST endpoint (like form of REST), you should add express route for that and handle this manually, but it is redundant.. You can transform form data to GraphQL request of course, reusing all the auth and quota code if you have..

Great example @tim-soft

Wondering if you have run into any difficulties initializing values within the forms. Did you have to store your queried data into component state? Imo it seems a little excessive to have to parse your response data into a nested structure just to use and design's mapPropsToFields helper.

@bbtran antd's form lib works via HOC, the form values are available via props. You could obviously handle forms whichever way you'd like, I like doing it this way

@tim-soft thanks for the response!

Was this page helpful?
0 / 5 - 0 ratings