<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Bayu Syaits</title>
    <description>The latest articles on DEV Community by Bayu Syaits (@bayusyaits).</description>
    <link>https://dev.to/bayusyaits</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F802985%2F7f186001-0b4d-495a-8ee5-2d3c79ce3d7e.jpg</url>
      <title>DEV Community: Bayu Syaits</title>
      <link>https://dev.to/bayusyaits</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bayusyaits"/>
    <language>en</language>
    <item>
      <title>Using Ant Design and React for Editable Cells Implementation and Table</title>
      <dc:creator>Bayu Syaits</dc:creator>
      <pubDate>Thu, 02 Nov 2023 09:26:07 +0000</pubDate>
      <link>https://dev.to/bayusyaits/using-antd-and-react-for-editable-cells-implementation-and-table-2b5m</link>
      <guid>https://dev.to/bayusyaits/using-antd-and-react-for-editable-cells-implementation-and-table-2b5m</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3xgw9q0viie3g1zapsgi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3xgw9q0viie3g1zapsgi.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hello developer, here I'm going to share how to make editable cells using Ant design (antd). Antd was developed by the Alibaba Group. It's one of the most popular UI frameworks for web applications and React-based applications. Antd provides many UI components that developers can use when building web applications. It helps developers to make the UI they create consistent and interesting. We can read the documentation on the official website of ant.design.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prerequisite&lt;/strong&gt;&lt;br&gt;
There are necessary requirements to create this application.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;ReactJS (here I am using version 18.2.0) &lt;/li&gt;
&lt;li&gt;Antd (when writing this, I was using version 4) Defining Dummy Data Code below is an example of the data to display&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Defining Dummy Data &lt;br&gt;
Code below is an example of the data to be displayed&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const data = {
    content: [
      {
        id: 1,
        name: 'John Sr',
        age: 60,
        address: 'Sudirman No 1, Jakarta Selatan',
      },
      {
        id: 2,
        name: 'Lennon Jr',
        age: 32,
        address: 'Kemanggisan No 3, Jakarta Barat',
      },
    ]
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;*&lt;em&gt;Defining Column *&lt;/em&gt;&lt;br&gt;
After knowing the response data to be displayed, then we need to define the column according to the data parameters.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const columns = [
    {
      title: 'Name',
      dataIndex: 'name',
    },
    {
      title: 'Age',
      dataIndex: 'age',
    },
    {
      title: 'Address',
      dataIndex: 'address',
    },
  ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Implement EditableCell &amp;amp; EditableContext&lt;/strong&gt;&lt;br&gt;
Next we can take advantage of some hooks in ReactJs on the &lt;code&gt;Context.tsx&lt;/code&gt; file.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/components/home/table/Context.tsx

import { createContext, useContext, useEffect, useRef, useState } from 'react';
import { validateInput } from '../../../utils/validate';
import { Form, Input } from 'antd';

export const EditableContext: any = createContext(null);
export const EditableCell = (props: any) =&amp;gt; {
  const {
    title,
    editable,
    children,
    dataIndex,
    record,
    index,
    suffix,
    validate,
    handleSave,
    success,
    setSuccess,
    handleValidate,
    ...restProps
  } = props
  const [editing, setEditing] = useState(false);
  const inputRef: any = useRef(null);
  const form: any = useContext(EditableContext);
  const content = restProps?.data?.content || [];
  useEffect(() =&amp;gt; {
    if (editing) {
      inputRef.current.focus();
    }
  }, [editing]);
  const toggleEdit = (val: any) =&amp;gt; {
    setEditing(!editing);
    const value = val.target.value;
    form.setFieldsValue({
      [dataIndex]: value,
    });
  };
  const save = async (e: any) =&amp;gt; {
    try {
      toggleEdit(e);
      if (e.target.value) {
        const val = await form.validateFields();
        await handleSave({
          ...record,
          ...val,
        });
      }
      const suc = success;
      const bool = await handleValidate(
        index,
        record,
        dataIndex,
        e.target.value
      );
      if (index !== undefined) {
        suc[index] = bool;
        setSuccess(suc);
      }
    } catch (errInfo) {
      //
    }
  };
  let errorMsg = null;
  let validateStatus: '' | 'success' | 'warning' | 'error' | 'validating' = '';
  if (
    validate &amp;amp;&amp;amp;
    validate.length &amp;amp;&amp;amp;
    validate[index] &amp;amp;&amp;amp;
    validate[index]?.[dataIndex]?.errorMsg
  ) {
    errorMsg = validate[index]?.[dataIndex]?.errorMsg;
    validateStatus = validate[index]?.[dataIndex]?.validateStatus;
  }
  let childNode = children;
  const value =
    children &amp;amp;&amp;amp; Array.isArray(children) &amp;amp;&amp;amp; children.length
      ? children[children.length - 1]
      : '';
  if (editable) {
    childNode = editing ? (
      &amp;lt;Form.Item
        style={{
          margin: 0,
        }}
        name={dataIndex}
        rules={[
          {
            required: true,
            message: `${title} is required.`,
          },
        ]}
      &amp;gt;
        &amp;lt;Input
          style={{
            width: '100%',
          }}
          max={suffix === 'th' ? 100 : undefined}
          placeholder="0"
          suffix={suffix}
          ref={inputRef}
          disabled={restProps?.disabled}
          onBlur={save}
          onKeyDown={(e) =&amp;gt;
            validateInput(e, () =&amp;gt; {}, {
              minLength: 1,
              maxLength: suffix === 'th' ? 2 : undefined,
              allowDecimal: suffix === 'th',
              isNumber: true,
            })
          }
        /&amp;gt;
      &amp;lt;/Form.Item&amp;gt;
    ) : (
      &amp;lt;Form.Item
        style={{
          margin: 0,
        }}
        help={errorMsg}
        validateStatus={validateStatus}
      &amp;gt;
        &amp;lt;Input
          className="editable-cell-value-wrap"
          style={{
            paddingRight: 24,
          }}
          onFocus={toggleEdit}
          value={value}
          disabled={restProps?.isDisabled}
        /&amp;gt;
      &amp;lt;/Form.Item&amp;gt;
    );
  }

  if (dataIndex === 'rangeOmzetUpper' &amp;amp;&amp;amp; index === content.length - 1) {
    childNode = &amp;lt;&amp;gt;&amp;lt;/&amp;gt;;
  }

  return &amp;lt;td {...restProps}&amp;gt;{childNode}&amp;lt;/td&amp;gt;;
};

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;There are 5 hooks that I use to make this application, namely:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;createContext&lt;/code&gt; is used to create a context object that allows components in the React application to share data without having to pass the prop from component to component. In the above code, I use it on the variable &lt;code&gt;EditableContext&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;useContext&lt;/code&gt; is used for accessing the data provided by the context object created with &lt;code&gt;createContext&lt;/code&gt;. It allows the components to take values from the context. I place it on a variable form to access the values of &lt;code&gt;EditableContext&lt;/code&gt;. &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;useEffect&lt;/code&gt; is used in dealing with side effects in the component React. In my example, it's used to detect changes in the variable editing.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;useRef&lt;/code&gt; is used to make references to the DOM element or other values in the component. I use useRef to access and manipulate the input element directly, triggering it if there are changes to the editing variable.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;useState&lt;/code&gt; is used to create state variables in functional components. With &lt;code&gt;useState&lt;/code&gt;, developers can store and modify data that affects the display of components, and any change will trigger the component rerender.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Implementation of the HomeTableContainer&lt;/strong&gt; &lt;br&gt;
Now I'm going to create a &lt;code&gt;HomeTableContainer&lt;/code&gt;, here's the logic code that will be used in the &lt;code&gt;HomeTableView&lt;/code&gt;. I also called the Functions &lt;code&gt;EditableContext&lt;/code&gt; and &lt;code&gt;EditableCell&lt;/code&gt; from the &lt;code&gt;Context.tsx&lt;/code&gt; file. On the function &lt;code&gt;handleValidate&lt;/code&gt; I created an age limit condition that can be entered by the user, if the age exceeds 60 years then there will be error. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/components/home/table/index.tsx

import { useEffect, useState } from 'react';
import HomeTableView from './Main'
import { Form } from 'antd';
import { cloneDeep, isEmpty } from 'lodash';
import { EditableContext, EditableCell } from './Context';

const HomeTableContainer = ({
  data,
  isLoading,
  isDisabled,
}: any) =&amp;gt; {
  const [validate, setValidate]: any = useState([]);
  const [mutableData, setMutableData] = useState(data)
  const [success, setSuccess] = useState&amp;lt;any&amp;gt;([]);
  const handleSave = async (row: any) =&amp;gt; {
    if (isDisabled) {
      return;
    }
    const newData = cloneDeep(data);
    const index = newData.content.findIndex(
      (item: any) =&amp;gt; Number(row.id) === Number(item.id)
    );
    if (newData.content[index]) {
      newData.content[index] = row;
      setMutableData({ ...newData });
    }
  };
  const handleValidate: any = (
    index: number,
    record: any,
    dataIndex: string,
    value: string | number
  ) =&amp;gt; {
    let bool = true;
    if (isDisabled) {
      return false;
    }
    const val = cloneDeep(validate);
    const obj: any = {}
    if ((dataIndex === 'age' &amp;amp;&amp;amp;
        Number(value) &amp;gt; 60)) {
        obj.age = {
        validateStatus: 'error',
        errorMsg: `Maksimal usia ${
          record.name
        } adalah 60th`,
      };
      bool = false;
    } 
    if (!val[index]) {
      val[index] = {
        [dataIndex]: {}
      }
    }
    if (obj &amp;amp;&amp;amp; !isEmpty(obj) &amp;amp;&amp;amp; val[index]) {
      val[index] = {...obj}
    } else {
      val[index] = {}
    }
    setValidate([...val])
    return bool;
  };
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  const EditableRow: any = ({ index, ...obj }: any) =&amp;gt; {
    const [form] = Form.useForm();
    return (
      &amp;lt;Form initialValues={data.content} form={form} component={false}&amp;gt;
        &amp;lt;EditableContext.Provider value={form}&amp;gt;
          &amp;lt;tr {...obj} /&amp;gt;
        &amp;lt;/EditableContext.Provider&amp;gt;
      &amp;lt;/Form&amp;gt;
    );
  };

  const components = {
    body: {
      row: EditableRow,
      cell: EditableCell,
    },
  };
  const sharedOnCell = () =&amp;gt; {
    return {};
  };
  const handleSetSuccess = (val: any) =&amp;gt; {
    setSuccess(val);
  };
  useEffect(() =&amp;gt; {
    return () =&amp;gt; {
      setSuccess([]);
    };
  }, []);
  return (
    &amp;lt;HomeTableView
      data={mutableData}
      components={components}
      isLoading={isLoading}
      rowKey={'id'}
      handleSave={handleSave}
      success={success}
      setSuccess={handleSetSuccess}
      validate={validate !== undefined ? validate : []}
      isDisabled={false}
      handleValidate={handleValidate}
      sharedOnCell={sharedOnCell}
    /&amp;gt;
  )
}

export default HomeTableContainer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Implement HomeTableView&lt;/strong&gt;&lt;br&gt;
After creating logic on &lt;code&gt;HomeTableContainer&lt;/code&gt;, in the &lt;code&gt;HomeTableView&lt;/code&gt; file I defined and modified the column by placing the function that we have created in the &lt;code&gt;HomeTableContainers&lt;/code&gt; file so that the rows on column age can be edited.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/components/home/table/Main.tsx

import {  Col, Row, Table  } from 'antd';

const HomeTableView = ({
  data,
  components,
  isLoading,
  onTableChange,
  rowKey,
  handleSave,
  handleValidate,
  sharedOnCell,
  validate,
  success,
  setSuccess,
  isDisabled,
}: any) =&amp;gt; {
  const columns = [
    {
      title: 'Name',
      dataIndex: 'name',
      key: 'name',
      width: '100px',
    },
    {
      title: 'Age',
      dataIndex: 'age',
      key: 'age',
      onCell: sharedOnCell,
      editable: true,
      suffix: 'th',
      width: '50px',
    },
    {
      title: 'Address',
      dataIndex: 'address',
      width: '100px',
      key: 'address',
    },
  ];
  const mutableColumns = columns.map((col: any) =&amp;gt; {
    if (!col.editable) {
      return col;
    }
    return {
      ...col,
      onCell: (record: any, index: number) =&amp;gt; ({
        record,
        suffix: col.suffix,
        editable: col.editable,
        dataIndex: col.dataIndex,
        title: col.title,
        handleSave,
        data,
        disabled: isDisabled,
        handleValidate,
        validate,
        success,
        setSuccess,
        index,
      }),
    };
  });
  return (
    &amp;lt;&amp;gt;
      &amp;lt;Row style={{ marginTop: '12px' }}&amp;gt;
        &amp;lt;Col flex={'auto'}&amp;gt;
          &amp;lt;Row style={{ marginBottom: '12px', marginTop: '12px' }}&amp;gt;
            &amp;lt;Table
              bordered
              columns={mutableColumns}
              components={components}
              dataSource={data?.content || []}
              loading={isLoading}
              rowClassName={() =&amp;gt; 'editable-row'}
              onChange={onTableChange}
              pagination={false}
              rowKey={rowKey}
              sticky
              scroll={{ x: 1500 }}
              style={{ width: '100%' }}
            /&amp;gt;
          &amp;lt;/Row&amp;gt;
        &amp;lt;/Col&amp;gt;
      &amp;lt;/Row&amp;gt;
    &amp;lt;/&amp;gt;
  );
};
export default HomeTableView

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Output&lt;/strong&gt;&lt;br&gt;
Below this is the result of the code above, the input form will display an error if the age is over 60 years.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4crcnisv5qvwz3niceol.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4crcnisv5qvwz3niceol.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I hope my sharing this time can be useful for the developer friends to create editable cells and tables using Antd-ReactJs. If you're inspired and think this can be implemented in your projects, please do it. For article with Indonesian Language you can read in &lt;a href="https://bayusyaits.medium.com/menggunakan-antd-dan-react-untuk-implement-editable-cells-dan-table-88e6992e127d" rel="noopener noreferrer"&gt;this article&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Feel free to ask questions&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Thank You&lt;/strong&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Build a Full-stack App Video Collections Using NextJs, Apollo.io Client, Graphql, Mysql, Typeorm and ExpressJs</title>
      <dc:creator>Bayu Syaits</dc:creator>
      <pubDate>Wed, 06 Sep 2023 12:13:56 +0000</pubDate>
      <link>https://dev.to/bayusyaits/build-a-full-stack-app-video-collections-using-nextjs-apolloio-client-graphql-mysql-typeorm-and-expressjs-e7a</link>
      <guid>https://dev.to/bayusyaits/build-a-full-stack-app-video-collections-using-nextjs-apolloio-client-graphql-mysql-typeorm-and-expressjs-e7a</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VF1vJ15o--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/spmtyowwymmr0hc7aheo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VF1vJ15o--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/spmtyowwymmr0hc7aheo.png" alt="Image 1, Tech Stack Bayu Syaits" width="800" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In this article I'm going to explain the code on the Video Collections app that I've created. It's a way for me to better understand the code that I have created and a way to exchange ideas with my developer friends. Before discussing technical issues, I'm going to list some of the features available in this app, including:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Video List:&lt;/strong&gt; Shows the entire video, at least 10 videos displayed. If there is more than that user can click Load More to add 10 videos to display. The Video Card must contain: Checkbox, title, image, short description, collections and categories. If the card is clicked, it can lead to the detail page. If the checkbox is on the &lt;code&gt;Add to Collection&lt;/code&gt; text on the active Sidebar.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Add to Collection:&lt;/strong&gt; This button will enable if the user has selected several videos and can &lt;code&gt;add to collection&lt;/code&gt;. If the user clicks on this button, it will display a popup containing the &lt;code&gt;list collection&lt;/code&gt; and the button Create or &lt;code&gt;Submit Collection&lt;/code&gt;. If a user does not have a &lt;code&gt;list collection&lt;/code&gt;, the user can create a new collection and will be directed to the popup &lt;code&gt;create collection&lt;/code&gt;. After the user selects the collection and &lt;code&gt;submit&lt;/code&gt;, the video will display the collection list on his card. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Create Collection:&lt;/strong&gt; Displays popups containing form &lt;code&gt;title&lt;/code&gt; and &lt;code&gt;link image&lt;/code&gt;. After submitting, the collection will be added to the database.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Video Details:&lt;/strong&gt; Displays &lt;code&gt;rank&lt;/code&gt;, &lt;code&gt;rates&lt;/code&gt;, &lt;code&gt;episodes&lt;/code&gt;, &lt;code&gt;collections&lt;/code&gt;, &lt;code&gt;categories&lt;/code&gt;, &lt;code&gt;release dates&lt;/code&gt; and &lt;code&gt;video descriptions&lt;/code&gt;. In addition, users can add videos to the collection by clicking &lt;code&gt;Add to Collection&lt;/code&gt; and popup.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I made this project using: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;NextJs:&lt;/strong&gt; I chose to build this application using &lt;code&gt;NextJs&lt;/code&gt;, because it's quick and easy to set up.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Graphql:&lt;/strong&gt; Developed by the Facebook team in 2012, aims to get a more flexible and efficient API. GraphQL itself is built on three main foundations: &lt;code&gt;query&lt;/code&gt;, &lt;code&gt;resolver&lt;/code&gt;, and &lt;code&gt;schema&lt;/code&gt;. Using GraphSQL, developers can call the data they need through a query. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Apollo:&lt;/strong&gt; To handle requests from the client side when using GraphQL, we can use Apollo servers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mysql:&lt;/strong&gt; Since I am quite familiar with SQL query and supports various types of data types, I chose to implement MYSQL in this project.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Typeorm:&lt;/strong&gt; TypeORM makes it easy to build an ORM-based application using an object-oriented programming paradigm.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ExpressJs:&lt;/strong&gt; I need a flexible framework but also a scalable one, for which I'm using &lt;code&gt;expressJs&lt;/code&gt; that's pretty popular with NodeJs users.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Implementation API
&lt;/h2&gt;

&lt;p&gt;The first folder I'm going to discuss is the api folder. It contains entities, and services whose names represent the table name, namely &lt;code&gt;category&lt;/code&gt;, &lt;code&gt;collection&lt;/code&gt;, &lt;code&gt;user&lt;/code&gt;, &lt;code&gt;video&lt;/code&gt;, &lt;code&gt;video-category&lt;/code&gt; and &lt;code&gt;video-collections&lt;/code&gt;. Each of these folders contains &lt;code&gt;typeDefs.ts&lt;/code&gt;, &lt;code&gt;args.ts&lt;/code&gt;, &lt;code&gt;entity.ts&lt;/code&gt; and &lt;code&gt;resolvers.ts&lt;/code&gt; files. I'll explain briefly about the files.&lt;/p&gt;

&lt;p&gt;The first is &lt;code&gt;typeDefs.ts&lt;/code&gt;, which contains data forms that are defined in the GraphQL schema language. The schema itself consists of one or more types such as Object Type, Scalar Type, Query Type, Mutation Type and Input Type. Here's a piece of code on the typeDEFS file in the video folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const typeDefs = `
  extend type Query {
    getVideo(slug: String!): Video
  }

  extend type Mutation {
    addVideo(payload: PayloadAddVideo): Video!
  }

  type Gallery {
    image: String
  }

  input PayloadAddVideo {
    title: String!
    image: String!
    episode: Int
    description: String
    isCencor: Int
    rates: Int
    rank: Int
    type: String
  }

  type Video {
    uuid: String!
    title: String!
    slug: String!
    image: String
    gallery: [Gallery]
    videoCollections: [VideoCollection]
    videoCategories: [VideoCategory]
  }
`;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Apollo server needs to know how to fill in the data in the schema in order to be able to respond to the client's request. To this, a resolution is required, following this piece of code in the file &lt;code&gt;resolver.ts&lt;/code&gt; on the video folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { isEmpty } from "lodash";
import { v4 } from "uuid"
import Args, { VideoResponse } from "./args";
import { AppDataSource } from "../../data-source"
import { Video as VideoEntity } from "./entity";
import { filterItems } from "../../helpers/filter";
import { generateKey, setSpaceToDash } from "../../helpers/mixins";
import slugify from "../../helpers/slugify";

// Provide resolver functions for your schema fields
export const Query = {
  getVideo: async (_: any, args: any) =&amp;gt; {
    const { slug } = args;
    if (!slug) {
      return null;
    }
    const videoEntity = AppDataSource.getRepository(VideoEntity)
    return await videoEntity.findOne({ 
      relations: {
        videoCollections: true,
        videoCategories: true
      },
      where: { 
        slug: setSpaceToDash(slug)
      } 
    });
  }
}

export const Mutation = {
  addVideo: async (_: any, args: any) =&amp;gt; {
    try {
      const generate = Math.floor(generateKey(100))
      const { 
        payload: {
          title, image, episode, rates, rank,
          status, description, gallery
        }
      } = args;
      const video = new VideoEntity()
      video.uuid = v4()
      if (episode) {
        video.episode = episode
      }
      if (rates) {
        video.rates = rates
      }
      if (rank) {
        video.rank = rank
      }
      if (description) {
        video.description = description
      }
      if (status &amp;amp;&amp;amp; ['editorial','favorite'].includes(status)) {
        video.status = status
      }
      if (gallery &amp;amp;&amp;amp; Array.isArray(gallery)) {
        video.gallery = gallery
      }
      video.slug = slugify(title) ? `${setSpaceToDash(slugify(title))}_${generate}` : 
      `${setSpaceToDash(title)}_${generate}`
      video.image = image
      const videoRepository = AppDataSource.getRepository(VideoEntity)
      return await videoRepository.save(video);
    } catch (error) {
      return {};
    }
  }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next file is &lt;code&gt;args.ts&lt;/code&gt;, I called the &lt;code&gt;type-graphql&lt;/code&gt; library to use some decorators like &lt;code&gt;Argstype&lt;/code&gt;, Field and &lt;code&gt;ObjectType&lt;/code&gt; to define GraphQL schemes easily.&lt;br&gt;
We can associate &lt;code&gt;Class Args&lt;/code&gt; properties with fields (limit, offset, type, dst) in a GraphQL scheme using Fields. Whereas to define the argument type in the &lt;code&gt;resolver&lt;/code&gt; of a class, we can use Argstype and to associate the object type can use &lt;code&gt;ObjectType&lt;/code&gt;. Here's a piece of code on the &lt;code&gt;args.ts&lt;/code&gt; file in the video folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Field, ArgsType, ObjectType } from 'type-graphql';
@ArgsType()
export default class Args {
  @Field({ defaultValue: 10 })
  limit: number;

  @Field({ defaultValue: 0 })
  offset: number;

  @Field({ nullable: true })
  type?: string;

  @Field({ nullable: true })
  sortBy?: string;

  @Field({ nullable: true })
  search?: string;

  @Field({ nullable: true })
  slug?: string;
}

@ObjectType()
export class Gallery {
  @Field()
  image: string;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each typeDefs.ts and &lt;code&gt;resolvers.ts&lt;/code&gt; file will be linked via &lt;code&gt;server.app.ts&lt;/code&gt;, below is an example of how to connect &lt;code&gt;User&lt;/code&gt; and &lt;code&gt;Category&lt;/code&gt; services with &lt;code&gt;apollo-server-express&lt;/code&gt; and integrate them into &lt;code&gt;ExpressJs&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import "reflect-metadata";
import * as express from "express";
import { ApolloServer, makeExecutableSchema } from "apollo-server-express";

import { merge } from "lodash";
import { typeDefs as UserTypeDefs } from "./app/services/user/typeDefs";
import { typeDefs as CategoryTypeDefs } from "./app/services/category/typeDefs";
import {
   Query as UserQuery,
   Mutation as UserMutations
} from "./app/services/user/resolvers";
import {
  Query as CategoryQuery,
  Mutation as CategoryMutations
} from "./app/services/category/resolvers";
const startServer = async () =&amp;gt; {
  const schema = makeExecutableSchema({
    typeDefs: [ 
      CategoryTypeDefs,
      UserTypeDefs,
      VideoTypeDefs,
      CollectionTypeDefs,
      VideoCollectionTypeDefs,
      VideoCategoryTypeDefs
    ],
    resolvers: merge(
      {
        Query: {
          ...CategoryQuery,
          ...UserQuery,
        },
        Mutation: {
          ...CategoryMutations,
          ...UserMutations,
        }
      }
    ),
  });
  const server = new ApolloServer({ 
    schema,
    introspection: true,
    playground: true,
    tracing: true,
  });

  const app: express.Application = express();

  server.applyMiddleware({ app });

  app.listen({ port: PORT }, () =&amp;gt;
    console.log(`🚀 Server ready at http://localhost:${PORT}${server.graphqlPath}`)
  );
};

startServer();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last part is the &lt;code&gt;entity.ts&lt;/code&gt; file, in this project I use a typeorm which is a representation of a table in a relational database. By defining a class in this Entity file, TypeORM will automatically create a table with a schema defined in the Video class according to the decorator defined on each column, and we can use TypeORN to perform CRUD operations without having to write SQL queries directly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { 
  Entity, 
  Column, 
  PrimaryColumn,
  Generated,
  BaseEntity,
  OneToMany,
  JoinColumn,
  DeleteDateColumn,
  CreateDateColumn,
  UpdateDateColumn,
} from "typeorm";

import { VideoCollection } from "../video-collection/entity";
import { VideoCategory } from "../video-category/entity";

export enum Status {
  EDITORIAL = "editorial",
  FAVORITE = "favorite",
}

type Gallery = {
  image: string
}

@Entity()
export class Video extends BaseEntity {
  @PrimaryColumn("char", {length: 100})
  @Generated("uuid")
  uuid: string

  @Column("char", { length: 50 })
  title: string;

  @OneToMany(() =&amp;gt; VideoCollection, 
    (videoCollection) =&amp;gt; videoCollection.videoUuid,
    {
      cascade: ["insert", "update"],
    })
  @JoinColumn([
      { name: "uuid" }
  ])
  public videoCollections: VideoCollection[]

  @OneToMany(() =&amp;gt; VideoCategory, 
    (videoCategory) =&amp;gt; videoCategory.videoUuid,
    {
      cascade: ["insert", "update"],
    })
  @JoinColumn([
      { name: "uuid" }
  ])

  public videoCategories: VideoCategory[]

  @Column("text", {nullable: true})
  description: string;

  @Column({ type: "int", nullable: true })
  episode: number;

  @Column({ type: "int", nullable: true })
  rates: number;

  @Column({ type: "int", nullable: true })
  rank: number;

  @Column({ type: "int", default: 0 })
  isCencor: number;

  @Column("char", { length: 20, nullable: true })
  type: string;

  @Column({ type: "simple-json", default: "", nullable: true })
  gallery: Gallery[];

  @Column({default: null, nullable: true})
  image: string;

  @Column("char", { length: 60 })
  slug: string;

  @Column({
    type: "enum",
    enum: Status,
    default: Status.FAVORITE
  })
  status: Status

  @Column({ type: 'date', nullable: true })
  publishDate: Date;

  @CreateDateColumn()
  createdDate: Date;

  @UpdateDateColumn()
  updateDate: Date;

  @DeleteDateColumn()
  deletedDate: Date;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All entity files in each service folder are connected using DataSource. On the code below is an example of the dataSource configuration to connect to the &lt;code&gt;MySQL&lt;/code&gt; database, in the &lt;code&gt;data-source.ts&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import "reflect-metadata"
import { DataSource } from "typeorm"
import { User } from "./services/user/entity"
import { Category } from "./services/category/entity"
import { Video } from "./services/video/entity"
import { Collection } from "./services/collection/entity"
import { VideoCollection } from "./services/video-collection/entity"
import { VideoCategory } from "./services/video-category/entity"

export const AppDataSource = new DataSource({
    type: "mysql",
    host: "127.0.0.1",
    port: 3306,
    username: "root",
    password: "xxxxx",
    database: "db_collections",
    synchronize: true,
    logging: false,
    entities: [
        User, 
        Category, 
        Video, 
        Collection, 
        VideoCollection,
        VideoCategory
    ],
    migrations: [],
    subscribers: [],
})
AppDataSource.initialize()
    .catch((error) =&amp;gt; console.log(error))

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Implementation APP
&lt;/h2&gt;

&lt;p&gt;This app is built using &lt;code&gt;NextJs&lt;/code&gt;, I just follow the documentation and use the features available on this framework. However, I've added some libraries like &lt;code&gt;material-ui&lt;/code&gt;, &lt;code&gt;GraphQL&lt;/code&gt;, &lt;code&gt;apollo&lt;/code&gt; and &lt;code&gt;react-hook-form&lt;/code&gt; to speed up the development process and adapt to the needs of this project.&lt;br&gt;
If in the api folder I have described the use of Apollo and GraphQL on the server side, then in this app folder I will describe the use on the client side. First, on the &lt;code&gt;_app.tsx&lt;/code&gt; file we need to configure and redirect to the &lt;code&gt;GraphQL&lt;/code&gt; server endpoint that has been created in the api folder. Then we can integrate the &lt;code&gt;Apollo Client&lt;/code&gt; with the React application using &lt;code&gt;ApolloProvider&lt;/code&gt; and connect it to the function &lt;code&gt;client&lt;/code&gt;. For more information, you can see the code examples below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import type { AppProps } from "next/app";
import { useEffect, useState } from "react";
import { unregister } from "next-offline/runtime";
import { ThemeProvider } from "@mui/material/styles";
import CssBaseline from "@mui/material/CssBaseline";
import theme from "hooks/theme";
import { sha256 } from 'crypto-hash';

import Offline from "./offline";
import Layout from "base-components/layout";
import { createPersistedQueryLink } from '@apollo/client/link/persisted-queries';
import { ApolloClient, InMemoryCache, ApolloProvider } from '@apollo/client';
import { createHttpLink } from 'apollo-link-http';
import { REACT_APP_API_URL } from "config";
import { AppBar, Grid, Toolbar } from "@mui/material";
import Typography from '@mui/material/Typography';
import { Box, Container } from "@mui/system";
import Link from "next/link";

const httpLink = createPersistedQueryLink({ sha256 }).concat(
  new createHttpLink({ uri: REACT_APP_API_URL }),
);

const client = new ApolloClient({
  link: httpLink,
  cache: new InMemoryCache(),
});

export default function MyApp({
  Component,
  emotionCache = clientSideEmotionCache,
  pageProps,
}: AppProps &amp;amp; {
  emotionCache: any;
}) {
  const [isDisconnected, setDisconnected] = useState&amp;lt;boolean&amp;gt;(false);
  const handleConnectionChange = () =&amp;gt; {
    if (!navigator.onLine) {
      setDisconnected(true);
    } else {
      setDisconnected(false);
    }
  };
  useEffect(() =&amp;gt; {
    unregister();
    const style: any = document.getElementById("server-side-styles");
    if (style) {
      style.parentNode.removeChild(style);
    }
    window.addEventListener("offline", handleConnectionChange);
    window.addEventListener("online", handleConnectionChange);
  });
  const setComponent = () =&amp;gt; {
    let d: any = &amp;lt;Component {...pageProps} /&amp;gt;;
    if (isDisconnected) {
      d = &amp;lt;Offline /&amp;gt;;
    }
    return d;
  };
  return (
    &amp;lt;ApolloProvider client={client as any}&amp;gt;
        &amp;lt;Layout&amp;gt;
          &amp;lt;ThemeProvider theme={theme}&amp;gt;
            &amp;lt;CssBaseline /&amp;gt;
            &amp;lt;AppBar position="relative"&amp;gt;
              &amp;lt;Toolbar
                sx={{
                  pr: '24px',
                }}
              &amp;gt;
                &amp;lt;Link 
                  style={{
                    textDecoration: 'none'
                  }}
                  href="/"&amp;gt;
                  &amp;lt;Typography
                    component="h1"
                    variant="h6"
                    color="inherit"
                    noWrap
                    sx={{ 
                      flexGrow: 1, 
                      color: 'white'
                    }}
                  &amp;gt;
                      Video List
                  &amp;lt;/Typography&amp;gt;
                &amp;lt;/Link&amp;gt;
              &amp;lt;/Toolbar&amp;gt;
            &amp;lt;/AppBar&amp;gt;
            &amp;lt;Box&amp;gt;
              &amp;lt;Container maxWidth="lg" sx={{ px: '24px', mt: 4, mb: 4 }}&amp;gt;
                &amp;lt;Grid container spacing={1}&amp;gt;
                  {setComponent()}
                &amp;lt;/Grid&amp;gt;
              &amp;lt;/Container&amp;gt;
            &amp;lt;/Box&amp;gt;
          &amp;lt;/ThemeProvider&amp;gt;
        &amp;lt;/Layout&amp;gt;
    &amp;lt;/ApolloProvider&amp;gt;
  );
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Implementation Query &amp;amp; Mutation
&lt;/h2&gt;

&lt;p&gt;After config, the most common step is to use the &lt;code&gt;Apollo Client&lt;/code&gt; with a query and a &lt;code&gt;GraphQL&lt;/code&gt; mutation on each component. Below is an example in the &lt;code&gt;queries.ts&lt;/code&gt; file in the &lt;code&gt;app folder &amp;gt; components &amp;gt; video &amp;gt; details&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;GET_VIDEO&lt;/code&gt; query, I have called the &lt;code&gt;JoinTable&lt;/code&gt; column (&lt;code&gt;videoCategories&lt;/code&gt; dan &lt;code&gt;videoCollections&lt;/code&gt;). When dealing with this, we can implement resolvers to call field &lt;code&gt;joinTable&lt;/code&gt; TypeORM through GraphQL. In this case, I take the value uuid in the column &lt;code&gt;videoCategories&lt;/code&gt; and &lt;code&gt;videoCollections&lt;/code&gt;. The rest of the values that I'm calling adjust to the needs in each column.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;PUT_BULK_VIDEO_COLLECTION&lt;/code&gt; mutation section, I call &lt;code&gt;PayloadBulkVideoCollect&lt;/code&gt; which is the Input Type. This Input type is called in a mutation to define the type of data that can be sent as an argument in the &lt;code&gt;bulkVideoCollection&lt;/code&gt; operation to be sent to the server. These Input Types are very useful in &lt;code&gt;GraphQL&lt;/code&gt;, as they allow developers to organize arguments well.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import gql from "graphql-tag";
export const PUT_BULK_VIDEO_COLLECTION = gql`
  mutation bulkVideoCollection(
    $payload: [PayloadBulkVideoCollection!]!
  ) {
    bulkVideoCollection(
      payload: $payload
    ) {
      uuid
      userUuid
    }
  }
`;
export const DELETE_VIDEO_COLLECTION = gql`
  mutation deleteVideoCollection(
    $uuid: String!
    $userUuid: String!
  ) {
    deleteVideoCollection(
      uuid: $uuid
      userUuid: $userUuid
    )
  }
`;
export const GET_VIDEO = gql`
  query getVideo(
    $slug: String!
  ) {
    getVideo(
      slug: $slug
    ) {
      uuid
      title
      slug
      episode
      description
      type
      image
      videoCategories {
        id
        uuid
        categoryUuid {
          title
        }
      }
      videoCollections {
        uuid
        collectionUuid {
          title
          uuid
        }
        id
      }
    }
  }
`;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Implementation useQuery dan useMutation
&lt;/h2&gt;

&lt;p&gt;There are two hooks provided by the &lt;code&gt;Apollo Client&lt;/code&gt; library, &lt;code&gt;useQuery&lt;/code&gt; and &lt;code&gt;useMutation&lt;/code&gt;. Below is the code in the &lt;code&gt;components &amp;gt; video &amp;gt; details &amp;gt; index.tsx&lt;/code&gt;. One of the advantages of using an &lt;code&gt;apollo client&lt;/code&gt; is that developers can control the data taken from the cache and/ or from the server by adding the &lt;code&gt;fetchPolicy&lt;/code&gt; option.&lt;/p&gt;

&lt;p&gt;Values for &lt;code&gt;fetchPolicy&lt;/code&gt; options are numerous, including: &lt;code&gt;cache-first&lt;/code&gt;, &lt;code&gt;cache-and-network&lt;/code&gt;, &lt;code&gt;network-only&lt;/code&gt;, &lt;code&gt;cache&lt;/code&gt;, &lt;code&gt;no-catch&lt;/code&gt; and &lt;code&gt;standby&lt;/code&gt;. For an explanation of this value, you can read on this &lt;a href="https://www.apollographql.com/docs/react/data/queries/"&gt;link&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In addition, when using &lt;code&gt;useQuery&lt;/code&gt; we can also deal with loading and errors that may occur during requests to the server, or perform &lt;code&gt;refetch&lt;/code&gt; (method used to reload data from the server with GraphQL query) if successful request on &lt;code&gt;useMutation&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The next hook is &lt;code&gt;useMutation&lt;/code&gt;, in the code below I call the mutation &lt;code&gt;DELETE_VIDEO_COLLECTION&lt;/code&gt;. Mutations in GraphQL are used to send data to servers to update data, just as &lt;code&gt;useQuery&lt;/code&gt; in &lt;code&gt;useMutation&lt;/code&gt; can also handle &lt;code&gt;loading&lt;/code&gt;, &lt;code&gt;error&lt;/code&gt; during request to the server and retrieve &lt;code&gt;data&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;deleteVideoCollection&lt;/code&gt; is a function called to run a mutation. for the payload/ parameter sent as an argument, we can put it in the variables. If the change of &lt;code&gt;data&lt;/code&gt;/ &lt;code&gt;mutation&lt;/code&gt; is successful then it will reload the data in the &lt;code&gt;GET_VIDEO&lt;/code&gt; query by adding the option &lt;code&gt;onCompleted: refetch&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { useContext } from "react";
import { useQuery, useMutation } from '@apollo/client';
import VideoDetailView from "./VideoDetailView";
import VideoDetailSidebarView from "./VideoDetailSidebarView";
import VideoDetailModal from "./modal";
import VideoDetailModalCreate from "./modal-create";
import { useModal, ModalPopupDispatchContext } from "hoc/withModal";
import { useRouter } from "next/router";
import { Grid } from "@mui/material";
import { debounce } from "lodash";
import { GET_VIDEO, DELETE_VIDEO_COLLECTION } from './queries'

type VideoProps = {};
const VideoDetailContainer: React.FC&amp;lt;VideoProps&amp;gt; = () =&amp;gt; {
  const router = useRouter();
  const {
    query: {
      slug
    }
  }: any = router
  const { loading, error, data, refetch } = useQuery(GET_VIDEO, {
    fetchPolicy: "cache-and-network",
    variables: {
      slug
    },
  }) 
  const { openModal } = useModal();
  const { closeModal, onSubmitModal } = useContext(ModalPopupDispatchContext);
  const [deleteVideoCollection, {}] = useMutation(DELETE_VIDEO_COLLECTION, {
    onCompleted: refetch,
    awaitRefetchQueries: true
  });
  const handleRemoveCollection = (val: string)  =&amp;gt; () =&amp;gt; {
    deleteVideoCollection({
      variables: {
          uuid: val,
          userUuid: 'de4e31bd-393d-40f7-86ae-ce8e25d81b00'
        } 
      },
    )
    .catch((err: any) =&amp;gt; {
      console.log('[011] err', err)
    });
  }
  const openModalAddCollection = debounce(() =&amp;gt; {
    const onFinish = () =&amp;gt; {
      onSubmitModal();
    };
    const onSwitch = () =&amp;gt; {
      closeModal();
      openModalCreateCollection()
    }
    openModal({
      title: "Add to Collection",
      hideClose: false,
      component: () =&amp;gt; (
        &amp;lt;VideoDetailModal
          onFinish={onFinish}
          onSwitch={onSwitch}
          field={{...data.getVideo}}
        /&amp;gt;
      ),
      onClose: () =&amp;gt; {
        closeModal();
      },
    });
  }, 1000);
  const openModalCreateCollection = debounce(() =&amp;gt; {
    const onFinish = () =&amp;gt; {
      onSubmitModal();
      openModalAddCollection()
    };
    const onSwitch = () =&amp;gt; {
      closeModal();
      openModalAddCollection()
    }
    openModal({
      title: "Create Collection",
      hideClose: false,
      component: () =&amp;gt; (
        &amp;lt;VideoDetailModalCreate
          onFinish={onFinish}
          onSwitch={onSwitch}
        /&amp;gt;
      ),
      onClose: () =&amp;gt; {
        closeModal();
      },
    });
  }, 1000);
  const handleAddCollection = (e: React.FormEvent&amp;lt;HTMLFormElement&amp;gt;) =&amp;gt; {
    if (data?.getVideo) {
      openModalAddCollection()
    }
  }
  const handlerList = {
    error,
    loading,
    data,
    handleRemoveCollection
  }

  const handlerCollection = {
    handleAddCollection,
    error,
    loading,
    data
  }

  return (
    &amp;lt;Grid 
        container 
        rowSpacing={3} 
        columnSpacing={{ xs: 2, sm: 2, md: 2, lg: 2 }}
        justifyContent="start"
        alignItems="start"
      &amp;gt;
      &amp;lt;Grid item lg={3} xl={3} xs={12} sm={12} md={3}&amp;gt;
        &amp;lt;VideoDetailSidebarView {...handlerCollection} /&amp;gt;
      &amp;lt;/Grid&amp;gt;
      &amp;lt;Grid item lg={9} xl={9} xs={12} sm={12} md={9}&amp;gt;
        &amp;lt;VideoDetailView {...handlerList} /&amp;gt;
      &amp;lt;/Grid&amp;gt;
    &amp;lt;/Grid&amp;gt;
  );
}

export default VideoDetailContainer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  File Query SQL
&lt;/h2&gt;

&lt;p&gt;The last folder in this project, &lt;code&gt;db_collections&lt;/code&gt;, contains SQL queries to create tables and mock data. This is to make it easier for friends who want to try this app without having to make data samples from scratch.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclution
&lt;/h2&gt;

&lt;p&gt;GraphQL can be an alternative if friends want to build applications instead of using REST-API. One of its advantages is that developers can call the required data through a query and I also use apollo to handle requests from the client side. The library that I use in this project is very helpful in the development process in addition to the flexibility and scalability factors, the project becomes faster in developing with the library-library that I have explained above.&lt;/p&gt;

&lt;p&gt;If there are errors or there is a way/ technique that friends think is better when implementing the above material, please comment below or you can send a message to me on &lt;a href="https://www.linkedin.com/in/bayusyaits/"&gt;linkedIn&lt;/a&gt;. For friends who want to learn more and explore, please check my &lt;a href="https://github.com/Bayusyaits/video-collections-next"&gt;github&lt;/a&gt;. Once you clone, and install depedency in each folder (api/ app), you can run by following the commands in the scripts in each &lt;code&gt;packages.json&lt;/code&gt;.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
