Overview
Based on this article:
Here's my sample code:
https://github.com/rowaxl/nextjs-with-firebase-hosting-functions
Background
Next.js is one of the most popular frameworks to make a React app with both Server Side Rendering and Static Site. So when the project started, I planned to use Next.js to generate a static website that will work fine, which means Firebase Hosting is quite fine(cannot choose Vercel, because it using a closed source library). After it was deployed, this project needed SSR-related features, like persist save of route.
This article helps me a lot.
Create a Next.js App
First of all, start with creating the boilerplate Next.js app.
$ yarn create next-app
or
$ npx create-next-app
create-next-app
module must be installed on global.
export default function Home(props) {
return (
<div>
<div>
{
props.isSSR ? (
<h2>
SSR Working
</h2>
) : (
<h2>
SSR Not Works
</h2>
)
}
</div>
<div>
{
props.isStatic ? (
<h2>
Static generated
</h2>
) : (
<h2>
is Not Static
</h2>
)
}
</div>
</div>
)
}
// currently, this method occurs error
export async function getServerSideProps() {
return { props: { isSSR: true } }
}
// currently, this method occurs error too!
export async function getStaticProps() {
return { props: { isStatic: true } }
}
Here's our base code for testing with SSR (Server Side Rendering) and SSG (Static Site Generation).
getServerSideProps
and getStaticProps
looks like doing same works. It fetches data before page renders, and provides data as props.
Before start, you cannot use getServerSideProps
and getStaticProps
at a same time.
getServerSideProps (SSR)
It will be called when Next.js server got a request. It slow than getStaticProps
, because it occurs every time get a request, but if you need to pre-render a page with dynamic data, definitely getServerSiteProps
makes it convenient to deal with it.
It only runs on server-side and routing by next/link
and next/router
runs it every time.
getStaticProps (SSG)
It generates static HTML files when next export
runs. With getStaticPaths
, you can use specific dynamic data to use.
If you need details and sample codes, please check official documents.
Deploy Next.js App with only Hosting (SSG)
Ready Firebase Projects
At first, I used SSG, because Firebase Hosting supports only static files.
So, let's start with initializing Firebase project.
When your project is deployed, add a new web app.
Then install Firebase CLI in your global
npm install -g firebase-tools
or
yarn global add firebase-tools
And click Next
and Continue to the console
. It will be deployed little bit a later.
Change codes a bit
Before execute next export
, remove the getServerSideProps
first. You can check the result of getStaticProps
with next dev
command.
// pages/index.js
export default function Home(props) {
return (
<div>
<div>
{
props.isSSR ? (
<h2>
SSR Working
</h2>
) : (
<h2>
SSR Not Works
</h2>
)
}
</div>
<div>
{
props.isStatic ? (
<h2>
Static generated
</h2>
) : (
<h2>
is Not Static
</h2>
)
}
</div>
</div>
)
}
export async function getStaticProps() {
return { props: { isStatic: true } }
}
So, it will shows like below when it runs.
Run yarn build
and yarn next export
to create static files. It will be exported to out
as a default folder.
You can see out/index.html
already has the texts that we expected:
When exporting success, let's try to deploy it to the Firebase Hostings.
$ firebase login
// login page will show up.
$ firebase init
// select the hosting only at this time.
Then, edit firebase.json
a little bit.
{
"hosting": {
"public": "out",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
]
}
}
OK, it looks ready for deployment. Let's try firebase deploy
.
If anything goes wrong, it will say ✔ Deploy complete!
and the Hosting URL for us.
It looks like it works perfectly!
Deploy Next.js App with Functions (SSR)
Next step, what we need to run our Next.js App on SSR is Firebase Functions.
It means you HAVE TO UPGRADE your billing plan to Blaze. Also, install firebase-functions
and firebase-admin
modules for deploying.
Update to use getServerSideProps
return (
<div>
<div>
{
props.isSSR ? (
<h2>
SSR Working
</h2>
) : (
<h2>
SSR Not Works
</h2>
)
}
</div>
<div>
{
props.isStatic ? (
<h2>
Static generated
</h2>
) : (
<h2>
is Not Static
</h2>
)
}
</div>
</div>
)
}
export async function getServerSideProps() {
return { props: { isSSR: true } }
}
WARNING: You should not use getServerSideProps
and getStaticProps
at the same time! This will show you build error.
Add Next server functions
// function.js
const { https } = require('firebase-functions');
const { default: next } = require('next');
const isDev = process.env.NODE_ENV !== 'production';
const server = next({
dev: isDev,
conf: { distDir: '.next' },
});
const nextjsHandle = server.getRequestHandler();
exports.nextServer = https.onRequest((req, res) => {
return server.prepare().then(() => nextjsHandle(req, res));
});
This one will handle requests toward to client, render pages, and return as a response.
And, update your firebase.json
like below
{
"hosting": {
"public": "public",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
"rewrites": [
{
"source": "**",
"function": "nextServer"
}
]
},
"functions": {
"source": ".",
"runtime": "nodejs14",
"ignore": [
".firebase/**",
".firebaserc",
"firebase.json",
"**/node_modules/**"
]
}
}
Also, update package.json
for deploy command
{
"name": "firebase-deploy",
"version": "0.1.0",
"main": "function.js",
// ...
}
Don't forget to remove public/index.html
, which is generated by firebase-tools
What's going on here is like below:
- The initial request goes to hosting
- When the request routes are not the
/
, then rewrites the request to functions named nextServer (hostings → functions) -
nextServer
handles request, renders it as a Next.js server, and returns the response.
After edit, remove .out
folder and try next build
, firebase deploy --only functions:nextServer,hosting
Then, you can see the result like below
🎉 SSR enabled! 🎉
Troubles that I encountered
400 unknown error in deployment
The first time deploying nextServer
function, it worked fine. But the next moment, it shows 400 unknown error
and failed when I tried to upload the new version.
When I run deploy command with --debug
option, it showed the logs like below:
<Code>
EntityTooLarge
</Code>
<Message>
Your proposed upload is larger than the maximum object size specified in your Policy Document.
</Message>
<Details>
Content-length exceeds upper bound on the range
</Details>
Initially, my Next app was over 200 MB after packaged. I believe Firebase Functions supports 1GB of functions for storing data, but it is obvious that some problems with the size of it.
What I've done is editing firebase.json
.
"functions": {
"source": ".",
// ...
"ignore": [
"**/src/**",
"**/.vscode/**",
".firebase/**",
".firebaserc",
"firebase.json",
"**/node_modules/**",
"**/public/**",
"**/.next/cache/**"
]
}
The cause I set the path of source
as .
is I need to deploy the function.js
and .next
folder simultaneously. But I've missed that it included src
, node_modules
, and .next/cache
. These were completely unnecessary to me, so I add some lines on ignore
option of firebase.json
.
Then, the package lightened and the problem solved! Its size reduced to 30MBs and deployment is way much faster now. I strongly recommend trying this optimization with it.
403 occurred when access to hosting(From Firebase Functions)
And a few days later, I've got another error like, Your client does not have permission to get URL (function name)
.
It was after deployed the new version of the app, it just returned 403 page, without any logs in Functions dashboard. Also, the backend using firebase functions worked fine and it made me super confusing.
I read carefully the response, and it was not the page that I've implemented, which means it is not occurred by my codes. What I tried is:
- Delete and install latest
firebase-tools
- Change the function name and deploy it as a new function
- Check the GCP -> Cloud Functions -> Authentication
As a result, the third one solved the problem. I'm not sure why, but Authentication
setting for the function nextServer
was changed to need the auth. This was the cause of the error.
After add allUsers
as a Function Invoker (this project needed to keep published), it works just as I intended.
Conclusion
So, that’s it for this article.
First of all, thanks to Dipesh K.C. who wrote the article saved me a lot.
I Usually use Vercel or Netlify for personal projects, but this time, the client demanded to use Firebase for their reason.
Maybe, using Google App Engine is the better way for some cases.
But it gave me a chance to understand Next.js and Firebase more deeply. Hope this article helps someone have the same trouble as me. Any suggestions and corrections are welcome.
Top comments (5)
I have small problem :) My server function is inside "functions" folder created with default setup of firebase (./functions/src/index.js) and after deploy in the browser I have "Error: could not handle the request" and in Firebase Functions log: "Couldn't find a
pages
directory. Please create one under the project root". How can I fix it?/.git/ is also what is needed to be added I guess in the ignore list :D
That’s SO true! XD
I hope there’s a way for deploying just .next folder with function file!
Would the next api routes work with firebase hosting?
Haven't tried it but I think it should work. The function plays the role of a web server which is what nextjs is designed to run on, routing the firebase functions params through the next build should run the middle wares required of the next API routes.
Although it is clever how it works, I'm skeptical at how inefficient this is compared to a traditional node server or vercel serverless deployment.
Edit: tested this today and it does work. API Routes are run via the same next server that serves the SSR pages