-
Notifications
You must be signed in to change notification settings - Fork 2.2k
docs(universal): getting started, serving with Cloud Functions, and prerendering #1841
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
c92e538
docs(ssr): New TOC for Angular Universal
jamesdaniels 8ad0fe7
docs(ssr): Handle lazy modules and webpack config for Cloud Functions
jamesdaniels 0abf5ae
docs(ssr): Rearrange a bit and start of prerendering
jamesdaniels a28b96d
docs(): remove extra real-time add missing modules
jamesdaniels 3b3d4ac
docs(ssr): Cleaned up the scripts
jamesdaniels f7c87fb
docs(ssr): Language changes and indenting
jamesdaniels d81f4ce
docs(ssr): now fairly trivial, drop the optional
jamesdaniels f825807
Merge branch 'master' into universal_docs
jamesdaniels 77e20e2
Fixed realtime references
jamesdaniels e758812
Reference to angularfire2
jamesdaniels 0a34424
Flushing out the Cloud Functions instructions
jamesdaniels b18b2bd
Flush out the prerendering doc
jamesdaniels dfaaac8
Render them...
jamesdaniels 94d9b71
AngularFire case
jamesdaniels 264ae4b
Merge branch 'master' into universal_docs
jamesdaniels File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
# Deploying your Universal application on Cloud Functions for Firebase | ||
|
||
After [setting up your application with Angular Universal as outlined in Getting Started](getting-started.md), you're now ready to build your application for Firebase Hosting & Cloud Functions. | ||
|
||
> Cloud Functions for Firebase lets you automatically run backend code in response to events triggered by Firebase features and HTTPS requests. Your code is stored in Google's cloud and runs in a managed environment. There's no need to manage and scale your own servers. [Learn more about Cloud Functions for Firebase](https://firebase.google.com/docs/functions/). | ||
|
||
If you don't already have the Firebase CLI installed, do so: | ||
|
||
```bash | ||
npm i -g @firebase-tools | ||
firebase login | ||
``` | ||
|
||
Then inside your project root, setup your Firebase CLI project: | ||
|
||
```bash | ||
firebase init | ||
``` | ||
|
||
Configure whichever features you'd want to manage but make sure to select at least `functions` and `hosting`. Choose Typescript for Cloud Functions and use the default `public` directory for Hosting. | ||
|
||
After you're configured, you should now see a `firebase.json` file in your project root. Let's add the following `rewrites` directive to it: | ||
|
||
```js | ||
{ | ||
// ... | ||
"hosting": { | ||
// ... | ||
"rewrites": [ | ||
{ "source": "**", "function": "universal" } | ||
] | ||
} | ||
} | ||
``` | ||
|
||
This will inform Firebase Hosting that it should proxy all requests to Cloud Functions, if a file isn't already present in the hosting directory. | ||
|
||
Let's go ahead and modify your `package.json` to build for Cloud Functions: | ||
|
||
```js | ||
"scripts": { | ||
// ... omitted | ||
"build": "ng build && npm run copy:hosting && npm run build:ssr && npm run build:functions", | ||
"copy:hosting": "cp -r ./dist/YOUR_PROJECT_NAME/* ./public && rm ./public/index.html", | ||
"build:functions": "npm run --prefix functions build" | ||
}, | ||
``` | ||
|
||
Change the build script in your `functions/package.json` to the following: | ||
|
||
```js | ||
"scripts": { | ||
// ... omitted | ||
"build": "rm -r ./dist && cp -r ../dist . && tsc", | ||
} | ||
``` | ||
|
||
Finally, add the following to your `functions/src/index.ts`: | ||
|
||
```ts | ||
export const universal = functions.https.onRequest((request, response) => { | ||
require(`${process.cwd()}/dist/YOUR_PROJECT_NAME-webpack/server`).app(request, response); | ||
}); | ||
``` | ||
|
||
We you should now be able to run `npm run build` to build your project for Firebase Hosting and Cloud Functions. | ||
|
||
To test, spin up the emulator with `firebase serve`. Once you've confirmed it's working go ahead and `firebase deploy`. | ||
|
||
### [Next Step: Prerendering your Universal application](prerendering.md) | ||
|
||
## Additional Resources | ||
|
||
- [Universal Starter Template](https://github.com/angular/universal-starter) | ||
- [AngularFirebase SSR Videos](https://angularfirebase.com/tag/ssr/) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
# Prerendering your Universal application | ||
|
||
Prerendering a Universal application allows us to generate the HTML before the user requests it; increasing performance and decreasing cost. Let's configure your application to prerender and staticly serve it's most commonly accessed routes on Firebase Hosting. | ||
|
||
First create a `static.paths.js` in your project root, which lists the URLs you'd want to prerender: | ||
|
||
```js | ||
export default [ | ||
'/', | ||
'/another_path', | ||
'/yet_another_path' | ||
]; | ||
``` | ||
|
||
Let's install `mkdir-recursive` to make the next step a little easier: | ||
|
||
```bash | ||
npm i --save-dev mkdir-recursive | ||
``` | ||
|
||
Now replace the listener in your `server.ts` with the following: | ||
|
||
```ts | ||
import { readFileSync, writeFileSync, existsSync } from 'fs'; | ||
import { renderModuleFactory } from '@angular/platform-server'; | ||
import { mkdirSync } from 'mkdir-recursive'; | ||
|
||
if (process.env.PRERENDER) { | ||
|
||
const routes = require('./static.paths').default; | ||
Promise.all( | ||
routes.map(route => | ||
renderModuleFactory(AppServerModuleNgFactory, { | ||
document: template, | ||
url: route, | ||
extraProviders: [ | ||
provideModuleMap(LAZY_MODULE_MAP) | ||
] | ||
}).then(html => [route, html]) | ||
) | ||
).then(results => { | ||
results.forEach(([route, html]) => { | ||
const fullPath = join('./public', route); | ||
if (!existsSync(fullPath)) { mkdirSync(fullPath); } | ||
writeFileSync(join(fullPath, 'index.html'), html); | ||
}); | ||
process.exit(); | ||
}); | ||
|
||
} else if (!process.env.FUNCTION_NAME) { | ||
|
||
// If we're not in the Cloud Functions environment, spin up a Node server | ||
const PORT = process.env.PORT || 4000; | ||
app.listen(PORT, () => { | ||
console.log(`Node server listening on http://localhost:${PORT}`); | ||
}); | ||
} | ||
``` | ||
|
||
Now if the `PRERENDER` environment variable is passed any value, instead of serving your application it will iterate over the paths in `static.paths.js`, render them, and write them to your `public` directory. *You could always make this a seperate script.* | ||
|
||
Finally make some modifications to your `package.json`, to prerender your content when you build: | ||
|
||
```js | ||
"scripts": { | ||
// ... omitted | ||
"build": "ng build && npm run copy:hosting && npm run build:functions && npm run prerender:ssr", | ||
"prerender:ssr": "PRERENDER=1 node dist/YOUR_PROJECT_NAME-webpack/server.js", | ||
}, | ||
``` | ||
|
||
Now when you run `npm run build` the prerendered content should be available in your `/public` directory, ready for deployment on Firebase Hosting. |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we need more context around
'YOUR_APP_NAME
. Something comment like "This is the same as the folder name...".There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Better yet, my mid-term plan here is to read the paths from the
angular.json
file; need to fiddle there. SGTY?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SGTM!