😱

<備忘録>Amplifyハンズオンにハマった話(export 'API' (imported as 'API') was not found

2024/02/25に公開

試したハンズオン

フルスタック React アプリケーションを構築する
AWS Amplify を使用してシンプルなウェブアプリケーションを作成する
https://aws.amazon.com/jp/getting-started/hands-on/build-react-app-amplify-graphql/

所用時間:50 分と書いてあったから気軽に始めたら少しハマってしまった。

5つのモジュールに分かれている。

1、REACT アプリケーションをデプロイおよびホストする

https://aws.amazon.com/jp/getting-started/hands-on/build-react-app-amplify-graphql/module-one/
概ねスムーズに終わる

2、ローカル Amplify アプリを初期化する

https://aws.amazon.com/jp/getting-started/hands-on/build-react-app-amplify-graphql/module-two/
概ねスムーズに終わる

3、認証を追加する

https://aws.amazon.com/jp/getting-started/hands-on/build-react-app-amplify-graphql/module-three/
概ねスムーズに終わるが、ここで Version Latestを選択しないほうがよかったのかな?
(未検証)

4、 GraphQL API とデータベースを追加する

https://aws.amazon.com/jp/getting-started/hands-on/build-react-app-amplify-graphql/module-four/?e=gs2020&p=build-a-react-app-three

src/App.js のサンプルソースがエラーで実行できない!!

export 'API' (imported as 'API') was not found in 'aws-amplify' (possible exports: Amplify)

以下のissueに上がっているものをみると
https://github.com/aws-amplify/amplify-js/issues/12635
2023年11月ころにでたAmplify V6だと

import { API } from "aws-amplify";

ではなく

import { Amplify } from 'aws-amplify';
import { generateClient } from 'aws-amplify/api';
import config from './amplifyconfiguration.json';
Amplify.configure(config);

const client = generateClient();

という書き方に変わっているので、これ移行のハンズオンはV6のやり方で書かないとエラーがでるようだ。

モジュール4でうまくいったソース。

import React, { useState, useEffect } from "react";
import "./App.css";
import "@aws-amplify/ui-react/styles.css";
import { Amplify } from 'aws-amplify';
import { generateClient } from 'aws-amplify/api';
import { createTodo,  deleteTodo } from './graphql/mutations';
import config from './amplifyconfiguration.json';
import { listTodos } from './graphql/queries';

import {
  Button,
  Flex,
  Heading,
  Text,
  TextField,
  View,
  withAuthenticator,
} from "@aws-amplify/ui-react";


Amplify.configure(config);
const API = generateClient();

const App = ({ signOut }) => {
  const [notes, setNotes] = useState([]);

  useEffect(() => {
    fetchNotes();
  }, []);

  async function fetchNotes() {
    const apiData = await API.graphql({ query: listTodos });
    const notesFromAPI = apiData.data.listTodos.items;
    setNotes(notesFromAPI);
  }

  async function createNote(event) {
    event.preventDefault();
    const form = new FormData(event.target);
    const data = {
      name: form.get("name"),
      description: form.get("description"),
    };
    await API.graphql({
      query: createTodo,
      variables: { input: data },
    });
    fetchNotes();
    event.target.reset();
  }

  async function deleteNote({ id }) {
    const newNotes = notes.filter((note) => note.id !== id);
    setNotes(newNotes);
    await API.graphql({
      query: deleteTodo,
      variables: { input: { id } },
    });
  }

  return (
    <View className="App">
      <Heading level={1}>My Notes App</Heading>
      <View as="form" margin="3rem 0" onSubmit={createNote}>
        <Flex direction="row" justifyContent="center">
          <TextField
            name="name"
            placeholder="Note Name"
            label="Note Name"
            labelHidden
            variation="quiet"
            required
          />
          <TextField
            name="description"
            placeholder="Note Description"
            label="Note Description"
            labelHidden
            variation="quiet"
            required
          />
          <Button type="submit" variation="primary">
            Create Note
          </Button>
        </Flex>
      </View>
      <Heading level={2}>Current Notes</Heading>
      <View margin="3rem 0">
        {notes.map((note) => (
          <Flex
            key={note.id || note.name}
            direction="row"
            justifyContent="center"
            alignItems="center"
          >
            <Text as="strong" fontWeight={700}>
              {note.name}
            </Text>
            <Text as="span">{note.description}</Text>
            <Button variation="link" onClick={() => deleteNote(note)}>
              Delete note
            </Button>
          </Flex>
        ))}
      </View>
      <Button onClick={signOut}>Sign Out</Button>
    </View>
  );
};

export default withAuthenticator(App);

感想

V5のハンズオン多いだろうから意識しないとひっかかりそう。
サクッと検証終わらせるつもりがV6が動いててハマった話でした。

モジュール6も今度検証の続きをやってみます。。。

Discussion