PlanetScaleとPrisma ORMによるサーバーレスAPIの構築と配備
43899 ワード
もともとはNitric Site
このガイドでは、Serverlessなフレームワークを使用してAPIを作成しますNitric . これは、Nine Serverlessな関数を使用しPlanetScale and Prisma データ持続性のために.その後、アプリケーションをお好みの雲に展開します.AWS , GCP , or Azure .
物事を点灯するには、APIを作成するイメージテンプレートのアップロードを許可することによって、テキストを生成すると、新しいイメージを生成することでmemesを生成します.あなたが構築したい他のAPIのための手順を適応させてください.
完了したら、テンプレートイメージを受け入れるAPIを持っているし、そこから複数のミームを生成することができます.
このガイドを完成するには、前もってセットアップを必要とすることがあります. とのアカウントPlanetScale 無料です
Node.js
安AWS , GCP or Azure 説明する
新しい窒素プロジェクトから始めましょう.
イメージ編集のために後でGIMPを使うので、今もインストールしましょう.
次に、PlanetScaleデータベースが必要になります.アカウントが既にある場合は、次の手順に進みます.そうでなければ、あなたはsign up 無料のアカウント.
PlanetScale CLIまたはWebダッシュボードを使用して新しいデータベースを作成できます.
以下はCLIを使用した例です.
ここでセットアップPrismaに準備ができました.NPMでプロジェクトに追加してみましょう.
そして、最初のスキーマファイルを生成し、最初のスキーマファイルを生成できます.
Prismaの内容を上書きします.スキーマ以下のスキーマ.これを使用してデータベースを初期化します.
現在、我々のスキーマは準備ができています.最も簡単な方法は
終わったら
アプリケーションは、コード内のリソースを定義してビルドすると、任意のルートでこれを書くことができます
まず、APIゲートウェイを宣言しましょう.新しいファイルを作成する
次に、私たちのmemeイメージファイルを格納するためにいくつかのバケツを作成しましょう.新しいファイルを作成する
リソースが宣言されたので、最初のサービスを作成しましょう.このサービスでは、APIコンシューマーは、ミームとベースの設定可能なセットのベースイメージを提供することによって新しいミームテンプレートを登録することができます.
に
また、テンプレート画像を保存するために使用されるバケットをインポートしている
着信
この
また、リクエスト
APIを確立したので、ローカルでテストしましょう.
以下はAPIをテストするためのリクエスト例です.
前の要求から返されたmeme idを使用してブラウザを開き、
準備ができたら、このプロジェクトをAWS、AzureまたはGoogleクラウドに配備できます.この例では、AWSの手順を示しますが、すべてのケースで本質的に同じです.
を定義する
実行して新しいスタックを作成できます
あなたがこのガイドに質問やフィードバックを持っているなら、私はあなたから聞いてみたい.コメント、またはGitHub
何をするか
このガイドでは、Serverlessなフレームワークを使用してAPIを作成しますNitric . これは、Nine Serverlessな関数を使用しPlanetScale and Prisma データ持続性のために.その後、アプリケーションをお好みの雲に展開します.AWS , GCP , or Azure .
物事を点灯するには、APIを作成するイメージテンプレートのアップロードを許可することによって、テキストを生成すると、新しいイメージを生成することでmemesを生成します.あなたが構築したい他のAPIのための手順を適応させてください.
For image editing, we used a library from NPM called jimp, but you could use anything else you like.
完了したら、テンプレートイメージを受け入れるAPIを持っているし、そこから複数のミームを生成することができます.
必要条件
このガイドを完成するには、前もってセットアップを必要とすることがあります.
始める
新しい窒素プロジェクトから始めましょう.
# create the project
nitric new api-guide
? Choose a template: [Use arrows to move, type to filter]
> official/TypeScript - Starter
official/JavaScript - Starter
# navigate to the new project directory
cd api-guide
# install dependencies
npm install
一度プロジェクトを実行すると、ローカルで実行できます.nitric run
例のアプリにはhello world
スタイル例の関数.一度実行したらHTTPリクエストでテストできます.curl http://localhost:9001/apis/main/hello/John
# expected response: Hello John
# press Ctrl+C to stop the app
Since we won't use the example function you can delete the
functions/hello.ts
file.
イメージ編集のために後でGIMPを使うので、今もインストールしましょう.
npm install jimp -save
データベースとスキーマの設定
次に、PlanetScaleデータベースが必要になります.アカウントが既にある場合は、次の手順に進みます.そうでなければ、あなたはsign up 無料のアカウント.
データベースの作成
PlanetScale CLIまたはWebダッシュボードを使用して新しいデータベースを作成できます.
以下はCLIを使用した例です.
pscale database create planetnitric --region us-east
You can pick a different region for you database if you prefer, see: available regions
プリズマセットアップ
ここでセットアップPrismaに準備ができました.NPMでプロジェクトに追加してみましょう.
npm install prisma --save-dev
npm install @prisma/client@dev --save
Note: @dev version of the prisma client is needed temporarily due to this issue, which has been resolved but not fully released. We'll update the guide to remove this step once that fix is fully released.
そして、最初のスキーマファイルを生成し、最初のスキーマファイルを生成できます.
npx prisma init
これは、フォルダ内の新しいprismaスキーマを与えますprisma
そして新しい.env
Configを含むファイルは、我々のPlanetScaleデータベースに接続するために使用します.スキーマのビルド
Prismaの内容を上書きします.スキーマ以下のスキーマ.これを使用してデータベースを初期化します.
// prisma/schema.prisma
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = "prisma-client-js"
previewFeatures = ["referentialIntegrity"]
binaryTargets = ["linux-musl"]
output = "./client"
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
referentialIntegrity = "prisma"
}
model MemeTemplate {
name String @id @unique
createdAt DateTime @default(now())
textPositions TextPosition[]
Meme Meme[]
}
model TextPosition {
id String @id @default(cuid())
name String
memeId String
posX Int
posY Int
width Int
height Int
template MemeTemplate @relation(fields: [memeId], references: [name], onDelete: Cascade)
@@index([memeId])
}
model Meme {
id String @id @default(cuid())
createdAt DateTime @default(now())
templateId String
template MemeTemplate @relation(fields: [templateId], references: [name], onDelete: Cascade)
@@index([templateId])
}
次に、スキーマからPrismaクライアントを生成します.npx prisma generate
最後に、ファイルを作成することによって、Prismaクライアントのインスタンスをインポートしやすくしましょうprisma/index.ts
このコードを追加するimport { PrismaClient } from './client';
export * from './client';
let prisma: PrismaClient;
if (process.env.NITRIC_ENVIRONMENT !== 'build') {
if (process.env.NODE_ENV === 'production') {
prisma = new PrismaClient({
errorFormat: 'minimal',
});
} else {
globalThis['prisma'] =
globalThis['prisma'] ||
new PrismaClient({
errorFormat: 'pretty',
});
prisma = globalThis['prisma'];
}
}
export default prisma;
プラネタリウムへの接続
現在、我々のスキーマは準備ができています.最も簡単な方法は
connect
ボタンをクリックして選択しますPrisma
ドロップダウンから.これは、あなたがコピーすることができます値を与える.env
プロジェクトのために.終わったら
.env
ファイルは次のようになります.DATABASE_URL='<Your URL from the above screenshot>'
スキーマが使用可能で、接続の詳細が設定されている場合は、PreSchemaスキーマをPlanetScaleにプッシュできます.npx prisma db push
アプリケーションにクラウドリソースを追加
アプリケーションは、コード内のリソースを定義してビルドすると、任意のルートでこれを書くことができます
.js
or .ts
ファイル.物事を整理するには、一緒にリソースをグループ化をお勧めします.では、新しいAPIをサポートするリソースを定義することから始めましょうresources
ディレクトリ.まず、APIゲートウェイを宣言しましょう.新しいファイルを作成する
apis.ts
新しいフォルダにresources
とこのコード:// resources/apis.ts
import { api } from '@nitric/sdk';
export const memeApi = api('meme');
これは新しいapi
「meme」という名前のリソースで、プロジェクト内の他の場所で参照できるリソースとしてエクスポートします.次に、私たちのmemeイメージファイルを格納するためにいくつかのバケツを作成しましょう.新しいファイルを作成する
buckets.ts
下resources
そして以下のように設定します:// resources/buckets.ts
import { bucket } from '@nitric/sdk';
export const templates = bucket('templates');
export const memes = bucket('memes');
再び、我々は、このケースでは、新しいリソースを、バケットを宣言していると、アプリケーション内で一意の名前を与えている.彼らは繰り返し宣言されることなく再び参照することができますので、我々はそれらのリソースをエクスポートします.テンプレートの作成
リソースが宣言されたので、最初のサービスを作成しましょう.このサービスでは、APIコンシューマーは、ミームとベースの設定可能なセットのベースイメージを提供することによって新しいミームテンプレートを登録することができます.
に
/functions
ディレクトリと呼ばれる新しいファイルを作成するtemplates.ts
次のコードを入力します.// functions/templates.ts
import Jimp from 'jimp';
import prisma, { MemeTemplate, TextPosition } from '../prisma';
import { memeApi } from '../resources/apis';
import { templates } from '../resources/buckets';
export interface CreateTemplateRequest
extends Omit<MemeTemplate, 'filepath' | 'createdAt'> {
source: string;
textPositions: Omit<TextPosition, 'id' | 'memeId'>[];
}
const templateImgs = templates.for('writing');
export const normalizeName = (name: string) => {
return name.replace(' ', '-').replace(/[^\w-]*/g, '');
};
// POST: /templates - Create new meme templates
memeApi.post('/templates', async ({ req, res }) => {
const {
textPositions,
source,
name: rawName,
} = req.json() as CreateTemplateRequest;
const name = normalizeName(rawName);
const img = await Jimp.read(source);
try {
const template = await prisma.memeTemplate.create({
data: {
name,
textPositions: {
create: textPositions,
},
},
});
// Limit width to 512px max to save space
const resizeFactor = 512 / img.getWidth();
img.resize(img.getWidth() * resizeFactor, img.getHeight() * resizeFactor);
// store the image in the bucket
const buf = await img.getBufferAsync(img.getMIME());
await templateImgs.file(name).write(buf);
res.json(template);
} catch (e) {
res.status = 409;
res.body = `Name already taken: ${name}: ${e.message}`;
}
});
// GET: /templates - List all meme templates
memeApi.get('/templates', async ({ res }) => {
const memeTemplates = await prisma.memeTemplate.findMany({
include: {
textPositions: true,
},
});
res.json(memeTemplates);
});
この例では、APIゲートウェイをインポートしていますmemeApi
我々は、我々の中でつくりましたresources
ディレクトリ、および方法を使用してルートとメソッドハンドラを登録するget
and post
, あなたのようなフレームワークのようなExpress .また、テンプレート画像を保存するために使用されるバケットをインポートしている
templateImages
リソースディレクトリから.また、バケットの我々の意図した使用を宣言するfor
あなたのコードがどのようなアクセス許可を必要とし、展開中にそれらを適用できるかをNoneにします.この例では、我々は我々のテンプレートサービスを与えているだけですwrite
テンプレートバケツへのアクセス.着信
context
オブジェクト(req
and res
) Path Params , Query Params , Headers , body , statusなどのリクエストと応答の詳細を含んでいます.ミームサービス
この
templates
例:別の新しいファイルを作成しますfunctions/memes.ts
, 次のコードを使用します.// functions/memes.ts
import { FileMode } from '@nitric/sdk';
import Jimp from 'jimp';
import prisma, { Meme } from '../prisma';
import { memes, templates } from '../resources/buckets';
import { memeApi } from '../resources/apis';
interface MemeCreationRequest extends Omit<Meme, 'id' | 'templateId'> {
templateName: string;
texts: {
name: string;
value: string;
}[];
}
const templateImgs = templates.for('reading');
const memesImgs = memes.for('reading', 'writing');
// POST: /memes - Create new meme images
memeApi.post('/memes', async ({ req, res }) => {
const meme = req.json() as MemeCreationRequest;
const template = await prisma.memeTemplate.findFirst({
include: {
textPositions: true,
},
where: {
name: {
equals: meme.templateName,
},
},
});
const imgBytes = await templateImgs.file(template.name).read();
// Load the image and font
const [img, font] = await Promise.all([
Jimp.read(Buffer.from(imgBytes)),
Jimp.loadFont(Jimp.FONT_SANS_32_WHITE),
]);
// Apply text to the template image to create the meme
meme.texts.forEach((text) => {
// get the text template
const matchingText = template.textPositions.find(
(tp) => tp.name === text.name
);
if (!matchingText) return;
// ignore if anchor tags don't match
img.print(
font,
img.getWidth() * (matchingText.posX / 100),
img.getHeight() * (matchingText.posY / 100),
{
text: text.value,
alignmentX: Jimp.HORIZONTAL_ALIGN_CENTER,
alignmentY: Jimp.VERTICAL_ALIGN_MIDDLE,
},
img.getWidth() * (matchingText.width / 100),
img.getHeight() * (matchingText.height / 100)
);
});
const [newMeme, buf] = await Promise.all([
prisma.meme.create({
data: {
templateId: meme.templateName,
},
}),
img.getBufferAsync(Jimp.MIME_PNG),
]);
await memesImgs.file(newMeme.id).write(buf);
res.json(newMeme);
});
// GET: /memes - List all created memes
memeApi.get('/memes', async ({ res }) => {
const memes = await prisma.meme.findMany();
return res.json(memes);
});
// GET: /memes/:id - Get a meme image by it's ID
memeApi.get('/memes/:id', async ({ req, res }) => {
const { id } = req.params;
const signedUrl = await memesImgs.file(id).signUrl(FileMode.Read);
res.status = 303;
res.headers['Location'] = [signedUrl];
});
繰り返しますが、このファイルはmemeApi
リソースではなく、/memes
パス.また、リクエスト
read
アクセスtemplateImages
バケットread-write
アクセスmemeImages
バケット.ローカルテスト
APIを確立したので、ローカルでテストしましょう.
nitric run
いくつかのスピナーが表示されますが、すべての設定を取得します.SUCCESS Configuration gathered (6s)
SUCCESS Created Dev Image! (0s)
SUCCESS Started Local Services! (4s)
SUCCESS Started Functions! (1s)
Api | Endpoint
meme | http://localhost:9001/apis/meme
ローカルでAPIを走らせるとき、Nailは彼らの名前で彼らを下位ルートにします.したがって、この例では、新しいミームテンプレートを作成するには、あなたのPOST
リクエストhttps://localhost:9001/apis/meme/templates
.以下はAPIをテストするためのリクエスト例です.
テンプレートを作成する
curl -X POST http://localhost:9001/apis/meme/templates \
-H 'Content-Type: application/json' \
-d '{"name":"my-meme","source":"https://www.meme-arsenal.com/memes/89f28a7e83e28f15b1d8e560c788b4fc.jpg","textPositions":[{"name":"topText","posX":50,"posY":0,"width":50,"height":50},{"name":"bottomText","posX":50,"posY":50,"width":50,"height":50}]}'
フルリクエスト{
"name": "my-meme",
"source": "https://www.meme-arsenal.com/memes/89f28a7e83e28f15b1d8e560c788b4fc.jpg",
"textPositions": [
{
"name": "topText",
"posX": 50,
"posY": 0,
"width": 50,
"height": 50
},
{
"name": "bottomText",
"posX": 50,
"posY": 50,
"width": 50,
"height": 50
}
]
}
For
source
provide a URL hosting a meme template image in a common format like .png or .jpg
テンプレートを使用して新しいmemeを作成します
curl -X POST http://localhost:9001/apis/meme/memes \
-H 'Content-Type: application/json' \
-d '{"templateName":"my-meme","texts":[{"name":"topText","value":"top text content"},{"name":"bottomText","value":"bottom text content"}]}'
フルリクエスト{
"templateName": "my-meme",
"texts": [
{
"name": "topText",
"value": "top text content"
},
{
"name": "bottomText",
"value": "bottom text content"
}
]
}
画像を取得する
前の要求から返されたmeme idを使用してブラウザを開き、
http://localhost:9001/apis/meme/memes/<Meme ID>
.クラウドに配備する
準備ができたら、このプロジェクトをAWS、AzureまたはGoogleクラウドに配備できます.この例では、AWSの手順を示しますが、すべてのケースで本質的に同じです.
を定義する
stack
. スタックは本質的に展開ターゲットと呼ばれ、クラウドで実行されているアプリケーションのインスタンスを表します.実行して新しいスタックを作成できます
nitric stack new
そして、プロンプトの後に.この場合、スタックを呼び出しますawsdev
, 絹篩で篩うたようaws
ターゲットクラウドとしてus-east-1
ターゲット領域として:nitric stack new
? What do you want to call your new stack? awsdev
? Which Cloud do you wish to deploy to? aws
? select the region us-east-1
最後にup
スタックを展開し、コードをクラウドにプッシュするコマンドnitric up -s awsdev
から返されるURLを使用できますup
あなたの新しく配備されたAPIに要求をするコマンド.次に、完了したら、スタックを破壊することができますdown
コマンドnitric down -s awsdev
そして、thats!あなたがこのガイドに質問やフィードバックを持っているなら、私はあなたから聞いてみたい.コメント、またはGitHub
Reference
この問題について(PlanetScaleとPrisma ORMによるサーバーレスAPIの構築と配備), 我々は、より多くの情報をここで見つけました https://dev.to/homelessdinosaur/deploy-a-serverless-api-with-planetscale-and-prisma-orm-l17テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol