【TypeScript】PostgreSQLとExpressをつなぐ
こんばんは、葛の葉です。
今回はPostgreSQLとExpressを繋いでみましょう。
諸々のバージョン
postgres (docker) postgres:11.5-alpine node (docker) 8.16.1-alpine pg(npm package) 7.12.1
データベースの情報
今回はPostgresを使用します。Herokuでも使えると思います。
hostname = postgres user = postgres password = example databaseName = demo tableName = posts uri = postgres://postgres:example@postgres:5432/demo
テーブルの中身
demo=# select * from posts; id | title | text | name ----+--------------------+------------------------------------------------------+--------------- 1 | タイトルマン | メロンはとてもおいしいと思うが、唇が張れると思います | 明後日マン 2 | はいはい | ところでドラゴンかわいいよね | スター 3 | いやー探しましたよ | これがおれの本気なのさ | 明後日マン 4 | タイトルです | ここが本文です | あっピーマン 5 | ここがタイトル | ここが本文 | ここが名前
pgをインストール
nodeのモジュールの一つであるpg
を使用します。勉強も兼ねてなのであえてORMは使わないようにしてみました。
npm i pg --save
このような感じのクラスを作る
コンストラクタにPostgreSQLのPoolのオブジェクトを渡すようにする。Poolでなくても良いかも。
// Posts.ts import {Pool} from "pg" export class Posts{ private readonly client:Pool; public tableName: String; constructor(conn:Pool){ this.client = conn; this.tableName = "posts"; } async row(id:string) { let message; let query = `SELECT * FROM ${this.tableName} WHERE id=${id}`; console.log({ query }) await this.client.query(query) .then(function(result){ console.log({result}) message = result.rows }) .catch(function(reason){ console.log({reason}); message = reason.message; }); return message; } }
このようにインスタンスを作成する。
import {Pool} from "pg" import {Posts} from "./Models/Posts" const uri = "postgres://postgres:example@postgres:5432/demo"; const conn = new Pool({connectionString: uri}); const postsClient = new Posts(conn); postsClient.row("1").then((data)=>{ console.log(data); }); /* [ { id: 1, title: 'タイトルマン', text: 'メロンはとてもおいしいと思うが、唇が張れると思います', name: '明後日マン'} ] */
前回のExpressを見る
前回作ったExpressにシングルトンパターンを組み合わせる。
create_posts
関数の引数にURIを渡すようにしている。
// singletons.ts // 新規 import { Pool } from "pg"; import { Posts } from "./Models/Posts" export let postsClient: Posts; export function create_posts(uri:string){ const conn = new Pool({connectionString: uri}); postsClient = new Posts(conn); };
create_posts
関数には直接URIを記載しているけれどもprocess.env
などを使って環境変数から取得してくるのもありだと思う。
// factory.ts import * as express from "express"; import * as bodyParser from "body-parser"; import testRouter from "./controllers/posts" import { create_posts } from "./singletons" // 追加 export function createApp(){ const app = express(); app.use(bodyParser.urlencoded({ extended: true })); app.use(bodyParser.json()); app.use('/test', testRouter); create_posts("postgres://postgres:example@postgres:5432/demo"); // 追加 return app }
コントローラーにシングルトンを渡すようにする。また、コントローラーがクエリを受け取った際にPostgresに問い合わせをしたいので async
とawait
を使う。ここは前回から結構変更があるので注意。
// ./controllers/test.ts // 結構変更点あり import * as express from "express"; import {postsClient} from "../singletons"; const testRouter = express.Router(); testRouter.get('/', async function (req, res) { var ret = await postsClient.row(req.query.id); res.json({ message:ret }); }) export default testRouter;
あとはtypescriptをコンパルして出現したapp.js
を実行する。
$ node app.js Listen to port http://localhost:8888
http://localhost:8888/test/
に対してget
メソッドのURLパラメーターid
を渡してあげます。すなわちhttp://localhost:8888/test/?id=1
と行った感じです。
$ curl http://localhost:8888/test/?id=1 {"message":[{"id":1,"title":"タイトルマン","text":"メロンはとてもおいしいと思うが、唇が張れると思います","name":"明後日マン"}]}```
と表示されているはずです。
セキュリティ的に
たぶんこれSQLインジェクションできるんじゃないかなって思ったりするんですけど、どうでしょう?自分でテストしてみるのもいいと思います。