Open6
nextauth mongodb
mongodb 接続
- mongodb atlas→New Project→Cluster(connect useing MongoDB Compass)→Copy the connection string, then open MongoDB Compass.→パスワードは DatabaseAccessで取得
MongoDB Compass
- connect → disconnect → edit → confirm → url取得
- vscode で .env.local ファイル作成(url貼り付け)
VSCode で編集したものをMongoDB compass でデータを取得
utils/database.ts
import { MongoClient, Db } from 'mongodb'
interface ConnectType {
db: Db
client: MongoClient
}
const client = new MongoClient(process.env.DATABASE_URL, {
useNewUrlParser: true,
useUnifiedTopology: true
})
export default async function connect(): Promise<ConnectType> {
if (!client.isConnected()) await client.connect()
const db = client.db('Database Name')
return { db, client }
}
api/user.tsx
import { NextApiRequest, NextApiResponse } from 'next'
import connect from '../../utils/database'
interface ResponseType {
message: string
}
export default async (
req: NextApiRequest,
res: NextApiResponse<ResponseType>
): Promise<void> => {
if (req.method === 'GET') {
const { db } = await connect()
const response = await db.collection('Collection Name').insertOne({
name: 'tsuboi'
})
res.status(200).json(response.ops[0])
} else {
res.status(400).json({ message: 'Wrong request method' })
}
}
Postman や Insomnia で編集したものを MongoDB compass でデータを取得
pages/api/user
import { NextApiRequest, NextApiResponse } from 'next'
import connect from '../../utils/database'
interface SuccessResponseType {
name: string
}
interface ErrorResponseType {
error: string
}
export default async (
req: NextApiRequest,
res: NextApiResponse<SuccessResponseType | ErrorResponseType>
): Promise<void> => {
if (req.method === 'POST') {
const { name } = req.body
if (!name) {
res.status(400).json({ error: 'not name' })
return
}
const { db } = await connect()
const response = await db.collection('user').insertOne({
name
})
res.status(200).json(response.ops[0])
} else {
res.status(400).json({ error: 'Wrong request method' })
}
}
より詳細に
- 条件分岐
pages/api/user
import { NextApiRequest, NextApiResponse } from 'next'
import connect from '../../utils/database'
interface SuccessResponseType {
name: string
email: string
teacher: boolean
courses: string[]
}
interface ErrorResponseType {
error: string
}
export default async (
req: NextApiRequest,
res: NextApiResponse<SuccessResponseType | ErrorResponseType>
): Promise<void> => {
if (req.method === 'POST') {
const { name, email, teacher, courses } = req.body
if (!teacher) {
if (!name || !email) {
res.status(400).json({ error: 'not current' })
return
}
} else if (teacher) {
if (!name || !email || !courses) {
res.status(400).json({ error: 'not current' })
return
}
}
const { db } = await connect()
const response = await db.collection('user').insertOne({
name,
email,
teacher,
courses: courses || []
})
res.status(200).json(response.ops[0])
} else {
res.status(400).json({ error: 'Wrong request method' })
}
}
2.1.1 MongoDB で登録したデータを Postman や Insomnia で email を指定することで確認
pages/api/user
else if (req.method === 'GET') {
const { email } = req.body
if (!email) {
res
.status(400)
.json({ error: 'email というプロパティを指定してください。' })
return
}
const { db } = await connect()
const response = await db.collection('user').findOne({ email })
if (!response) {
res
.status(400)
.json({ error: 'そのようなアドレスは登録されていません。' })
return
}
res.status(200).json(response)
}
2.1.2 MongoDB で登録したデータを Postman や Insomnia で url に email を指定することで確認
pages/api/user/[email].tsx
import { NextApiRequest, NextApiResponse } from 'next'
import connect from '../../../utils/database'
interface SuccessResponseType {
name: string
email: string
teacher: boolean
courses: string[]
appointments: Record<string, unknown>[]
}
interface ErrorResponseType {
error: string
}
export default async (
req: NextApiRequest,
res: NextApiResponse<SuccessResponseType | ErrorResponseType>
): Promise<void> => {
if (req.method === 'GET') {
const { email } = req.query
if (!email) {
res
.status(400)
.json({ error: 'email というプロパティを指定してください。' })
return
}
const { db } = await connect()
const response = await db.collection('user').findOne({ email })
if (!response) {
res
.status(400)
.json({ error: `${email} のようなアドレスは登録されていません。` })
return
}
res.status(200).json(response)
} else {
res.status(400).json({ error: 'Wrong request method' })
}
}
2.2 MongoDB で登録したデータを Postman や Insomnia で id を指定することで確認
pages/api/teacher.tsx
import { ObjectId } from 'mongodb'
import { NextApiRequest, NextApiResponse } from 'next'
import connect from '../../utils/database'
interface SuccessResponseType {
name: string
email: string
teacher: boolean
courses: string[]
}
interface ErrorResponseType {
error: string
}
export default async (
req: NextApiRequest,
res: NextApiResponse<SuccessResponseType | ErrorResponseType>
): Promise<void> => {
if (req.method === 'GET') {
const { id } = req.body
if (!id) {
res.status(400).json({ error: 'id というプロパティを指定してください。' })
return
}
const { db } = await connect()
const response = await db
.collection('user')
.findOne({ _id: new ObjectId(id) })
if (!response) {
res.status(400).json({ error: 'そのような ID は登録されていません。' })
return
}
res.status(200).json(response)
} else {
res.status(400).json({ error: 'Wrong request method' })
}
}
2.3 MongoDB で登録したデータを Postman や Insomnia で Course を指定することで(配列で)取得
pages/api/search.tsx
import { NextApiRequest, NextApiResponse } from 'next'
import connect from '../../utils/database'
interface SuccessResponseType {
name: string
email: string
teacher: boolean
courses: string[]
}
interface ErrorResponseType {
error: string
}
export default async (
req: NextApiRequest,
res: NextApiResponse<SuccessResponseType[] | ErrorResponseType>
): Promise<void> => {
if (req.method === 'GET') {
const { courses } = req.body
if (!courses) {
res
.status(400)
.json({ error: 'courses というプロパティを指定してください。' })
return
}
const { db } = await connect()
const response = await db.collection('user').find({ courses }).toArray()
if (response.length === 0) {
res
.status(400)
.json({ error: 'そのような Course は登録されていません。' })
return
}
res.status(200).json(response)
} else {
res.status(400).json({ error: 'Wrong request method' })
}
}
2.3 Postman や Insomnia で 編集後 MongoDB で登録した2つのデータをつなげる。
pages/api/appointment.tsx
import { ObjectId } from 'mongodb'
import { NextApiRequest, NextApiResponse } from 'next'
import { getSession } from 'next-auth/client'
import connect from '../../utils/database'
interface SuccessResponseType {
date: string
teacherName: string
teacherId: string
studentName: string
studentId: string
course: string
location: string
appointmentLink: string
}
interface ErrorResponseType {
error: string
}
export default async (
req: NextApiRequest,
res: NextApiResponse<SuccessResponseType | ErrorResponseType>
): Promise<void> => {
if (req.method === 'POST') {
// const session = await getSession({ req })
// if (!session) {
// res.status(400).json({ error: '先にログインしてください ' })
// return
// }
const {
date,
teacherName,
teacherId,
studentName,
studentId,
course,
location,
appointmentLink
} = req.body
if (
!date ||
!teacherName ||
!teacherId ||
!studentName ||
!studentId ||
!course ||
!location
) {
res.status(400).json({ error: 'パラメータが不足しています。 ' })
return
}
const { db } = await connect()
const teacherExists = await db
.collection('user')
.findOne({ _id: new ObjectId(teacherId) })
if (!teacherExists) {
res
.status(400)
.json({ error: `${teacherId} と ${teacherName}は紐付いていません。` })
return
}
const studentExists = await db
.collection('user')
.findOne({ _id: new ObjectId(studentId) })
if (!studentExists) {
res
.status(400)
.json({ error: `${studentId} と ${studentName}は紐付いていません。` })
return
}
const appointment = {
date,
teacherName,
teacherId,
studentName,
studentId,
course,
location,
appointmentLink: appointmentLink || ' '
}
await db
.collection('user')
.updateOne(
{ _id: new ObjectId(teacherId) },
{ $push: { appointments: appointment } }
)
await db
.collection('user')
.updateOne(
{ _id: new ObjectId(studentId) },
{ $push: { appointments: appointment } }
)
res.status(200).json(appointment)
} else {
res.status(400).json({ error: 'Wrong request method' })
}
}
next-auth との接続
指示に従ってコードの編集。
- Allowed Callback URLs内 http://localhost:3000/api/auth/callback/auth0 に書き換え
- Settings より Domain Client ID Client Secret をコピーし .env.local に貼り付け
pages/api/auth/[...nextauth].tsx
import NextAuth from 'next-auth'
import Providers from 'next-auth/providers'
import { NextApiRequest, NextApiResponse } from 'next'
const options = {
// Configure one or more authentication providers
providers: [
Providers.Auth0({
clientId: process.env.AUTH0_CLIENT_ID,
clientSecret: process.env.AUTH0_CLIENT_SECRET,
domain: process.env.AUTH0_DOMAIN
})
]
}
export default (
req: NextApiRequest,
res: NextApiResponse
): void | Promise<void> => NextAuth(req, res, options)
ページの設定
pages/index.tsx
import React from 'react'
import Head from 'next/head'
import { Container } from '../styles/pages/Home'
import { signIn, signOut, useSession } from 'next-auth/client'
import { NextPage } from 'next'
const Home: NextPage = () => {
const [session, loading] = useSession()
return (
<Container>
<Head>
<title>Homepage</title>
</Head>
{!session && (
<>
Not signed in <br />
<button onClick={(): Promise<void> => signIn('auth0')}>
Sign in
</button>
</>
)}
{session && (
<>
Signed in as {session.user.email} <br />
<button onClick={(): Promise<void> => signOut()}>Sign out</button>
</>
)}
{loading && <h1>Loading...</h1>}
</Container>
)
}
export default Home
セッション状態をページ間で共有できるようにするために、NextAuth.js の Provider を使用
pages/_app.tsx
import React from 'react'
import { AppProps } from 'next/app'
import { ThemeProvider } from 'styled-components'
import GlobalStyle from '../styles/global'
import theme from '../styles/theme'
import { Provider } from 'next-auth/client'
function MyApp({ Component, pageProps }: AppProps): JSX.Element {
return (
<ThemeProvider theme={theme}>
<Provider session={pageProps.session}>
<Component {...pageProps} />
<GlobalStyle />
</Provider>
</ThemeProvider>
)
}
export default MyApp
MongoDB と NextAuth とのデータを紐付ける(SWR)
pages/profile.tsx
import React from 'react'
import Navbar from '../components/NavBar/NavBar'
import Head from 'next/head'
import { Container } from '../styles/pages/Home'
import { signIn, signOut, useSession } from 'next-auth/client'
import { NextPage } from 'next'
import useSWR from 'swr'
import api from '../utils/api'
const Profile: NextPage = () => {
const [session, loading] = useSession()
const { data, error } = useSWR(`/api/user/${session?.user.email} `, api) //第一引数にfetchするURL文字列,第二引数に、第一引数で渡したURLを引数に取りデータを返却するfetch関数
//返り値のオブジェクトの中のdataがfetch関数で返却したデータ
return (
<Container>
<Head>
<title>Homepage</title>
</Head>
<Navbar />
{!session && (
<>
Not signed in <br />
<button onClick={(): Promise<void> => signIn('auth0')}>
Sign in
</button>
</>
)}
{session && data && (
<>
<h1>Profile Page</h1>
Signed in as {session.user.email} <br />
<p>{data.data.name}</p>
<p>{data.data.email}</p>
<button onClick={(): Promise<void> => signOut()}>Sign out</button>
</>
)}
{error && <h1>dont exisit {session?.user.email} </h1>}
{loading && <h1>Loading...</h1>}
</Container>
)
}
export default Profile
条件にあったユーザーをセレクト
バックエンド側で courses を取り出す
import { NextApiRequest, NextApiResponse } from 'next'
import connect from '../../../utils/database'
interface SuccessResponseType {
name: string
email: string
teacher: boolean
courses: string[]
}
interface ErrorResponseType {
error: string
}
export default async (
req: NextApiRequest,
res: NextApiResponse<SuccessResponseType[] | ErrorResponseType>
): Promise<void> => {
if (req.method === 'GET') {
const courses = req.query.courses as string
if (!courses) {
res
.status(400)
.json({ error: 'courses というプロパティを指定してください。' })
return
}
const { db } = await connect()
const response = await db
.collection('user')
.find({ courses: { $in: [new RegExp(`^${courses}`, 'i')] } })
.toArray()
if (response.length === 0) {
res
.status(400)
.json({ error: 'そのような Course は登録されていません。' })
return
}
res.status(200).json(response)
} else {
res.status(400).json({ error: 'Wrong request method' })
}
}
フロントエンド側でコースを抽出
pages/search/index.tsx
import React, { useState, useCallback } from 'react'
import Navbar from '../../components/NavBar/NavBar'
import Head from 'next/head'
import { Container } from '../../styles/pages/Home'
import { signIn, signOut, useSession } from 'next-auth/client'
import { NextPage } from 'next'
import api from '../../utils/api'
import Link from 'next/link'
interface Teacher {
name: string
email: string
teacher: boolean
courses: string[]
_id: string
appointments: Record<string, unknown>[]
}
const Search: NextPage = () => {
const [textInput, setTextInput] = useState('')
const [data, setData] = useState<Teacher[]>([])
const [session, loading] = useSession()
const handleSearch = useCallback(() => {
api(`/api/search/${textInput}`).then(response => {
const teacher: Teacher[] = response.data
setData(teacher)
})
}, [textInput])
return (
<Container>
<Head>
<title>Homepage</title>
</Head>
<Navbar />
<h1>Search</h1>
{!session && (
<>
Not signed in <br />
<button onClick={(): Promise<void> => signIn('auth0')}>
Sign in
</button>
</>
)}
{session && (
<>
<h1>言語を選択</h1>
<input
value={textInput}
onChange={e => setTextInput(e.target.value)}
type="text"
placeholder="Nome da matéria"
className="bg-pink-200"
/>
<button type="submit" onClick={handleSearch}>
Submit
</button>
<Container>
Signed in as {session.user.email} <br />
<button onClick={(): Promise<void> => signOut()}>Sign out</button>
</Container>
{data.length !== 0 &&
data.map(teacher => (
<Link href={`/search/${teacher._id}`} key={teacher._id}>
<a style={{ textDecoration: 'none' }}>
<h1>{teacher.name}</h1>
</a>
</Link>
))}
</>
)}
{loading && <h1>Loading...</h1>}
</Container>
)
}
export default Search
axios でパスを取得
utils/api
import axios, { AxiosResponse } from 'axios'
export default function api(path: string): Promise<AxiosResponse<any>> {
return axios.get(path)
}
個人データを取得
pages/search/[_id].tsx
import React from 'react'
import axios from 'axios'
import { GetServerSideProps, GetServerSidePropsContext } from 'next'
interface Teacher {
name: string
email: string
teacher: boolean
courses: string[]
_id: string
appointments: Record<string, unknown>[]
}
export default function teacherProfilePage({
name,
email,
_id
}: Teacher): JSX.Element {
return (
<>
<h1>Name: {name}</h1>
<h1>E-mail: {email}</h1>
<h1>ID: {_id}</h1>
</>
)
}
export const getServerSideProps: GetServerSideProps = async (
context: GetServerSidePropsContext
) => {
const _id = context.query._id as string
const response = await axios.get<Teacher>(
`http://localhost:3000/api/teacher/${_id}`
)
const teacher = response.data
return {
props: teacher
}
}