タイプスクリプトでのシーケンスの使用
60002 ワード
執筆Ibiyemi Adewakun ✏️
あなたのAPIで生のSQLを書くことは、それをパスします、あるいは、最高に、それは本当に複雑な質問のために予約されます.これらは開発のためのより簡単な時間であり、ほとんどのAPIのために、多くのオブジェクトリレーショナル・マッパ(ORM)の1つを使用することは十分です.
また、便利なデータベースとそのクエリ言語との通信の複雑な詳細をカプセル化します.
これは、MySQL、PostgreSQL、またはMongoDBのような複数のデータベースタイプで、単一のORMを使用することができます、簡単にあなたのコードを書き換えることなく、データベース間で切り替えることができることを意味します!また、それらにアクセスするために同じコードを使用している間、異なる種類のデータベースをプロジェクトに接続できます.
この記事では、Sequelize ORMを使用する方法を学びます.それで、あなたのラップトップをつかんで、あなたのIDEを開けて、始めましょう!
必要条件
この記事に沿って次の手順を実行します.
JavaScript package manager; we’ll use yarn
あなたの好みのIDEまたはテキストエディタSublime Text or Visual Studio Code
プロジェクトの設定
プロジェクトを始めましょうa simple Express.js API レシピや食材を格納する仮想料理の本を作成し、人気のあるカテゴリで私たちのレシピをタグ付けします.
まず、以下のように入力してプロジェクトディレクトリを作りましょう.
typescriptをプロジェクトに追加しました.
最後に、プロジェクトのディレクトリとファイルを作成し、以下のアウトラインに合わせて簡単なAPI構造を定義しましょう.
シーケンスのORMの設定
この時点で、急行.JSのアプリケーションを実行しているので、楽しいものをもたらすには時間です:シーケンシャルORM!
プロジェクトにシーケンスを追加するには、次の手順を実行します.
シーケンスの接続の開始
Sequelizeのインストール後、データベースへの接続を開始する必要があります.一度起動すると、この接続によりモデルが登録されます.
シーケンサモデルの作成と登録
Sequulizeは、モデルを登録する2つの方法を提供します.using
次のインターフェイスを作成します.
モデルを完了し、接続したデータベースにターゲットテーブルを作成するには、モデルを実行します
💡 プロのヒント:予備使用
DARとサービスにおけるモデルの使用
データアクセス層(DAL)は、SQLクエリを実装する場合、またはこの例では、シーケンサモデルクエリが実行される場所です.
上記の我々のダルでは、我々は我々の
今、我々は我々のサービスを作成することができます.
ルートとコントローラによるモデルの電力供給
我々は長い道のりを来た!我々のデータベースから我々のデータをフェッチしているサービスがある今、ルートとコントローラを使用しているすべてのその魔法を市民に持ってくる時間です.
我々の創造によって始めましょう
ルートとコントローラ間のパラメータと結果を入力するために、データ転送オブジェクト(DTOS)とマップを追加して結果を変換します.
結論
この記事では、簡単なタイプのアプリケーションをExpressで設定します.JSはシーケンシャルORMを使用して、我々のモデルを作成して、ORMを通して質問を走らせて、順序付けを初期化することによって歩きました.
我々のプロジェクトのtypescriptでシーケンス化を使用すると、モデルの入力と出力の厳密な型を定義しながら、コードエンジンを抽象化し、データベースエンジンを抽象化できません.これにより、データベースの種類を変更しても、SQLの注入を防ぐことができます.
全体code from this article is available on Github . 私はあなたがこの記事を簡単に従うことを発見し、私はあなたのアプリケーションまたはあなたがコメントのセクションにあるすべての質問にシーケンス化を使用するクールな方法を持っているすべてのアイデアを聞くのが大好きだと思います!
あなたのAPIで生のSQLを書くことは、それをパスします、あるいは、最高に、それは本当に複雑な質問のために予約されます.これらは開発のためのより簡単な時間であり、ほとんどのAPIのために、多くのオブジェクトリレーショナル・マッパ(ORM)の1つを使用することは十分です.
また、便利なデータベースとそのクエリ言語との通信の複雑な詳細をカプセル化します.
これは、MySQL、PostgreSQL、またはMongoDBのような複数のデータベースタイプで、単一のORMを使用することができます、簡単にあなたのコードを書き換えることなく、データベース間で切り替えることができることを意味します!また、それらにアクセスするために同じコードを使用している間、異なる種類のデータベースをプロジェクトに接続できます.
この記事では、Sequelize ORMを使用する方法を学びます.それで、あなたのラップトップをつかんで、あなたのIDEを開けて、始めましょう!
必要条件
この記事に沿って次の手順を実行します.
Node.js
プロジェクトの設定
プロジェクトを始めましょうa simple Express.js API レシピや食材を格納する仮想料理の本を作成し、人気のあるカテゴリで私たちのレシピをタグ付けします.
まず、以下のように入力してプロジェクトディレクトリを作りましょう.
$ mkdir cookbook
$ cd cookbook
インサイドニューcookbook
プロジェクトディレクトリ、必要なプロジェクト依存関係をインストールするyarn
. ファーストランnpm init
ノードを初期化する.JSプロジェクトpackage.json
ファイル$ npm init
ノードの後.JSプロジェクトでは、express
:$ yarn add express
次に、TypeScript 次のプロジェクトを実行します.$ yarn add -D typescript ts-node @types/express @types/node
💡 我々はフラグを追加したことに注意してください.-D
, インストールコマンドに.このフラグは、これらのライブラリをdev依存関係として追加するために、これらのライブラリを追加するように糸を指示します.また、Express用の型定義を追加しました.JSとノード.jstypescriptをプロジェクトに追加しました.
$ npx tsc --init
これは、私たちのtypescript設定ファイルを作成しますts.config
, デフォルト値を設定します.// ts.config
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"sourceMap": true,
"outDir": "dist",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
}
}
詳細情報customizing ts.config
here .最後に、プロジェクトのディレクトリとファイルを作成し、以下のアウトラインに合わせて簡単なAPI構造を定義しましょう.
- dist # the name of our outDir set in tsconfig.json
- src
- api
- controllers
- contracts
- routes
- services
- db
- dal
- dto
- models
config.ts
init.ts
- errors
index.ts
ts.config
プロジェクトの構造を定義しましたindex.ts
ファイルは、アプリケーションの出発点です.次のコードを追加してExpressを作成します.JSサーバ# src/index.ts
import express, { Application, Request, Response } from 'express'
const app: Application = express()
const port = 3000
// Body parsing Middleware
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.get('/', async(req: Request, res: Response): Promise<Response> => {
return res.status(200).send({ message: `Welcome to the cookbook API! \n Endpoints available at http://localhost:${port}/api/v1` })
})
try {
app.listen(port, () => {
console.log(`Server running on http://localhost:${port}`)
})
} catch (error) {
console.log(`Error occurred: ${error.message}`)
}
また、アプリケーションを簡単に実行し、環境変数を渡すための追加ライブラリを追加する必要があります.これらの追加ライブラリはnodemon using yarn add -D nodemon
, eslint using yarn add -D eslint
, and dotenv using yarn add dotenv
.シーケンスのORMの設定
この時点で、急行.JSのアプリケーションを実行しているので、楽しいものをもたらすには時間です:シーケンシャルORM!
プロジェクトにシーケンスを追加するには、次の手順を実行します.
$ yarn add sequelize
$ yarn add mysql2
MySQL用のデータベースドライバを追加しましたが、これは個人的な好みだけに基づいていますが、代わりに任意のドライバをインストールすることができます.ビューhere for other available database drivers .シーケンスの接続の開始
Sequelizeのインストール後、データベースへの接続を開始する必要があります.一度起動すると、この接続によりモデルが登録されます.
# db/config.ts
import { Dialect, Sequelize } from 'sequelize'
const dbName = process.env.DB_NAME as string
const dbUser = process.env.DB_USER as string
const dbHost = process.env.DB_HOST
const dbDriver = process.env.DB_DRIVER as Dialect
const dbPassword = process.env.DB_PASSWORD
const sequelizeConnection = new Sequelize(dbName, dbUser, dbPassword, {
host: dbHost,
dialect: dbDriver
})
export default sequelizeConnection
シーケンサモデルの作成と登録
Sequulizeは、モデルを登録する2つの方法を提供します.using
sequelize.define
またはSequelizeモデルクラスを拡張します.このチュートリアルでは、モデル拡張メソッドを使用してIngredient
モデル.次のインターフェイスを作成します.
IngredientAttributes
モデルのすべての可能な属性を定義するIngredientInput
Sequelize ' sに渡されるオブジェクトの型を定義しますmodel.create
IngredientOuput
返されるオブジェクトをmodel.create
, model.update
, and model.findOne
# db/models/Ingredient.ts
import { DataTypes, Model, Optional } from 'sequelize'
import sequelizeConnection from '../config'
interface IngredientAttributes {
id: number;
name: string;
slug: string;
description?: string;
foodGroup?: string;
createdAt?: Date;
updatedAt?: Date;
deletedAt?: Date;
}
export interface IngredientInput extends Optional<IngredientAttributes, 'id' | 'slug'> {}
export interface IngredientOuput extends Required<IngredientAttributes> {}
次に、Ingredient
拡張、初期化、およびエクスポートimport {Model} from 'sequelize'
モデルクラスをシーケンス化する# db/models/Ingredient.ts
...
class Ingredient extends Model<IngredientAttributes, IngredientInput> implements IngredientAttributes {
public id!: number
public name!: string
public slug!: string
public description!: string
public foodGroup!: string
// timestamps!
public readonly createdAt!: Date;
public readonly updatedAt!: Date;
public readonly deletedAt!: Date;
}
Ingredient.init({
id: {
type: DataTypes.INTEGER.UNSIGNED,
autoIncrement: true,
primaryKey: true,
},
name: {
type: DataTypes.STRING,
allowNull: false
},
slug: {
type: DataTypes.STRING,
allowNull: false,
unique: true
},
description: {
type: DataTypes.TEXT
},
foodGroup: {
type: DataTypes.STRING
}
}, {
timestamps: true,
sequelize: sequelizeConnection,
paranoid: true
})
export default Ingredient
💡 我々はオプションを追加注意してくださいparanoid: true
我々のモデルにこれは、モデルにソフト削除を加えるdeletedAt
マークが記録する属性deleted
を呼び出すときdestroy
メソッド.モデルを完了し、接続したデータベースにターゲットテーブルを作成するには、モデルを実行します
sync
メソッド:# db/init.ts
import { Recipe, RecipeTags, Tag, Review, Ingredient, RecipeIngredients } from './models'
const isDev = process.env.NODE_ENV === 'development'
const dbInit = () => {
Ingredient.sync({ alter: isDev })
}
export default dbInit
💡 The sync
メソッドはforce
and alter
オプション.The force
オプションはテーブルのレクリエーションを強制する.The alter
オプションが存在しない場合はテーブルを作成します.💡 プロのヒント:予備使用
force
or alter
開発環境では、誤ってデータベースを再作成しないでください.すべてのデータを失う、またはアプリケーションを中断する可能性のあるデータベースへの変更を適用します.DARとサービスにおけるモデルの使用
データアクセス層(DAL)は、SQLクエリを実装する場合、またはこの例では、シーケンサモデルクエリが実行される場所です.
# db/dal/ingredient.ts
import {Op} from 'sequelize'
import {Ingredient} from '../models'
import {GetAllIngredientsFilters} from './types'
import {IngredientInput, IngredientOuput} from '../models/Ingredient'
export const create = async (payload: IngredientInput): Promise<IngredientOuput> => {
const ingredient = await Ingredient.create(payload)
return ingredient
}
export const update = async (id: number, payload: Partial<IngredientInput>): Promise<IngredientOuput> => {
const ingredient = await Ingredient.findByPk(id)
if (!ingredient) {
// @todo throw custom error
throw new Error('not found')
}
const updatedIngredient = await (ingredient as Ingredient).update(payload)
return updatedIngredient
}
export const getById = async (id: number): Promise<IngredientOuput> => {
const ingredient = await Ingredient.findByPk(id)
if (!ingredient) {
// @todo throw custom error
throw new Error('not found')
}
return ingredient
}
export const deleteById = async (id: number): Promise<boolean> => {
const deletedIngredientCount = await Ingredient.destroy({
where: {id}
})
return !!deletedIngredientCount
}
export const getAll = async (filters?: GetAllIngredientsFilters): Promise<IngredientOuput[]> => {
return Ingredient.findAll({
where: {
...(filters?.isDeleted && {deletedAt: {[Op.not]: null}})
},
...((filters?.isDeleted || filters?.includeDeleted) && {paranoid: true})
})
}
追加paranoid: true
オプションfindAll
モデルメソッドは、deletedAt
結果を設定する.それ以外の場合、結果はデフォルトでソフト削除レコードを除外します.上記の我々のダルでは、我々は我々の
ModelInput
型定義と任意の追加の型を配置するdb/dal/types.ts
:# db/dal/types.ts
export interface GetAllIngredientsFilters {
isDeleted?: boolean
includeDeleted?: boolean
}
💡 Sequelize ORMは、いくつかの本当にクールなモデルメソッドを含むfindAndCountAll
, レコードのリストと、フィルタ条件にマッチするすべてのレコードのカウントを返します.これは、APIでページ化されたリスト応答を返すのに本当に役に立ちます.今、我々は我々のサービスを作成することができます.
# api/services/ingredientService.ts
import * as ingredientDal from '../dal/ingredient'
import {GetAllIngredientsFilters} from '../dal/types'
import {IngredientInput, IngredientOuput} from '../models/Ingredient'
export const create = (payload: IngredientInput): Promise<IngredientOuput> => {
return ingredientDal.create(payload)
}
export const update = (id: number, payload: Partial<IngredientInput>): Promise<IngredientOuput> => {
return ingredientDal.update(id, payload)
}
export const getById = (id: number): Promise<IngredientOuput> => {
return ingredientDal.getById(id)
}
export const deleteById = (id: number): Promise<boolean> => {
return ingredientDal.deleteById(id)
}
export const getAll = (filters: GetAllIngredientsFilters): Promise<IngredientOuput[]> => {
return ingredientDal.getAll(filters)
}
ルートとコントローラによるモデルの電力供給
我々は長い道のりを来た!我々のデータベースから我々のデータをフェッチしているサービスがある今、ルートとコントローラを使用しているすべてのその魔法を市民に持ってくる時間です.
我々の創造によって始めましょう
Ingredients
路線src/api/routes/ingredients.ts
:# src/api/routes/ingredients.ts
import { Router } from 'express'
const ingredientsRouter = Router()
ingredientsRouter.get(':/slug', () => {
// get ingredient
})
ingredientsRouter.put('/:id', () => {
// update ingredient
})
ingredientsRouter.delete('/:id', () => {
// delete ingredient
})
ingredientsRouter.post('/', () => {
// create ingredient
})
export default ingredientsRouter
私たちの料理のAPIは、最終的には、いくつかのルートがありますRecipes
and Tags
. だから、我々はindex.ts
ファイルをベースパスに別のルートを登録し、私たちの急行に接続する1つの中央の輸出を持っている.以前からのJSサーバー# src/api/routes/index.ts
import { Router } from 'express'
import ingredientsRouter from './ingredients'
const router = Router()
router.use('/ingredients', ingredientsRouter)
export default router
更新しましょうsrc/index.ts
我々のエクスポートされたルートを輸入して、我々を我々の急行に登録すること.JSサーバ# src/index.ts
import express, { Application, Request, Response } from 'express'
import routes from './api/routes'
const app: Application = express()
...
app.use('/api/v1', routes)
ルートを作成して、接続した後に、我々のルートにリンクして、サービスメソッドを呼ぶコントローラをつくりましょう.ルートとコントローラ間のパラメータと結果を入力するために、データ転送オブジェクト(DTOS)とマップを追加して結果を変換します.
# src/api/controllers/ingredient/index.ts
import * as service from '../../../db/services/IngredientService'
import {CreateIngredientDTO, UpdateIngredientDTO, FilterIngredientsDTO} from '../../dto/ingredient.dto'
import {Ingredient} from '../../interfaces'
import * as mapper from './mapper'
export const create = async(payload: CreateIngredientDTO): Promise<Ingredient> => {
return mapper.toIngredient(await service.create(payload))
}
export const update = async (id: number, payload: UpdateIngredientDTO): Promise<Ingredient> => {
return mapper.toIngredient(await service.update(id, payload))
}
export const getById = async (id: number): Promise<Ingredient> => {
return mapper.toIngredient(await service.getById(id))
}
export const deleteById = async(id: number): Promise<Boolean> => {
const isDeleted = await service.deleteById(id)
return isDeleted
}
export const getAll = async(filters: FilterIngredientsDTO): Promise<Ingredient[]> => {
return (await service.getAll(filters)).map(mapper.toIngredient)
}
では、コントローラへの呼び出しでルータを更新します.# src/api/routes/ingredients.ts
import { Router, Request, Response} from 'express'
import * as ingredientController from '../controllers/ingredient'
import {CreateIngredientDTO, FilterIngredientsDTO, UpdateIngredientDTO} from '../dto/ingredient.dto'
const ingredientsRouter = Router()
ingredientsRouter.get(':/id', async (req: Request, res: Response) => {
const id = Number(req.params.id)
const result = await ingredientController.getById(id)
return res.status(200).send(result)
})
ingredientsRouter.put('/:id', async (req: Request, res: Response) => {
const id = Number(req.params.id)
const payload:UpdateIngredientDTO = req.body
const result = await ingredientController.update(id, payload)
return res.status(201).send(result)
})
ingredientsRouter.delete('/:id', async (req: Request, res: Response) => {
const id = Number(req.params.id)
const result = await ingredientController.deleteById(id)
return res.status(204).send({
success: result
})
})
ingredientsRouter.post('/', async (req: Request, res: Response) => {
const payload:CreateIngredientDTO = req.body
const result = await ingredientController.create(payload)
return res.status(200).send(result)
})
ingredientsRouter.get('/', async (req: Request, res: Response) => {
const filters:FilterIngredientsDTO = req.query
const results = await ingredientController.getAll(filters)
return res.status(200).send(results)
})
export default ingredientsRouter
この時点で、ビルドスクリプトを追加してAPIを実行できます.# package.json
...
"scripts": {
"dev": "nodemon src/index.ts",
"build": "npx tsc"
},
...
最終的な製品を見るには、APIを使いますyarn run dev
そして、我々の成分終点を訪問してくださいhttp://localhost:3000/api/v1/ingredients .結論
この記事では、簡単なタイプのアプリケーションをExpressで設定します.JSはシーケンシャルORMを使用して、我々のモデルを作成して、ORMを通して質問を走らせて、順序付けを初期化することによって歩きました.
我々のプロジェクトのtypescriptでシーケンス化を使用すると、モデルの入力と出力の厳密な型を定義しながら、コードエンジンを抽象化し、データベースエンジンを抽象化できません.これにより、データベースの種類を変更しても、SQLの注入を防ぐことができます.
全体code from this article is available on Github . 私はあなたがこの記事を簡単に従うことを発見し、私はあなたのアプリケーションまたはあなたがコメントのセクションにあるすべての質問にシーケンス化を使用するクールな方法を持っているすべてのアイデアを聞くのが大好きだと思います!
Reference
この問題について(タイプスクリプトでのシーケンスの使用), 我々は、より多くの情報をここで見つけました https://dev.to/logrocket/using-sequelize-with-typescript-2m1eテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol