첫 프로덕션 하수라 배포에 대한 생각

18520 단어 javascriptgraphqlreact
Hasura는 데이터베이스에서 직접 API를 생성하는 도구입니다. 워크플로는 다음과 같이 요약됩니다.
  • 데이터베이스 테이블 및 관계 정의(일반 SQL DDL 문 사용)
  • 스핀 업 하수라
  • 권한 규칙 구성
  • 사용자 관리를 위해 au0과 같은 항목에 연결합니다
  • .
  • 현재 핫한 항목을 사용하여 UI 구현

  • 하수라에 대한 나의 첫 번째 생각은 그것이 얼마나 지루한가 하는 것입니다. 모든 것이 그냥 .. 작동합니까? 예를 들어, 앱에 새로운 기능을 추가해야 하는 자신을 발견하고 아주 적은 노력으로 많은 시간을 할애하여 작업을 완료하게 됩니다.

    UI 측면도 매우 훌륭합니다. 반응을 위한 클라이언트 코드를 생성하기 위해 @graphql-codegen/typescript-react-apollo를 사용하고 있습니다. graphQL 쿼리(가장 어려운 부분)를 작성하고 codegen을 실행하면 구성 요소에서 사용할 수 있는 후크가 제공됩니다.

    다음은 페이지 매김, 순서 지정 및 이벤트 이름으로 필터링하는 검색 필드를 포함하여 많은 데이터가 포함된 테이블을 그리는 예입니다. 우리가 프로덕션에서 사용하고 있는 것입니다.

    const EVENTS_QUERY = gql`
      query Events(
        $limit: Int = 10
        $offset: Int = 0
        $order_by: [events_order_by!] = []
        $where: events_bool_exp = {}
      ) {
        events(limit: $limit, offset: $offset, order_by: $order_by, where: $where) {
          date
          eventnumber
          name
          seriesevent {
            id
            seriesid
            series {
              seriesname
            }
          }
        }
        events_aggregate(where: $where) {
          aggregate {
            count
          }
        }
      }
    `;
    
    export const DEFAULT_PAGE_SIZE = 10;
    
    export const FieldContainsComparison = (s: string): String_Comparison_Exp => {
      return { _ilike: `%${s}%` };
    };
    
    export function EventListContainer(props: { searchText: string }) {
      const [offset, setOffset] = useState(0);
      const [orderBy, setOrderBy] = useState<Events_Order_By>({
        date: Order_By.Desc,
      });
    
      let filter: Events_Bool_Exp | undefined = undefined;
      if (props.searchText !== "") {
        filter = { name: FieldContainsComparison(props.searchText) };
      }
    
      const { loading, error, data, previousData, refetch } = useEventsQuery({
        variables: {
          limit: DEFAULT_PAGE_SIZE,
          offset: offset,
          where: filter,
          order_by: orderBy,
        },
      });
    
      const latest = data ?? previousData;
    
      if (error) return <div>Error: {error.message}</div>;
    
      /* Don't attempt to draw the table until after the first set of data has been loaded. */
      if (loading && !latest) return <Loading loading={loading} />;
    
      return (
        <>
          <Loading loading={loading} />
    
          <table>
            <thead>
              <tr>
                <td>
                  <div>
                    Event Number
                    <OrderByControls
                      setAsc={() => setOrderBy({ eventnumber: Order_By.Asc })}
                      setDesc={() => setOrderBy({ eventnumber: Order_By.Desc })}
                    />
                  </div>
                </td>
                <td>
                  <div>
                    Event Name
                    <OrderByControls
                      setAsc={() => setOrderBy({ name: Order_By.Asc })}
                      setDesc={() => setOrderBy({ name: Order_By.Desc })}
                    />
                  </div>
                </td>
                <td>
                  <div>
                    Date
                    <OrderByControls
                      setAsc={() => setOrderBy({ date: Order_By.Asc })}
                      setDesc={() => setOrderBy({ date: Order_By.Desc })}
                    />
                  </div>
                </td>
                <td>
                  <div>
                    Series
                    <OrderByControls
                      setAsc={() =>
                        setOrderBy({ seriesevent: { seriesid: Order_By.Asc } })
                      }
                      setDesc={() =>
                        setOrderBy({ seriesevent: { seriesid: Order_By.Desc } })
                      }
                    />
                  </div>
                </td>
                <td>Action</td>
              </tr>
            </thead>
            <tbody>
              {latest?.events.map((event) => (
                <tr key={event.eventnumber}>
                  <td>{event.eventnumber}</td>
                  <td>{event.name}</td>
                  <td>{event.date}</td>
                  <td>{event.seriesevent?.series?.seriesname ?? ""}</td>
                  <td>
                    <Link to={`/dashboard/events/${event.eventnumber}`}>
                      <img width="20" height="20" src="/edit.svg" />
                    </Link>
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
          <Pagination
            pageSize={DEFAULT_PAGE_SIZE}
            total={latest?.events_aggregate.aggregate?.count}
            offset={offset}
            setOffset={setOffset}
          />
        </>
      );
    }
    


    우리는 사용자 관리를 위해 Auth0을 사용했습니다. 올바른 페이로드로 JWT를 생성하는 방법을 알아내는 것은 분명히 간단하지 않았지만 그렇게 오래 걸리지도 않았습니다. hasura가 이러한 JWT를 수락하도록 하는 것은 매우 쉬웠습니다. JWT 암호를 복사하여 env 변수에 붙여넣기만 하면 됩니다.

    앱의 화면 중 하나에 타사 REST API의 데이터가 표시됩니다. 우리는 REST 끝점을 graphql 쿼리로 노출하기 위해 hasura 작업을 설정하고 다른 모든 것과 마찬가지로 graphql api 정의에 나타납니다. 꽤 깔끔한!

    그렇다면 무엇을 개선할 수 있을까요? 데이터 조작 경험에는 약간의 작업이 필요하다고 말하고 싶습니다. 레코드를 삽입하려고 하는데 문제가 있는 경우 제약 조건 위반 또는 권한 오류가 발생합니다. 최종 사용자를 위한 적절한 오류 메시지를 작성하기에는 정보가 충분하지 않습니다. 누락된 것으로 생각되는 또 다른 주요 기능은 필드를 선택 또는 필수로 표시하는 기능입니다. 현재 나오는 graphql 정의에는 모든 필드가 선택 사항으로 포함되어 있습니다. 많은 필드가 생략되면 오류가 발생한다는 것을 알고 있습니다. Hasura의 미친 펀딩을 통해 그들이 이러한 점을 해결할 수 있기를 바라지만 지금까지는 제품에 정말 만족합니다.

    좋은 웹페이지 즐겨찾기