Sử dụng React và Apollo lấy dữ liệu từ GraphQL server



  • Introduce to GraphQL and Apollo

    GraphQL là một ngôn ngữ truy vấn dành cho API, nó cho phép client có thể yêu cầu chính xác những dữ liệu mà client mong muốn nhận được từ server. Được phát triển bởi Facebook vào năm 2012, giờ đây GraphQL đang được sử dụng bởi chính Facebook và những công ty khác như là Shopify và Github. GraphQL là một mã nguồn mở và được bảo trì bởi Facebook.

    Trong khi REST API thường gửi request tới rất nhiều endpoint thì GraphQL cho phép bạn gửi request tới một endpoint duy nhất để thao tác với dữ liệu. Với việc sử dụng GraphQL thì các lập trình viên frontend có thể truy vấn dựa trên những dữ liệu họ cần chứ không phải phụ thuộc vào server trả về những dữ liệu gì. Việc kiểm soát version của api đối với REST API khá là đau đầu với các lập trình viên, tuy nhiên với GraphQL thì nó cho phép bạn thêm các field hoặc các type rất dễ dàng mà không ảnh hưởng gì tới các query trước đó. Những field không sử dụng đến cũng không cần phải lo bị thừa dữ liệu trả về cho client. GraphQL cho phép ứng dụng của chúng ta có thể ngay lập tức sử dụng các API mới và việc bảo trì cũng sẽ dễ dàng hơn.

    Có vẻ GraphQL thì rất là hay rồi, vậy thì sau đây chúng ta hãy thử implement nó nhé :D

    Example

    Set up backend

    Ở đây bài viết của mình là sử dụng react và apollo để lấy dữ liệu về từ server GraphQL nên mình xin phép sử dụng một server dựng sẵn sử dụng express. Các bạn có thể clone về theo link này nhé https://github.com/nguyenminhtu/react-apollo-graphql/tree/master/backend

    Các bạn có thể copy 2 file đó về rồi chạy npm install sau đó chạy tiếp node server.js (Để chạy được command này thì máy các bạn phải cài đặt nodejs trước đó rồi nhé. Nếu chưa cài các bạn có thể tham khảo ở đây https://nodejs.org/en/download/package-manager/#debian-and-ubuntu-based-linux-distributions).

    Khi màn hình console log ra là Vào đường dẫn http://localhost:4000/graphiql để query thử nhé! thì các bạn đã chạy server thành công rồi đó :))) Giờ chỉ việc mở trình duyệt ra và gõ vào http://localhost:4000/graphiql

    nếu các bạn ra được như này là chạy đúng rồi nhé :)))

    Vậy là chúng ra đã có server rồi, giờ tạo client để lấy dữ liệu về nào :D

    Set up frontend

    Ở đây mình sử dụng create-react-app để tạo project react, đây là tool của facebook nên dùng rất tiện (các bạn có thể tham khảo thêm ở đây https://github.com/facebook/create-react-app)

    create-react-app client
    

    Khi bạn chạy command trên thì create-react-app sẽ tạo ra 1 project react hoàn chỉnh cho bạn. Sau rồi bạn chỉ cần cd client rồi chạy npm start hoặc yarn start tùy máy của bạn cài npm hay yarn. create-react-app cũng đã cài đặt sẵn cả hot reload hay là cả build production nếu bạn muốn :)))

    Tiếp theo các bạn chạy command

    npm i --save apollo-boost graphql react-apollo graphql-tag
    

    apollo-boost là package bao gồm những package cần thiết khác của apollo như apollo-client apollo-cache-inmemory hay apollo-link-http.
    react-apollo sử dụng 1 HOC(High Order Component) tên là graphql để truyền dữ liệu vào trong component của chúng ta thông qua prop.
    graphql-tag sẽ giúp parse câu truy vấn thành đúng chuẩn GraphQL để gửi lên server.

    Để sử dụng thì đầu tiên ta phải import các thư viện cần thiết vào để sử dụng

    // src/App.js
    
    import React, { Component } from 'react';
    import gpl from 'graphql-tag';
    import { graphql } from 'react-apollo';
    

    Tiếp theo chúng ta sẽ viết câu query GraphQL để lấy dữ liệu về

    // src/App.js
    
    import React, { Component } from 'react';
    import gpl from 'graphql-tag';
    import { graphql } from 'react-apollo';
    
    const AppQuery = gpl`
      query {
        animals {
          name
        }
      }
    `;
    

    Câu query trên giúp chúng ra lấy về list animals và chỉ lấy về 1 field duy nhất là name, điều này giúp chúng ta tránh đc dư thừa dữ liệu trả về từ server nếu chẳng may server serializer dữ liệu trả về không tốt. Do dữ liệu giả trên server của mình chỉ có 1 field là name nên mình chỉ lấy về name để hiển thị ra thôi. Còn trong bài toàn thực tế các bạn có thể lấy về nhiều trường hơn tùy vào mục đích sử dụng :)))

    À nếu bạn thắc mắc ở đâu chúng ta có câu query như này thì các bạn có thể vào file server.js để xem. Trong typeDefs ta đã khai báo 1 query là animals

    // server.js
    
    const typeDefs = `
      type Query {
       animals: [Animal]
      }
    
      type Animal {
       name: String
      }
    `;
    

    có kiểu trả về là Animal và trong resolvers

    // server.js
    
    const resolvers = {
      Query: {
        animals: () => animals
      },
    };
    

    ta định nghĩa query animals đó trả về list dữ liệu giả ta đã khai báo ở trên

    // server.js
    
    const animals = [
      {
        name: "Dog",
      },
      {
        name: 'Cat',
      },
    ];
    

    Rồi sau khi đã định nghĩa câu query để lấy dữ liệu thì chúng ra phải đưa được dữ liệu đó vào trong component để sử dụng

    // src/App.js
    
    const AppWithData = graphql(AppQuery)(App);
    

    Đoạn code trên sử dụng HOC tên là graphql của react-apollo để đưa dữ liệu thông qua props vào trong component App của chúng ta. Mặc định thì HOC này sẽ truyền vào props là data, trong đó có khá nhiều các dữ liệu cũng như function hay ho bạn có thể tìm hiểu thêm như là loading.... Với câu query ta viết như trên thì ta sẽ có 1 trường dữ liệu là animals mà ta có thể lấy ra bằng cách this.props.data.animals.

    Cuối cùng thì ta hiển thị dữ liệu lấy được về như sau

    // src/App.js
    
    const App = ({ data: { loading, animals } }) => {
      if (loading) {
        return <p>Loading...</p>;
      }
      return (
        <div className='App'>
          {animals.map((animal, index) => {
            return (
              <p key={index}>
                {animal.name}
              </p>
            );
          })}
        </div>
      );
    };
    

    Ở đây mình sử dụng stateless component hoặc các bạn có thể sử dụng statefull component như sau

    // src/App.js
    
    class App extends Component {
      render() {
      const { loading, animals } = this.props.data;
        if (loading) {
          return <p>Loading...</p>;
        }
        return (
          <div className='App'>
            {animals.map((animal, index) => {
              return (
                <p key={index}>
                  {animal.name}
                </p>
              );
            })}
          </div>
        );
      }
    }
    

    Do component của mình chỉ đơn giản là nhận vào dữ liệu từ props và hiển thị ra nên mình sử dụng stateless component :)))

    Cuối cùng thì file App.js sẽ trông như thế này

    // src/App.js
    
    import React from 'react';
    import gpl from 'graphql-tag';
    import { graphql } from 'react-apollo';
    
    const AppQuery = gpl`
      query {
        animals {
          name
        }
      }
    `;
    
    const App = ({ data: { loading, animals } }) => {
      if (loading) {
        return <p>Loading...</p>;
      }
      return (
        <div className='App'>
          {animals.map((animal, index) => {
            return (
              <p key={index}>
                {animal.name}
              </p>
            );
          })}
        </div>
      );
    };
    
    const AppWithData = graphql(AppQuery)(App);
    
    export default AppWithData;
    

    Tiếp theo chúng ra phải set up main component

    /// src/index.js
    
    import ApolloClient from 'apollo-boost';
    import { ApolloProvider } from 'react-apollo';
    

    ApolloProvider cung cấp 1 thể hiện của ApolloClient tới cho tất cả các component con của nó, giúp cho các component con của nó sử dụng HOC graphql có thể hoạt động được. Tiếp theo ta phải khai báo 1 thể hiện của ApolloClient như sau

    // src/index.js
    
    const client = new ApolloClient({ uri: 'http://localhost:4000/graphql' });
    

    Đoạn code trên chỉ định url của server GraphQL là http://localhost:4000/graphql.

    Tiếp theo ta chỉ việc bọc tất cả component của chúng ra ở trong ApolloProvider là được rồi

    // src/index.js
    
    ReactDOM.render(
      <ApolloProvider client={client}>
        <App />
      </ApolloProvider>
      , document.getElementById('root'));
    

    Cuối cùng thì file index.js sẽ như thế này

    // src/index.js
    
    import React from 'react';
    import ReactDOM from 'react-dom';
    
    import ApolloClient from 'apollo-boost';
    import { ApolloProvider } from 'react-apollo';
    
    import './index.css';
    import App from './App';
    import registerServiceWorker from './registerServiceWorker';
    
    const client = new ApolloClient({ uri: 'http://localhost:4000/graphql' });
    
    ReactDOM.render(
      <ApolloProvider client={client}>
        <App />
      </ApolloProvider>
      , document.getElementById('root'));
    registerServiceWorker();
    

    Conclusion

    Vậy là đã xong, các bạn có thể truy cập vào browser thông qua url http://localhost:3000 để kiểm tra xem có ra đúng như này không nhé

    Ra được đúng như thế là các bạn đã implement thành công react và apollo để lấy dữ liệu từ server GraphQL rồi đấy :D
    Đây là repo của mình, các bạn có thể tham khảo source code ở đây nhé: https://github.com/nguyenminhtu/react-apollo-graphql

    GraphQL đang ngày càng mạnh mẽ hơn và được sử dụng rất nhiều, nó đang dần thay thế REST trở thành sự lựa chọn tốt nhất để viết API. Ở đây mình chỉ giới thiệu một ví dụ đơn giản thôi còn trong thực tế sẽ phức tạp hơn rất nhiều, nhưng đây cũng là nền tảng để chúng ta có thể nghiên cứu và implement các thứ phức tạp sau này.

    References

    https://www.apollographql.com/docs/react/quick-start.html

    http://graphql.org/learn/
    Nguồn: Viblo



Có vẻ như bạn đã mất kết nối tới LaptrinhX, vui lòng đợi một lúc để chúng tôi thử kết nối lại.