Apollo with Prismaで簡単なJWT認証を作るとこまで写経する
- JavaScript
- Next.js
- nodejs
- React
- GraphQL
リアルワールドで使っていくなら結局データの出し入れができないとなので、次はそこをやってみようと思う。
この辺をメインに参考にしていきます。
- How to Set Up Basic Authentication with Apollo Server and Prisma - Prisma Tutorials
- pantharshit00/apollo-server-prisma-starter
写経みたいなものなので完全に備忘録。
言葉を整理する
登場人物が一気に増えるので整理しよう。
GraphQL?
クエリランゲージ。欲しいデータ、ジョインしてほしいデータとかを問い合わせ時に全部指定できる。
RESTと対比されがちだけどこっちのほうがめっちゃ柔軟でいい感じ。まだまだ若いのでこれから成長していくんだと思う。
prisma?
バックエンドにMySQLやPostgreSQLなどを選べるGraphQL実装、スキーマ定義をすれば必要なQueryやMutationをざっと吐き出してくれるツールみたいな感じ。
apollo?
GraqhQLのサーバにつないだりリクエストを投げたりできる。
Reactプラグインもあったりしてかなり汎用的に使えるすごいやつ。
世のフロントエンドエンジニアにApollo Clientを布教したい - Qiita
GraphQLサーバを立ち上げる
prisma
が入ってるとdocker-composeでばしっと立ち上がる環境と一緒にplaygroundがついてくるので、
docker-compose up -d
でMySQLと一緒にデーモン起動して、
prisma deploy
でスキーマ反映して、(Railsでいうマイグレーションみたいなもの)
package.json
に定義されてる dev
スクリプトを流せば、
npm run dev
ブラウザでGraphQLが実行できるようになる。
ここまでのまとめ
ちょっとかなりややこしいんだけど、
src/index.js
がprismaの定義を見つつapolloのサーバを起動してつないでくれる- prismaは全Query、Mutationを吐き出してくれちゃうので、
src/resolver.js
に実際に使うクエリ定義を載せておく - なのでパスワードのハッシュ化などは
src/resolver.js
などで噛ませることができる - つまり読んでほしくないデータはここで塞ぐ
という感じらしい。つまりこいつはBFF相当の立ち位置になって、フロントエンドは完全に分離する設計になるのかなあ。
JSON WEB TOKENを利用したログイン
src/resolver.js
でmutationを追加する。
login: async (parent, { username, password }, ctx, info) => {
const user = await ctx.prisma.user({ username })
if (!user) {
throw new Error('Invalid Login')
}
const passwordMatch = await bcrypt.compare(password, user.password)
if (!passwordMatch) {
throw new Error('Invalid Login')
}
const token = jwt.sign(
{
id: user.id,
username: user.email,
},
'my-secret-from-env-file-in-prod',
{
expiresIn: '30d', // token will expire in 30days
},
)
return {
token,
user,
}
}
ベアラートークンを使った認証とコンテキストへの保持
次はQueryにcurrentUserを追加。
const resolvers = {
Query: {
currentUser: (parent, args, { user, prisma }) => {
// this if statement is our authentication check
if (!user) {
throw new Error('Not Authenticated')
}
return prisma.user({ id: user.id })
},
},
}
// Mutation: ....
(parent, args, { user, prisma })
になっていて、userとprismaを渡せるように変わっている。
合わせて srd/index.js
も対応。
// Add this import to top of your file
const jwt = require('jsonwebtoken')
const getUser = token => {
try {
if (token) {
return jwt.verify(token, 'my-secret-from-env-file-in-prod')
}
return null
} catch (err) {
return null
}
}
const server = new ApolloServer({
typeDefs,
resolvers,
context: ({ req }) => {
const tokenWithBearer = req.headers.authorization || ''
const token = tokenWithBearer.split(' ')[1]
const user = getUser(token)
return {
user,
prisma, // the generated prisma client if you are using it
}
},
})
ログイン時に帰ってくるトークンをこんな感じで、プレイグラウンド下部にあるヘッダに設定したらちゃんと取れる。
というところまで来たので、次はインタフェースを作ろう!