はじめに
Strapiは新規にエントリを作成すると、連番のIDが自動で付与されます。
REST APIではこのIDを使って特定のエントリを取得できますが、連番のIDではなく、一意のフィールドを使って検索をしたいような場面がありました。
今回はバックエンドをカスタマイズして、Unique Filedの値でエントリを取得する方法を紹介します。
なお、Strapiに関しては過去に軽く解説をした記事を投稿したので、こちらを参照してください。
環境
- TypeScript
- Strapi: 4.19.1
この記事を読むとできるようになること
- バックエンドをカスタマイズできる
- Unique Fieldの値でエントリを取得できる
Unique Fieldの値で検索したい例
今回は、YYYY年MM月DD日の日記を検索したい、という例をモデルにします。
以下は日記Collection Typeのサンプルです。
フィールド | 属性 |
---|---|
date | date, required, unique |
body | text |
以下略 |
以下のような検索結果になることを目指します。
$ curl <baseUrl>/api/diaries/YYYY-MM-DD \
-H "Authorization:Bearer <token>
{"data":{"id":1,"attributes":{"date":"2024-01-01","body":"今日はいい天気でした","createdAt":"2024-02-09T01:04:08.931Z","updatedAt":"2024-02-09T01:04:08.931Z"}},"meta":{}}
デフォルトのREST APIを使う場合
デフォルトのREST APIを使う場合はどのような結果が得られるのか確認してみましょう。
Get entries
Get entriesはクエリフィルタに一致するエントリの一覧を返します。
こちらを使う場合はクエリパラメータにfilters[field][operator]=value
と指定することで特定の日付を検索できます。ただし配列形式で取得されてしまうことに注意しましょう。
$ curl "<baseUrl>/api/diaries?filters\[date\]\[$eq\]=2024-01-01" \
-H Authorization: Bearer <token>
{"data":[{"id":1,"attributes":{"date":"2024-01-01","body":"今日はいい天気でした","createdAt":"2024-02-09T01:04:08.931Z","updatedAt":"2024-02-09T01:04:08.931Z"}}],"meta":{"pagination":{"page":1,"pageSize":25,"pageCount":1,"total":1}}}
Get entriesの詳細については、こちらを確認してください。
クエリパラメータについても同様にリンク先で確認できます。
Get an entry
Get an entryはid
を元にエントリを検索します。
今回の場合、idは自動で付与される連番になっているため日付で検索するとエラーが返ります。
$ curl <baseUrl>/api/diaries/2024-01-01 \
-H Authorization: Bearer <token>
{"data":null,"error":{"status":404,"name":"NotFoundError","message":"Not Found","details":{}}}
Get an entryのレスポンス例についてはこちらから確認してください。
バックエンドをカスタマイズする
デフォルトの挙動では日付検索が不可能だと確認できたため、Strapiのバックエンドをカスタマイズしていきたいと思います。
Strapi v4のドキュメントには丁度、Back-end customizationという章が用意されています。
上記ページに掲載されている図と照らし合わせると、今回の場合はコントローラーを編集すれば日付で検索できるようになりそうです。
Unique Fieldの値で検索する
早速、コントローラーを編集して日付で検索できるようにしてみましょう。
編集対象のファイルは./src/api/[api-name]/controllers/
にあります。今回の場合は./src/api/diary/controllers/diary.ts
です。
JavaScriptの場合はモジュール部分など適宜書き変えてください。
/**
* diary controller
*/
import { factories } from '@strapi/strapi'
export default factories.createCoreController('api::diary.diary', ({ strapi}) => ({
async findOne(ctx) {
await this.validateQuery(ctx);
const sanitizedQueryParams = await this.sanitizeQuery(ctx);
const entry = await strapi.db.query('api::diary.diary').findOne({
where: {
date: ctx.params.id
},
select: sanitizedQueryParams.fields
});
if (!entry) {
return ctx.notFound();
}
const sanitized = await this.sanitizeOutput(entry, ctx);
return this.transformResponse(sanitized);
}
}));
サニタイズとバリデーション
処理の最初とレスポンス前でサニタイズとバリデーションを行っています。
以下の部分です。
await this.validateQuery(ctx);
const sanitizedQueryParams = await this.sanitizeQuery(ctx);
// ...
const sanitized = await this.sanitizeOutput(entry, ctx);
これらは各種設定に基づいてデータのサニタイズを行っています。
関数の概要など詳細については、こちらから確認できます。
Query Engine API
実際の検索はQuery Engine APIを使っています。
コードでは以下の部分です。
const entry = await strapi.db.query('api::diary.diary').findOne({
where: {
date: ctx.params.id
},
select: sanitizedQueryParams.fields
});
Query Engine APIはStrapiが提供しているAPIの一つで、より低いレベルでデータベース・レイヤーと対話できます。
現時点(2024年2月現在)では、Unique Field検索はこのQuery Engine APIを使うほかないようです。
しかしほとんどのケースでは、Entity Service APIが推奨されています。
👉 In most use cases, it's recommended to use the Entity Service API instead of the Query Engine API.
最終確認
編集後、Get an entryのエンドポイントをもう一度叩いてみます。
$ curl <baseUrl>/api/diaries/2024-01-01 \
-H Authorization: Bearer <token>
{"data":{"id":1,"attributes":{"date":"2024-01-01","body":"今日はいい天気でした","createdAt":"2024-02-09T01:04:08.931Z","updatedAt":"2024-02-09T01:04:08.931Z"}},"meta":{}}
無事、期待通りの検索結果を受け取ることができました。
まとめ
今回はStrapiのバックエンドをカスタマイズして、Unique Fieldの値でエントリを取得する方法を紹介しました。