UGA Boxxx

つぶやきの延長のつもりで、知ったこと思ったこと書いてます

【Prisma】実行時にdebian-openssl周りでエラー

Prismaを導入してローカルでの確認ができたのでAppEngineにデプロイして動作確認したところエラーが発生した

Prisma Client could not locate the Query Engine for runtime "debian-openssl-1.1.x".

This happened because Prisma Client was generated for "debian-openssl-3.0.x", but the actual deployment required "debian-openssl-1.1.x".
Add "debian-openssl-1.1.x" to `binaryTargets` in the "schema.prisma" file and run `prisma generate` after saving it:

generator client {
provider = "prisma-client-js"
binaryTargets = ["native", "debian-openssl-1.1.x"]
}

調べたところ、これはPrismaが利用しようとしているOpenSSLのバージョンがOSのバージョンと一致していないことが原因とわかった

binaryTargetsに何か指定しろとのことなので、どうすべきか調べる

バイナリターゲット

Prisma Client はコードを実行するプラ​​ットフォームに応じたバイナリターゲットを指定する必要がある

上のエラーメッセージになるnativedebian-openssl-1.1.x については

ドキュメントによるとnativeを指定すると、Prisma Client が現在のOSを検出し、そのOSに対する正しいバイナリ ターゲットを自動的に指定してくれるみたい

www.prisma.io

例えば、macOSの2つのバージョン違い(Intel x86 と ARM64)を自動に検知してくれるので指定しておくと良さそう

次に、debian-openssl-1.1.xはデプロイするOSに応じて指定するもの

Linux debian系であれば以下から選択する
https://www.prisma.io/docs/orm/reference/prisma-schema-reference#linux-debian-x86_64

なので、AppEngineにデプロイするOSを調べる必要がある

OSバージョン

AppEngineにデプロイするDokerイメージはgcr.io/distroless/nodejs:18を使っている

ちょっと古いようで全く同じイメージについては調べられなかったが以下をみると、debian11かdebian10を使っていそうということがわかった

distroless/nodejs at main · GoogleContainerTools/distroless · GitHub

先ほどのPrismaの表と照らし合わせるとdebian-openssl-1.1.xを指定すれば良いことがわかった

ということで、schema.prismaに以下を記述したら無事に動いた

generator client {
  provider = "prisma-client-js"
  binaryTargets = ["native", "debian-openssl-1.1.x"]
}

【Prisma】デプロイ時にエラーになった

Prismaを導入してローカルでの確認ができたのでAppEngineにデプロイしようとしたところデプロイ時にエラーになってしまった

エラーメッセージは以下

Error: @prisma/client did not initialize yet. Please run "prisma generate" and try to import it again.
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report

簡単な話、build時にprisma generateしてね、というものだったのでpackage.jsonでビルドコマンドを以下のようにしたら動いた

"scripts": {
  ...
  "build": "prisma generate && next build",
  ...
}

【Prisma】Prismaの .envファイルをNext.jsのenvファイルと合わせたい

Next.jsでPrismaを使う準備をした

uga-box.hatenablog.com

この準備のさなか、.env というファイルが自動生成されたので、その中でDBへの接続先URLなどを記載して動作確認を行なったのだが、Next.jsでは.env.localというファイルをローカル開発では使っていて、本番環境用には.env.productionというファイルを用意しているので、Prisma環境変数の設定もこれらのファイルに記載したい

色々試した結果、これは特にライブラリを用意する必要はなく、素直に .env.local と .env.production に書くことで問題なく動いた🤔

.env.local

...
PRISMA_DATABASE_URL=postgresql://user:password@localhost:5432/postgres
...

schema.prisma

...
datasource db {
  provider = "postgresql"
  url      = env("PRISMA_DATABASE_URL")
}
...

ドキュメントにはdotenv-cliというライブラリが必要とのことだがよくわかっていない
www.prisma.io

おそらく、.env.sample のようなNODE_ENVではない接尾辞をもつファイルを作りたい時には必要なのかもしれない?が今回は不要だった

【Prisma】prisma client のセットアップ

Next.jsからDBにアクセスする機会がなく、知ってはいたが使ったことがなかったPrisma client のセットアップする

www.prisma.io

DBはPostgreSQLがすでにあり、データも入っているのでDB作成や更新はなく、接続して検索するのみ

インストール

npm install prisma

PostgreSQLを使っているので、--datasource-provider postgresqlを指定して初期化する

npx prisma init --datasource-provider postgresql

すると、prisma/schema.prismaenvファイルが生成される

prisma/schema.prismaには以下の情報が記載されていて

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

.envファイルにはDATABASE_URLのキー名に接続先のURLが記載されているのでこれを接続先のDBのURLに変える

モデルの作成

schema.prisma にデータモデルを定義するのだが、すでにDBはあるのでそれを素に schema.prisma に反映したい

そこで db pull コマンドを使う

npx prisma db pull

https://www.prisma.io/docs/orm/reference/prisma-cli-reference#db

これを使うとschema.prismaが更新される(上書きなので注意)

Prisma Client を生成する

schema.prismaを素に今度は Prisma Client を生成する

npx prisma generate

これによりデータモデルに沿った型定義などが生成され、補完も効くようになる

検索する

PrismaClientをimportし、PrismaClientのインスタンスを作る

今回はfindManyで検索をする

import { PrismaClient } from "@prisma/client";

const prisma = new PrismaClient();

const result = await prisma.my_table.findMany({
  where: {
    my_field: id,
  },
});

これでPrismaを使う準備ができた

【Spring Boot】Apollo3が利用できなかった話

Spring Boot にApollo Clientを導入しようとした際に、Apolloのバージョンがv2系とv3系があることがわかり、当初v3系にしようとしていたがうまくいかなかった

uga-box.hatenablog.com

前提としてSpring Boot は2.5.12を使っていて、古いのは承知しているが今すぐバージョンを上げられないため、このバージョンで動くようにする必要がある

うまくいかなった理由は、Rx3Apollo の部分でランタイム エラーが発生したこと

同じ事象のissueが上がっていたので読むと
github.com

これはspring boot のバージョンと中で使われている kotlinx-coroutines-* のバージョンがアンマッチが原因とわかった

kotlinx-coroutines-* のバージョンが1.6.1である必要があるが、以下をみると確かに<kotlin-coroutines.version>1.5.2</kotlin-coroutines.version>が使われている

https://repo1.maven.org/maven2/org/springframework/boot/spring-boot-dependencies/2.5.12/spring-boot-dependencies-2.5.12.pom

ということで、spring-bootのバージョンを上げられないので、Apollo Kotlin v2を使うことにした

【Spring Boot】GraphQL Apollo Client でリクエスト時にヘッダーにAuth情報を含めたい

以前セットアップしたApollo Client でリクエスト時にヘッダーにAuth情報を含めたい

uga-box.hatenablog.com

設定方法は以下に書かれている

10. Authenticate your queries - Apollo GraphQL Docs

Kotlin用をJavaに読み替えて、以下のように書くけば良さそう

    OkHttpClient okHttp =
        new OkHttpClient.Builder()
            .addInterceptor(
                chain -> {
                  Request request =
                      chain
                          .request()
                          .newBuilder()
                          .method("POST", chain.request().body())
                          .addHeader("X-Api-Key", properties.getAuth().getKey())
                          .build();
                  return chain.proceed(request);
                })
            .build();

    ApolloClient client = ApolloClient.builder()
        .serverUrl("https://apollo-fullstack-tutorial.herokuapp.com/")
        .okHttpClient(okHttp)
        .build();

Spring boot 2.5系の場合エラーになる

上の実装をして実行したところ、以下のランタイムエラーが発生した

java.lang.NoSuchFieldError: Companion

これはcom.squareup.okhttp3:okhttp:4.xが期待される所でcom.squareup.okhttp3:okhttp:3.x が利用されたということで発生するエラー

下を見ると、確かにspring-bootのv2.5系のokhttpはokhttp3がv3系なので、これが原因だった

https://repo1.maven.org/maven2/org/springframework/boot/spring-boot-dependencies/2.5.12/spring-boot-dependencies-2.5.12.pom

pomに直接okhttp3をv4系にあげてみるとエラーが発生しなくなった

動くようにはなったが、spring-bootが古いのはよくない状態なので、一旦はこれで様子を見てspring-bootのバージョンアップを検討する

参考

https://wrongwrong163377.hatenablog.com/entry/2022/01/19/192535

【Spring Boot】Spring Boot でGraphQL Apolloクライアントを使う

Spring Boot でGraphQLで構築された外部のAPIを利用するため、GraphQLクライアントを用意する

GraphQLクライアントには Apollo や Relay が思いつくが、RelayはどちらかというとReact特化でApolloのようにさまざまな言語で使えるようになっていないとのこと

参考記事
最高のGraphQLクライアントを求めて Apollo vs Relay - nakamoriのblog

そのため Apollo でGraphQLクライアントをセットアップすることにした

セットアップ

Spring Boot のバージョンは 2.5.12(理由があって上げられない)、Apollo クライアントは v2系を使う(理由は別記事に書く)

以下の公式ドキュメントと
4. Execute your first query - Apollo GraphQL Docs

以下の記事が大変参考になったのでこれらを見ながら進める
Java × Spring Boot × Apollo で GraphQL クライアントを実装する #Java - Qiita

pom.xml に記載

まず、pom.xml に必要とするライブラリ記載する

この時、同期通信を簡単に書くために RxJava で書けるライブラリも記載する

    <dependency>
      <groupId>com.apollographql.apollo</groupId>
      <artifactId>apollo-runtime</artifactId>
      <version>2.5.14</version>
    </dependency>

    <dependency>
      <groupId>com.apollographql.apollo</groupId>
      <artifactId>apollo-rx3-support</artifactId>
      <version>2.5.14</version>
    </dependency>

また、Apollo による Query モデルクラスの自動生成を行うため、以下のプラグインも記載する

GitHub - aoudiamoncef/apollo-client-maven-plugin at support/apollo-2

<plugins>
    <plugin>
        <groupId>com.github.aoudiamoncef</groupId>
        <artifactId>apollo-client-maven-plugin</artifactId>
        <version>4.0.6</version>
        <executions>
            <execution>
                <goals>
                    <goal>generate</goal>
                </goals>
                <configuration>
                    <services>
                        <example-api>
                            <compilationUnit>
                                <name>example</name>
                                <compilerParams>
                                    <rootPackageName>com.example.graphql.client</rootPackageName>
                                </compilerParams>
                            </compilationUnit>
                        </example-api>
                    </services>
                </configuration>
            </execution>
        </executions>
    </plugin>
</plugins>

Query モデルクラスの自動生成

先ほど入れたpluginのintrospectionの箇所に、利用する外部APIの情報や出力先を記載する

mvn コマンドで generate-sourcesを実行すると、src/main/graphql/exampleに schema.json が生成されている

さらに、その同フォルダに GraphQLクエリファイルを置き、再度プラグインを実行すると target 配下に Query モデルクラスが自動生成されているので、このフォルダにクラスパスを通しておく

クライアントインスタンスの生成

以下のようにApolloクライアントのインスタンスを生成する

final ApolloClient apolloClient = ApolloClient.builder()
        .serverUrl("http://localhost:5000/graphql/endpoint")
        .build();

このクライアントと先ほど生成したクエリを使って実行する

        MyQuery query = MyQuery.builder().build();

         ApolloQueryCall<Data> apolloQueryCall = apolloClient.query(query);

        return Rx3Apollo.from(apolloQueryCall)
                .map(Response::getData)
                .map(MyQuery.Data::getOffers)
                .blockingFirst();

これで一旦、単純なセットアップは完了した

リクエスト時にヘッダーにAuth情報を含めたい場合はどうするかなど、詰まったところは別記事にする

ApolloクライアントをつかないGraphQLクライアントのセットアップ

試していないが spring-boot-starter-graphqlでGraphQLクライアントをセットアップすることも可能みたい

www.danvega.dev

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-graphql</artifactId>
</dependency>

簡単に始められそうだが、スキーマ情報はない中でクエリを書くみたいで後々苦労しそう?