Рубрики
Без рубрики

Как создать поддельный API-сервер для приложений React с помощью MirageJS

Допустим, вы хотите быстро создать прототип приложения, но у вас еще нет готовых бэкенд API, что делать в этом случае? Лучший способ – это иметь имитационные данные с поддельного сервера Mirage.js.

Если вы создаете веб-приложение с помощью React, Vue, Angular или любого другого вашего любимого front-end фреймворка, вам необходимо взаимодействовать с backend API для CRUD-операций. Допустим, вы хотите быстро создать прототип приложения, но у вас еще нет готовых бэкенд API, что делать в этом случае? Лучший способ – это иметь имитационные данные с поддельного сервера.

Как создать имитационные данные, у нас есть так много библиотек, которые могут помочь нам достичь этой цели, но в этом посте я рассматриваю использование miragejs с React.

Почему я рассматриваю именно эту библиотеку, хотя есть и другие популярные библиотеки, по двум причинам, первая – вам не нужно создавать/крутить другой сервер для загрузки ваших данных, например: http://localhost:3001, где работает ваш mock-сервер, но mirage работает на том же сервере разработки и позволяет вам получить доступ к данным, как будто вы работаете с реальным API, и второе, вы можете использовать mirage в качестве конечной точки API для написания сквозных тестов с помощью Cypress, я даже не думал о других вариантах, когда я получил 2 преимущества, просто создав mock-сервер с mirage, и он предлагает большой опыт разработчика, на мой взгляд.

Вы также можете использовать его для имитации конечных точек API с react-testing-library для написания юнит-тестов. Пожалуйста, обратитесь к документации для получения более подробной информации.

Давайте начнем, создадим react-приложение с помощью create-react-app и добавим это в index.js. Запускаем макет сервера только во время разработки.

// index.js
import React from "react";
import ReactDOM from "react-dom";

import { makeServer } from "./server";
import UsersLayout from "./users-layout";

// It creates the mock server only in development mode
if (process.env.NODE_ENV === "development") {
  makeServer({ environment: "development" });
}

const App = () => <UsersLayout />;

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Создайте server.js, где настоящее волшебство происходит с меньшим количеством кода,

// server.js
import { createServer, Model } from "miragejs";

export function makeServer({ environment = "test" } = {}) {
  let server = createServer({
    environment,

    models: {
      user: Model,
    },

    seeds(server) {
      server.create("user", { id: 1, name: "Bob Jhon" });
      server.create("user", { id: 2, name: "Alice" });
    },

    routes() {
      this.namespace = "api";

      this.get("/users", (schema) => schema.users.all());
      
      // To increment the id for each user inserted,
      // Mirage auto creates an id as string if you don't pass one
      let newId = 3
      this.post("/users", (schema, request) => {
        const attrs = JSON.parse(request.requestBody);
        attrs.id = newId++
        
        return schema.users.create(attrs);
      });

      this.delete("/users/:id", (schema, request) => {
        const id = request.params.id;

        return schema.users.find(id).destroy();
      });
    },
  });

  return server;
}

Метод seeds() засеет нашу модель пользователя некоторыми начальными данными, чтобы мы могли сразу начать ее использовать, вы можете оставить его пустым, если хотите начать с пустой коллекции пользователей.

Определите все ваши маршруты API в методе routes() и вы можете определить ваше пространство имен API с помощью this.namespace = ‘api’, чтобы вам не пришлось повторять его во всех маршрутах, например: this.get(‘/api/users’). Здесь у меня есть три маршрута для GET, POST и DELETE пользователя.

Вам нужно создать модель с помощью mirage Model и с ее помощью вы можете получить доступ к данным из схемы, если вы внимательно посмотрите, я создал модель пользователя с именем user, но доступ к ней как schema.users.all(), mirage создает множественную коллекцию для нас, глядя на имя модели, хорошая практика – сохранять единственные имена для ваших моделей.

Mirage предлагает другие методы на схеме для добавления и удаления элемента из коллекции, см. delete и post API маршруты в примере кода выше.

Вот и все, давайте напишем React-часть кода, чтобы мы могли использовать поддельный API Mirage с помощью fetch или axios, я использую fetch.

// users-layout.js
import React, { useState, useEffect, useCallback } from "react";
import { useFetch } from "./use-fetch";

export default function UsersLayout() {
  const [users, setUsers] = useState([]);
  const { data, loading: userLoading, error: userError } = useFetch(
    "/api/users"
  );
  const [name, setName] = useState("");
  const [isUpdating, setIsUpdating] = useState(false);

  useEffect(() => {
    if (data) {
      setUsers(data.users);
    }
  }, [data]);

  const onAddUser = useCallback(
    async (e) => {
      e.preventDefault();
      try {
        setIsUpdating(true);
        const res = await fetch("/api/users", {
          method: "POST",
          body: JSON.stringify({ name }),
        });

        const data = await res.json();
        setUsers((users) => users.concat(data.user));
        setIsUpdating(false);
        setName("");
      } catch (error) {
        throw error;
      }
    },
    [name]
  );

  return (
    <>
      <form onSubmit={onAddUser}>
        <input
          type="text"
          onChange={(e) => setName(e.target.value)}
          value={name}
        />
        <button type="submit" disabled={isUpdating}>
          {isUpdating ? "Updating..." : "Add User"}
        </button>
      </form>
      {userError && <div>{userError.message}</div>}
      <ul>
        {!userLoading &&
          users.map((user) => <li key={user.id}>{user.name}</li>)}
      </ul>
    </>
  );
}

И в качестве бонуса в приведенном выше коде я написал пользовательский хук для получения данных useFetch с любых конечных точек API. Давайте посмотрим на код для useFetch

// use-fetch.js
import { useEffect, useState, useRef } from "react";

/**
 * Hook to fetch data from any API endpoints
 */
export const useFetch = (url) => {
  const [state, setState] = useState({
    data: null,
    loading: true,
    error: null,
  });
  const isCurrent = useRef(true);

  useEffect(() => {
    return () => {
      isCurrent.current = false;
    };
  }, []);

  useEffect(() => {
    setState((state) => ({ ...state, loading: true }));
    const getData = async () => {
      try {
        const res = await fetch(url);
        const data = await res.json();

        // If calling component unmounts before the data is
        // fetched, then there is a warning, "Can't perform
        // React state update on an unmounted component"
        // it may introduce side-effects, to avoid this, useRef to
        // check for current reference.
        if (isCurrent.current) {
          setState((state) => ({
            ...state,
            data,
            loading: false,
            error: null,
          }));
        }
      } catch (error) {
        setState((state) => ({ ...state, error: error }));
      }
    };

    getData();
  }, [url]);

  return state;
};

Вот и все, с небольшими усилиями вы можете подделать данные с помощью поддельного API-сервера, используя miragejs. И mirage хорошо масштабируется с большими приложениями, я проверил это в бою и надеюсь, что вы найдете это полезным. Попробуйте на своем следующем проекте. Это сэкономит много времени во время разработки.

Оригинал: “https://www.codementor.io/@kpunith8/how-to-create-fake-api-server-for-react-apps-with-miragejs-1baqn9nc4d”