with-firebase example for SSR (#16070)

I couldn't find an example when creating an SSR page using firebase's firestore data, so I improved the example based on the actual app I created

My sample app: https://github.com/mikan3rd/commitly
This commit is contained in:
mikan3rd 2020-11-25 04:57:23 +09:00 committed by GitHub
parent af5b8ceb7a
commit 9ca1f39f75
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 99 additions and 4 deletions

View file

@ -5,4 +5,8 @@ NEXT_PUBLIC_FIREBASE_DATABASE_URL=
NEXT_PUBLIC_FIREBASE_PROJECT_ID=
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=
NEXT_PUBLIC_FIREBASE_APP_ID=
NEXT_PUBLIC_FIREBASE_APP_ID=
# for firebase-admin
FIREBASE_CLIENT_EMAIL=
FIREBASE_PRIVATE_KEY=

View file

@ -33,6 +33,8 @@ cp .env.local.example .env.local
3. Set each variable on `.env.local` with your Firebase Configuration (found in "Project settings").
4. If you want to check the SSR page, get your account credentials from the Firebase console at _Project settings > Service accounts_, where you can click on _Generate new private key_ and download the credentials as a json file. Then set `FIREBASE_CLIENT_EMAIL` and `FIREBASE_PRIVATE_KEY` in `.env.local`
## Deploy on Vercel
You can deploy this app to the cloud with [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)).

View file

@ -0,0 +1,13 @@
import admin from '../firebase/nodeApp'
export const getProfileData = async (username) => {
const db = admin.firestore()
const profileCollection = db.collection('profile')
const profileDoc = await profileCollection.doc(username).get()
if (!profileDoc.exists) {
return null
}
return profileDoc.data()
}

View file

@ -18,7 +18,10 @@ const clientCredentials = {
if (typeof window !== 'undefined' && !firebase.apps.length) {
firebase.initializeApp(clientCredentials)
// To enable analytics. https://firebase.google.com/docs/analytics/get-started
if ('measurementId' in clientCredentials) firebase.analytics()
if ('measurementId' in clientCredentials) {
firebase.analytics()
firebase.performance()
}
}
export default firebase

View file

@ -0,0 +1,14 @@
import * as admin from 'firebase-admin'
if (!admin.apps.length) {
admin.initializeApp({
credential: admin.credential.cert({
projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
clientEmail: process.env.FIREBASE_CLIENT_EMAIL,
privateKey: process.env.FIREBASE_PRIVATE_KEY.replace(/\\n/g, '\n'),
}),
databaseURL: process.env.NEXT_PUBLIC_FIREBASE_DATABASE_URL,
})
}
export default admin

View file

@ -8,6 +8,7 @@
},
"dependencies": {
"firebase": "7.17.0",
"firebase-admin": "9.0.0",
"next": "latest",
"react": "^16.13.0",
"react-dom": "^16.13.0"

View file

@ -1,4 +1,5 @@
import Head from 'next/head'
import Link from 'next/link'
import { useEffect } from 'react'
import { useUser } from '../context/userContext'
import firebase from '../firebase/clientApp'
@ -7,6 +8,8 @@ export default function Home() {
// Our custom hook to get context values
const { loadingUser, user } = useUser()
const profile = { username: 'nextjs_user', message: 'Awesome!!' }
useEffect(() => {
if (!loadingUser) {
// You know that the user is loaded: either logged in or out!
@ -16,6 +19,12 @@ export default function Home() {
console.log(firebase)
}, [loadingUser, user])
const createUser = async () => {
const db = firebase.firestore()
await db.collection('profile').doc(profile.username).set(profile)
alert('User created!!')
}
return (
<div className="container">
<Head>
@ -26,6 +35,19 @@ export default function Home() {
<main>
<h1 className="title">Next.js w/ Firebase Client-Side</h1>
<p className="description">Fill in your credentials to get started</p>
<p className="description">
Cloud Firestore Security Rules write permissions are required for
adding users
</p>
<button onClick={createUser}>Create 'nextjs_user'</button>
<p className="description">
Please press the link below after adding the user
</p>
<Link href={`/profile/${profile.username}`} passHref>
<a>Go to SSR Page</a>
</Link>
</main>
<style jsx>{`
@ -66,9 +88,14 @@ export default function Home() {
align-items: center;
}
button {
font-size: 1.5em;
margin: 1em 0;
}
a {
color: inherit;
text-decoration: none;
color: blue;
font-size: 1.5em;
}
.title a {

View file

@ -0,0 +1,31 @@
import Head from 'next/head'
import { getProfileData } from '../../fetchData/getProfileData'
export default function SSRPage({ data }) {
const { username, profile } = data
return (
<div className="container">
<Head>
<title>Next.js w/ Firebase Client-Side</title>
<link rel="icon" href="/favicon.ico" />
</Head>
<main>
<h1 className="title">Next.js w/ Firebase Server-Side</h1>
<h2>{username}</h2>
<p>{profile.message}</p>
</main>
</div>
)
}
export const getServerSideProps = async ({ params }) => {
const { username } = params
const profile = await getProfileData(username)
if (!profile) {
return { notFound: true }
}
return { props: { data: { username, profile } } }
}