バージョン情報
"devDependencies": {
"@types/express": "^4.17.8",
"@types/node": "^14.14.5",
"nodemon": "^2.0.6",
"ts-node": "^9.0.0",
"typescript": "^4.0.5"
}
"dependencies": {
"@mikro-orm/cli": "^4.2.3",
"@mikro-orm/core": "^4.2.3",
"@mikro-orm/migrations": "^4.2.3",
"@mikro-orm/postgresql": "^4.2.3",
"apollo-server-express": "^2.18.2",
"argon2": "^0.27.0",
"class-validator": "^0.12.2",
"express": "^4.17.1",
"graphql": "^15.4.0",
"pg": "^8.4.2",
"reflect-metadata": "^0.1.13",
"type-graphql": "^1.1.0"
}
はじめに
TypeScriptとGraphQLで簡単なUser登録APIを作成し、基本を学びます。
TypeScriptとMikroORMのインストール
npm init -y
yarn add -D @types/node typescript ts-node nodemon
yarn add @mikro-orm/cli @mikro-orm/core @mikro-orm/migrations @mikro-orm/postgresql pg
npx tsconfig.json
GraphQLとExpressのインストール
yarn add class-validator reflect-metadata express apollo-server-express graphql type-graphql
yarn add -D @types/express
postgreSQL DB作成
createdb {DB名}
エンティティ作成
touch User.ts
entities/User.ts
import { Entity, PrimaryKey, Property } from '@mikro-orm/core';
import { Field, ObjectType } from 'type-graphql';
@ObjectType()
@Entity()
export class User {
@Field()
@PrimaryKey()
id!: number;
@Field(() => String)
@Property({ type: 'date' })
createdAt = new Date();
@Field(() => String)
@Property({ type: 'date', onUpdate: () => new Date() })
updatedAt = new Date();
@Field(() => String)
@Property({ type: 'text', unique: true })
username!: string;
@Property({ type: 'text' })
password!: string;
}
デコレータ
- @ObjectType(): Classの型情報をGraphQLでも扱えるようにする
- @Entity(): エンティティ定義
- @Field(): GraphQLでクエリできるようにする(例えばパスワードとかには付けない)
- @Property({ type: ‘text’ }): プロパティ定義
MikroORMの設定
touch mikro-orm.config.ts
mikro-orm.config.ts
import { __prod__ } from './constants';
import { MikroORM } from '@mikro-orm/core';
import * as path from 'path';
import { Post } from './entities/Post';
import { User } from './entities/User';
export default {
migrations: {
path: path.join(__dirname, './migrations'), // マイグレーションファイルの保存先
pattern: /^[\w-]+\d+\.[tj]s$/, // JS/TSファイルの両方に対応させる
},
entities: [Post, User], 使用するエンティティをここに
dbName: {DB名},
user: '',
password: '',
type: 'postgresql',
debug: !__prod__,
} as Parameters<typeof MikroORM.init>[0];
マイグレーションファイル作成
mikro-orm.config.ts
に追加したエンティティを元にマイグレーションファイル作成
npx mikro-orm migration:create
サーバのセットアップ
index.ts
import 'reflect-metadata';
import { MikroORM } from '@mikro-orm/core';
import { __prod__ } from './constants';
import mikroConfig from './mikro-orm.config';
import express from 'express';
import { ApolloServer } from 'apollo-server-express';
import { buildSchema } from 'type-graphql';
const main = async () => {
const orm = await MikroORM.init(mikroConfig);
/* mikroConfigを元にマイグレーションする -> DB作成 */
await orm.getMigrator().up();
/* Expressサーバーの作成 */
const app = express();
/* ApolloServerの作成 */
const apolloServer = new ApolloServer({
schema: await buildSchema({
resolvers: [],
validate: false,
}),
/* GraphQL(Apollo)にMikroORMコンテクストを渡す */
context: () => ({ em: orm.em }),
});
/* ApolloServerのミドルウェアとしてExpressを登録 */
apolloServer.applyMiddleware({ app });
/* サーバーの起動 */
app.listen(4000, () => {
console.log('--- サーバー起動: localhost:4000 --- ');
});
};
main().catch((err) => {
console.log(err);
});
リゾルバの作成
touch user.ts
# パスワードのハッシュ用モジュール
yarn add argon2
mutation/register
クエリでUserを登録させる場合
resolver/user.ts
import { Arg, Ctx, Field, InputType, Mutation, Resolver } from 'type-graphql';
import argon2 from 'argon2';
import { User } from 'src/entities/User';
import { MyContext } from 'src/types';
@InputType()
class UsernamePasswordInput {
@Field()
username: string;
password: string;
}
@Resolver()
export class UserResolvers {
@Mutation(() => User)
async register(
@Arg('options') options: UsernamePasswordInput,
@Ctx() { em }: MyContext,
) {
const hashedPassword = await argon2.hash(options.password);
const user = em.create(User, {
username: options.username,
password: hashedPassword,
});
await em.persistAndFlush(user);
return user;
}
}
デコレータ
- @InputType(): クエリの引数を定義
- @Resolver(): リゾルバ定義
- @Mutation(() => User): mutationクエリ時にUser情報が返るよう定義
- @Arg(‘options’) options: UsernamePasswordInput: options
という引数を設定/TypeScriptに型情報を渡す
- @Ctx() { em }: MyContext: MikroORMのコンテクスト(DB操作に必要)(orm.em
を分割代入)
MikroORMの型情報を設定 -> @Ctx()に使用
types.ts
import { EntityManager, IDatabaseDriver, Connection } from '@mikro-orm/core';
export type MyContext = {
em: EntityManager<any> & EntityManager<IDatabaseDriver<Connection>>;
};
リゾルバをApolloサーバのスキーマに登録
index.ts
...
/* ApolloServerの作成 */
const apolloServer = new ApolloServer({
schema: await buildSchema({
resolvers: [UserResolvers],
validate: false,
}),
...
PlayGroundで確認
http://localhost:4000/graphql
まとめ
- TypeScriptの型情報がうまくGraphQLと連携できる
- MikroORMも型が効いて使いやすい
- デコレータ見慣れない場合は慣れるしかない