Thứ năm, 12/12/2019 | 00:00 GMT+7

Cách xây dựng ứng dụng Recipe bằng React, Prisma và GraphQL

GraphQL đã trở nên phổ biến về mặt phát triển giao diện user do những lợi thế khác nhau mà nó mang lại so với các API REST . Tuy nhiên, việc cài đặt server GraphQL của bạn vừa dễ xảy ra lỗi vừa phức tạp. Do đó, các dịch vụ được quản lý như Prisma đã được thực hiện để quản lý server GraphQL của bạn, cho phép bạn tập trung vào việc phát triển ứng dụng của bạn .

Trong hướng dẫn này, ta sẽ xây dựng một ứng dụng công thức đầy đủ chức năng bằng cách sử dụng ReactPrisma để quản lý GraphQL.

Yêu cầu

  • Kiến thức trung cấp về Javascript và React
  • Các nguyên tắc cơ bản về GraphQL
  • Các nguyên tắc cơ bản của Docker

Bước 1 - Cài đặt phụ thuộc

Cài đặt client Prisma CLI trên phạm vi global bằng cách chạy lệnh sau:

  • npm install -g prisma

Ta sẽ sử dụng create-react-app để khởi động ứng dụng React của bạn , vì vậy hãy chạy lệnh sau để cài đặt nó trên phạm vi global :

  • npm install -g create-react-app

Để sử dụng Prisma local , bạn cần cài đặt Docker trên máy của bạn . Nếu bạn chưa có Docker, bạn có thể download Docker Community Edition .

Bước 2 - Cài đặt Prisma

Để sử dụng Prisma CLI, bạn cần phải có account Prisma. Bạn có thể tạo một account tại trang web Prisma , sau đó đăng nhập vào Prisma CLI bằng cách chạy lệnh sau:

  • prisma login

Bây giờ ta đã có tất cả các phụ thuộc cần thiết, hãy tạo một folder cho dự án và chuyển vào folder bằng cách chạy các lệnh sau:

  • mkdir recipe-app-prisma-react
  • cd recipe-app-prisma-react

Sau đó khởi tạo server Prisma của bạn trong folder :

  • prisma init

Một dấu nhắc sẽ xuất hiện với một vài tùy chọn về phương pháp bạn muốn sử dụng để cài đặt server lăng trụ của bạn . Ta sẽ làm việc với server local ngay bây giờ và sau đó triển khai nó sau. Chọn Create new database để Prisma tạo database local bằng Docker .

 kết quả  `prima init`

Tiếp theo, bạn sẽ nhận được dấu nhắc chọn database . Đối với hướng dẫn này, ta sẽ sử dụng Postgres, vì vậy hãy chọn PostgreSQL :

 Database  Prisma nhắc

Tiếp theo, ta phải chọn một ngôn ngữ lập trình cho ứng dụng client lăng trụ đã tạo của ta . Chọn Prisma Javascript Client :

 Dấu nhắc  ngôn ngữ Prisma

Bạn sẽ có các file sau do Prisma tạo dựa trên các tùy chọn đã chọn:

Tóm tắt cài đặt Prisma

Bước 3 - Triển khai Prisma

Bây giờ ta đã cài đặt server Prisma của bạn , hãy đảm bảo docker đang chạy. Sau đó, chạy lệnh sau để khởi động server :

  • docker-compose up -d

Docker composer được sử dụng để chạy nhiều containers như một dịch vụ duy nhất. Lệnh trước đó sẽ khởi động server Prisma của ta và database Postgres. 127.0.0.1:4466 trong trình duyệt của bạn để xem playground Prisma.

Nếu bạn muốn dừng server của bạn , hãy chạy docker-compose stop .

Tiếp theo, mở file datamodel.prisma của bạn và thay thế nội dung demo bằng nội dung sau:

type Recipe {   id: ID! @unique   createdAt: DateTime!   updatedAt: DateTime!   title: String! @unique   ingredients: String!   directions: String!   published: Boolean! @default(value: "false") } 

Sau đó, chạy lệnh sau để triển khai tới server demo:

  • prisma deploy

Bạn sẽ nhận được phản hồi hiển thị các mô hình đã tạo và điểm cuối Prisma của bạn như sau:

Đầu ra hiển thị các mô hình đã tạo và điểm cuối Prisma

Để xem server đã triển khai, hãy mở console Prisma tại https://app.prisma.io/ và chuyển đến các dịch vụ. Bạn sẽ có những thông tin sau hiển thị trong trang tổng quan của bạn :

 Server  đã triển khai trong console  Prisma

Để triển khai tới server local của bạn, hãy mở file prisma.yml và thay đổi điểm cuối thành http://localhost:4466 , sau đó chạy prisma deploy prisma.yml

Bước 4 - Cài đặt ứng dụng React

Bây giờ server Prisma của ta đã sẵn sàng, ta có thể cài đặt ứng dụng React của bạn để sử dụng điểm cuối Prisma GraphQL.

Trong folder dự án, hãy chạy lệnh sau để khởi động ứng dụng client của ta bằng cách sử dụng create-react-app :

  • create-react-app client

Để làm việc với GraphQL, ta yêu cầu một số phụ thuộc. Điều hướng vào folder khách và chạy các lệnh sau để cài đặt chúng:

  • cd client
  • npm install apollo-boost react-apollo graphql-tag graphql --save

Đối với giao diện user , ta sẽ sử dụng Ant Design :

  • npm install antd --save

Cấu trúc folder :

Cấu trúc folder ứng dụng của ta sẽ như sau:

src ├── components │   ├── App.js │   ├── App.test.js │   ├── RecipeCard │   │   ├── RecipeCard.js │   │   └── index.js │   └── modals │       ├── AddRecipeModal.js │       └── ViewRecipeModal.js ├── containers │   └── AllRecipesContainer │       ├── AllRecipesContainer.js │       └── index.js ├── graphql │   ├── mutations │   │   ├── AddNewRecipe.js │   │   └── UpdateRecipe.js │   └── queries │       ├── GetAllPublishedRecipes.js │       └── GetSingleRecipe.js ├── index.js ├── serviceWorker.js └── styles     └── index.css 

Bước 5 - Viết mã

Index.js

Ở đây ta sẽ thực hiện cấu hình apollo. Đây sẽ là file mục nhập chính cho ứng dụng của ta :

import React from 'react'; import ReactDOM from 'react-dom'; import ApolloClient from 'apollo-boost'; import { ApolloProvider } from 'react-apollo';  import App from './components/App';  // Pass your prisma endpoint to uri const client = new ApolloClient({   uri: 'https://eu1.prisma.sh/XXXXXX' });  ReactDOM.render(   <ApolloProvider client={client}>     <App />   </ApolloProvider>,   document.getElementById('root') ); 

GetAllPublishedRecipes.js

Truy vấn tìm nạp tất cả các công thức nấu ăn:

import { gql } from 'apollo-boost';  export default gql`query GetAllPublishedRecipes {     recipes(where: { published: true }) {       id       createdAt       title       ingredients       directions       published     }   }`; 

GetSingleRecipe.js

Truy vấn để tìm nạp một công thức theo id công thức:

import { gql } from 'apollo-boost';  export default gql`query GetSingleRecipe($recipeId: ID!) {     recipe(where: { id: $recipeId }) {       id       createdAt       title       directions       ingredients       published     }   }`; 

AddNewRecipe.js

Đột biến để tạo ra một công thức mới:

import { gql } from 'apollo-boost';  export default gql`mutation AddRecipe(     $directions: String!     $title: String!     $ingredients: String!     $published: Boolean   ) {     createRecipe(       data: {         directions: $directions         title: $title         ingredients: $ingredients         published: $published       }     ) {       id     }   }`; 

UpdateRecipe.js

Đột biến để cập nhật một công thức:

import { gql } from 'apollo-boost';  export default gql`mutation UpdateRecipe(     $id: ID!     $directions: String!     $title: String!     $ingredients: String!     $published: Boolean   ) {     updateRecipe(       where: { id: $id }       data: {         directions: $directions         title: $title         ingredients: $ingredients         published: $published       }     ) {       id     }   }`; 

AllRecipesContainer.js

Đây là nơi dựa trên logic của ta cho CRUD hoạt động CRUD . Tệp khá lớn, vì vậy ta chỉ bao gồm những phần quan trọng. Bạn có thể xem phần còn lại của mã trên GitHub .

Để sử dụng các truy vấn và đột biến của ta , ta cần nhập chúng và sau đó sử dụng graphql react-apollo's react react-apollo's cho phép ta tạo higher-order component có thể thực hiện các truy vấn và cập nhật một cách phản ứng dựa trên dữ liệu ta có trong ứng dụng của bạn .

Dưới đây là một ví dụ về cách ta có thể tìm nạp và hiển thị tất cả các công thức đã xuất bản:

import React, { Component } from 'react'; import { graphql } from 'react-apollo';  import { Card, Col, Row, Empty, Spin } from 'antd';  // queries import GetAllPublishedRecipes from '../../graphql/queries/GetAllPublishedRecipes';  class AllRecipesContainer extends Component {   render() {     const { loading, recipes } = this.props.data;      return (       <div>         {loading ? (           <div className="spin-container">             <Spin />           </div>         ) : recipes.length > 0 ? (           <Row gutter={16}>             {recipes.map(recipe => (               <Col span={6} key={recipe.id}>                 <RecipeCard                   title={recipe.title}                   content={                     <Fragment>                       <Card                         type="inner"                         title="Ingredients"                         style={{ marginBottom: '15px' }}                       >                         {`${recipe.ingredients.substring(0, 50)}.....`}                       </Card>                       <Card type="inner" title="Directions">                         {`${recipe.directions.substring(0, 50)}.....`}                       </Card>                     </Fragment>                   }                   handleOnClick={this._handleOnClick}                   handleOnEdit={this._handleOnEdit}                   handleOnDelete={this._handleOnDelete}                   {...recipe}                 />               </Col>             ))}           </Row>         ) : (           <Empty />         )}       </div>     );   } }  graphql(GetAllPublishedRecipes)(AllRecipesContainer); 

Chế độ xem kết quả sẽ như sau:

Chế độ xem công thức nấu ăn được hiển thị trong ứng dụng thử nghiệm

Lưu ý: Việc tạo kiểu cho các thành phần sẽ không được bao gồm do kích thước file . Mã có sẵn trong repo GitHub .

Vì ta yêu cầu nhiều hơn một chất tăng cường trong thành phần của bạn , ta sẽ sử dụng chế phẩm để kết hợp tất cả các chất tăng cường cần thiết cho thành phần:

import React, { Component } from 'react'; import { graphql, compose, withApollo } from 'react-apollo';  // queries import GetAllPublishedRecipes from '../../graphql/queries/GetAllPublishedRecipes'; import GetSingleRecipe from '../../graphql/queries/GetSingleRecipe';  // mutations import UpdateRecipe from '../../graphql/mutations/UpdateRecipe'; import AddNewRecipe from '../../graphql/mutations/AddNewRecipe';  // other imports  class GetAllPublishedRecipes extends Component {     // class logic }  export default compose(   graphql(UpdateRecipe, { name: 'updateRecipeMutation' }),   graphql(AddNewRecipe, { name: 'addNewRecipeMutation' }),   graphql(GetAllPublishedRecipes) )(withApollo(AllRecipesContainer)); 

Ta cũng yêu cầu trình tăng cường withApollo , cung cấp quyền truy cập trực tiếp vào ApolloClient của bạn. Điều này sẽ hữu ích, vì ta cần thực hiện các truy vấn một lần để tìm nạp dữ liệu cho một công thức.

Tạo công thức

Sau khi thu thập dữ liệu từ biểu mẫu sau:

Biểu mẫu dữ liệu cho các công thức nấu ăn mới trong ứng dụng thử nghiệm

Sau đó, ta thực hiện lệnh gọi lại handleSubmit sau, chạy đột biến addNewRecipeMutation :

class GetAllPublishedRecipes extends Component {   //other logic    _handleSubmit = event => {     this.props       .addNewRecipeMutation({         variables: {           directions,           title,           ingredients,           published         },         refetchQueries: [           {             query: GetAllPublishedRecipes           }         ]       })       .then(res => {         if (res.data.createRecipe.id) {           this.setState(             (prevState, nextProps) => ({               addModalOpen: false             }),             () =>               this.setState(                 (prevState, nextProps) => ({                   notification: {                     notificationOpen: true,                     type: 'success',                     message: `recipe ${title} added successfully`,                     title: 'Success'                   }                 }),                 () => this._handleResetState()               )           );         }       })       .catch(e => {         this.setState((prevState, nextProps) => ({           notification: {             ...prevState.notification,             notificationOpen: true,             type: 'error',             message: e.message,             title: 'Error Occured'           }         }));       });   }; }; 

Chỉnh sửa công thức

Để chỉnh sửa công thức, ta sử dụng lại biểu mẫu đã dùng để tạo công thức mới và sau đó chuyển dữ liệu công thức. Khi user nhấp vào biểu tượng chỉnh sửa, biểu mẫu sẽ bật lên với dữ liệu được điền sẵn như sau:

Biểu mẫu chỉnh sửa điền sẵn cho ứng dụng thử nghiệm công thức

Sau đó, ta chạy một trình xử lý handleSubmit khác để chạy đột biến cập nhật như sau:

class GetAllPublishedRecipes extends Component {   // other logic   _updateRecipe = ({     id,     directions,     ingredients,     title,     published,     action   }) => {     this.props       .updateRecipeMutation({         variables: {           id,           directions,           title,           ingredients,           published: false         },         refetchQueries: [           {             query: GetAllPublishedRecipes           }         ]       })       .then(res => {         if (res.data.updateRecipe.id) {           this.setState(             (prevState, nextProps) => ({               isEditing: false             }),             () =>               this.setState(                 (prevState, nextProps) => ({                   notification: {                     notificationOpen: true,                     type: 'success',                     message: `recipe ${title} ${action} successfully`,                     title: 'Success'                   }                 }),                 () => this._handleResetState()               )           );         }       })       .catch(e => {         this.setState((prevState, nextProps) => ({           notification: {             ...prevState.notification,             notificationOpen: true,             type: 'error',             message: e.message,             title: 'Error Occured'           }         }));       });   }; } 

Xóa công thức

Đối với chức năng xóa, ta sẽ thực hiện soft-delete đối với công thức đã xóa, nghĩa là ta sẽ thay đổi thuộc tính đã published thành sai vì khi tìm nạp các bài báo ta lọc đảm bảo rằng ta chỉ nhận được các bài báo đã published .

Ta sẽ sử dụng chức năng tương tự như trước và chuyển vào được xuất bản là false, như trong ví dụ sau:

class GetAllPublishedRecipes extends Component {    // other logic     _handleOnDelete = ({ id, directions, ingredients, title }) => {     // user confirmed delete prompt      this._updateRecipe({       id,       directions,       ingredients,       title,       published: false, // soft delete the recipe       action: 'deleted'     });   }; }; 

Kết luận :

Trong hướng dẫn này, bạn đã xây dựng một ứng dụng công thức với React và GraphQL, sử dụng Prisma để quản lý server GraphQL của bạn . Prisma là một dịch vụ tin cậy cho phép bạn tập trung vào việc thực hiện logic kinh doanh của bạn.

Bạn có thể truy cập mã tại GitHub .


Tags:

Các tin liên quan