Skip to content

Commit a0ca444

Browse files
committed
added deployment documentation
1 parent 515697c commit a0ca444

8 files changed

+201
-10
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
---
2+
description: >-
3+
Take the client and API server applications into production. The React client app will be hosted on Netlify. The Node.js server API will be hosted on Render.
4+
keywords:
5+
- Vite Build
6+
- Netlify
7+
- Render
8+
---
9+
10+
## Deploying
11+
12+
> 📝 Deploying was discussed, but not demonstrated in the course. You can follow the steps below if you would like to deploy the client and server applications.
13+
14+
With React or any client-side framework, deployment is easy. Run `npm run build` to generate your HTML, CSS, and JS file with Vite and toss them on any static web host. This project is a little different because it calls an API from a local Node.js server. So that needs to be deployed too. What good is a pizza ordering website with out pizzas, right?
15+
16+
To get both the client and server applications deployed, we'll have to do a little refactoring. We'll also introduces some best practices along the way. The React client app will be deployed to [Netlify](https://www.netlify.com/) and the Node.js server app will get deployed to [Render](https://render.com/). Both platforms have free tiers and we'll use a [GitHub](https://github.com/) workflow to make deployment super easy.
17+
18+
> If you want to skip all the refactoring and just deploy the finished applications, you can use the code in the `pizza-client-app` and `pizza-server-app` directories in the [18-deploying directory](https://github.com/btholt/citr-v9-project/tree/main/18-deploying) in the repo.
19+
20+
### Refactoring the Static Assets
21+
22+
Our CSS, fonts, and images are all currently hosted in the API server application. This was purely for convenience so they didn't need to be copied across each code checkpoint in the course. In the real world, these should be with the React application.
23+
24+
Move the `public` folder from the API project to the React project:
25+
26+
![public folder in the VS Code file explorer](/images/deploy-public.png)
27+
28+
Also, Vite assumes anything in the `public` directory lives at the root of the published files so open `style.css` and remove all the "public" references. For example, the font should load from:
29+
30+
```css
31+
src: url("/Pacifico-Regular.ttf");
32+
```
33+
34+
Lastly, update the stylesheet link in the `index.html` head:
35+
36+
```html
37+
<link rel="stylesheet" href="/style.css" />
38+
```
39+
40+
### Deploy the Node.js API Application
41+
42+
We still have some work to do in the React app, but let's get the server application deployed. This is a Node.js application built with Fastify so you need a host that supports Node. We're going to use [Render](https://render.com/) so create an account there if you haven't already.
43+
44+
We're going to deploy it to Render with GitHub. Create a new GitHub repo for the API server. I'm using `pizza-server-app`. Run `git init` in your local project and add your GitHub repo as a remote repository so we can push the app there when we're ready.
45+
46+
Now to some refactoring. We moved the `/public` directory to the client app so we can delete the static file serving:
47+
48+
```javascript
49+
server.register(fastifyStatic, {
50+
...
51+
});
52+
```
53+
54+
Our image paths need to be updated too. Remove every reference to `public` in the API responses. For example:
55+
56+
```javascript
57+
// Any image property like this:
58+
image: `/public/pizzas/${pizza.pizza_type_id}.webp`,
59+
60+
// ...Should have public removed from it:
61+
image: `/pizzas/${pizza.pizza_type_id}.webp`,
62+
```
63+
64+
There no reason we need a fake 5 second wait in a production application (it was added to slow down requests so we could see the loading states locally). Remove the Promise:
65+
66+
```javascript
67+
await new Promise((resolve) => setTimeout(resolve, 5000));
68+
```
69+
70+
Who loves CORS? Since our client application is loading data from a separate server, we need to allow that. The Vite proxy fixed that issue for us locally, but that won't work in production. We're taking the easy way out and just allowing everything. Add this Fastify hook to the server and check out the Frontend Masters [Web Security course](https://frontendmasters.com/courses/web-security-v2/) to learn more about CORS.
71+
72+
```javascript
73+
server.addHook('preHandler', (req, res, done) => {
74+
res.header("Access-Control-Allow-Origin", "*");
75+
res.header("Access-Control-Allow-Methods", "GET, POST");
76+
res.header("Access-Control-Allow-Headers", "*");
77+
const isPreflight = /options/i.test(req.method);
78+
if (isPreflight) {
79+
return res.send();
80+
}
81+
done();
82+
})
83+
```
84+
85+
Lastly, Render requires your application to bind the `host` property to `0.0.0.0` instead of `localhost`, so create a property and update the `server.listen()` method:
86+
87+
```javascript
88+
// Create a HOST property after the PORT property
89+
const HOST = ("RENDER" in process.env) ? `0.0.0.0` : `localhost`;
90+
91+
// pass it to the server.listen() method:
92+
await server.listen({ host: HOST, port: PORT });
93+
```
94+
95+
### Deploy to Render
96+
97+
Ok, we are ready to deploy. Push your code to your GitHub repo.
98+
99+
![Node.js server application files in a GitHub repo](/images/deploy-repo.png)
100+
101+
Head over to the [Render Dashboard](https://dashboard.render.com/) and **Deploy a New Web Service**. Connect your GitHub account and choose the `pizza-server-app` repo (or whatever you called yours) as the source code for the project. All the defaults should work, however, make sure you select Free/Hobby tier for the server instance type.
102+
103+
Once your server has deployed, you’ll be able to access it by the URL displayed in your dashboard. You can test it by accessing the `[yourserver]/api/pizzas` endpoint:
104+
105+
![Node.js server application files in a GitHub repo](/images/deploy-render.png)
106+
107+
Your server API is deployed! And the cool thing about this is any time you push to your repo, it will automatically redeploy to Render.
108+
109+
### Deploying the Client Application
110+
111+
Before we deploy the client application, we need to tweak a few things. We have a local development API server and a remote production API server -- both with two different URLs. We don't want these hard-coded in our application nor do we want to worry about switching the URL when we build our app. We're going to use some Vite environment variables to help us. Create two files in the root: `.env.development` and `.env.production`. Then add a `VITE_API_URL` to each:
112+
113+
```bash
114+
# Add this to .env.development
115+
VITE_API_URL=""
116+
117+
118+
# Add this to .env.production
119+
VITE_API_URL="https://[YOUR-SERVER-URL].onrender.com"
120+
121+
```
122+
123+
You can learn more about this in our [Vite course](https://frontendmasters.com/courses/vite/), but the short version is these variables are added to the `import.meta.env` property by Vite (depending on it being development or production/build). We are not ignoring these files in Git so you wouldn't want any sensitive data added to these environment files. If we are running `npm run build`, add our production URL. If we are running `npm run dev` don't add anything -- because our local proxy will handle it.
124+
125+
Speaking of proxy, you can remove the `/public` proxy from the `vite.config.js` file. Now let's use `VITE_API_URL`. Find our three API helpers (getPastOrders.js, getPastOrder.js, and usePizzaOfTheDay.js) and update the fetch call to use the environment variable. For example:
126+
127+
```javascript
128+
const apiUrl = import.meta.env.VITE_API_URL;
129+
const response = await fetch(`${apiUrl}/api/past-order/${order}`);
130+
```
131+
132+
You'll also need to do this in `order.lazy.jsx`:
133+
134+
```javascript
135+
// create the property at the top
136+
const apiUrl = import.meta.env.VITE_API_URL;
137+
138+
// update checkout
139+
await fetch(`${apiUrl}/api/order`, {
140+
...
141+
})
142+
143+
// update fetchPizzaTypes
144+
const pizzasRes = await fetch(`${apiUrl}/api/pizzas`);
145+
```
146+
147+
### Deploy to Netlify
148+
149+
Whew! I think that's it. Push your code up to a septate GitHub repo. We're now ready to deploy!
150+
151+
![React client app files in a GitHub repo](/images/deploy-client-repo.png)
152+
153+
Head over to [Netilfy](https://www.netlify.com/), create an account if you don't already have one, and click to create a new site. You’ll choose to import a project from GitHub:
154+
155+
![Choosing a repo in Netlify](/images/deploy-netlify.png)
156+
157+
When configuring the project, the defaults should work:
158+
159+
![Netlify configuration settings](/images/deploy-netlify-config.png)
160+
161+
Once your site is deployed, you should be able to access it with the name you selected during the setup. For example, https://[Your-App-Name].netlify.app.
162+
163+
### Congrats!
164+
165+
You now have a client React application deployed to Netlify and a Node.js server API deployed to Render!
166+
167+
168+
169+
170+
171+

public/images/deploy-client-repo.png

283 KB
Loading
124 KB
Loading

public/images/deploy-netlify.png

95.7 KB
Loading

public/images/deploy-public.png

49 KB
Loading

public/images/deploy-render.png

92.9 KB
Loading

public/images/deploy-repo.png

46 KB
Loading

styles/courses.css

+30-10
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,8 @@ a {
7676
}
7777

7878
header .cta-btn {
79-
display: none; /* only displays at large screen sizes */
79+
display: none;
80+
/* only displays at large screen sizes */
8081
}
8182

8283
.main .cta-btn {
@@ -130,16 +131,19 @@ header .cta-btn {
130131
width: 100%;
131132
min-height: 45vh;
132133
}
134+
133135
.jumbotron .courseInfo,
134136
.jumbotron .courseIcon {
135137
display: flex;
136138
justify-content: center;
137139
align-items: center;
138140
}
141+
139142
.jumbotron .courseInfo {
140143
width: 65%;
141144
text-align: right;
142145
}
146+
143147
.jumbotron .courseIcon {
144148
width: 35%;
145149
display: flex;
@@ -150,6 +154,7 @@ header .cta-btn {
150154
.author {
151155
justify-content: flex-end;
152156
}
157+
153158
.jumbotron .courseInfo-inner {
154159
max-width: 85%;
155160
}
@@ -226,7 +231,7 @@ header .cta-btn {
226231
border-radius: 8px;
227232
}
228233

229-
.lesson > h1 {
234+
.lesson>h1 {
230235
color: var(--text-header);
231236
font-size: 24px;
232237
}
@@ -333,11 +338,11 @@ blockquote {
333338
margin: 10px 0;
334339
}
335340

336-
blockquote > *:last-child {
341+
blockquote>*:last-child {
337342
margin-bottom: 0;
338343
}
339344

340-
blockquote > *:first-child {
345+
blockquote>*:first-child {
341346
margin-top: 0;
342347
}
343348

@@ -380,7 +385,7 @@ ol.sections-name {
380385
padding: 0;
381386
}
382387

383-
ol.sections-name > li {
388+
ol.sections-name>li {
384389
counter-increment: my-awesome-counter;
385390
display: flex;
386391
flex-direction: row;
@@ -391,6 +396,7 @@ ol.sections-name > li {
391396
border-bottom-right-radius: 5px;
392397
border-top-right-radius: 5px;
393398
}
399+
394400
ol.sections-name .lesson-preface {
395401
color: var(--icons);
396402
display: flex;
@@ -403,7 +409,7 @@ ol.sections-name .lesson-preface {
403409
width: 40%;
404410
}
405411

406-
.lesson-preface.lesson-preface > svg {
412+
.lesson-preface.lesson-preface>svg {
407413
width: 80%;
408414
height: inherit;
409415
max-height: 100px;
@@ -440,12 +446,12 @@ ol.sections-name .lesson-details {
440446
right: 0;
441447
}
442448

443-
.details-bg > svg {
449+
.details-bg>svg {
444450
width: 100%;
445451
height: auto;
446452
}
447453

448-
.details-bg > svg path {
454+
.details-bg>svg path {
449455
transition: fill 0.25s;
450456
}
451457

@@ -455,10 +461,11 @@ ol.sections-name .lesson-details {
455461
}
456462

457463
@media (min-width: 1000px) {
458-
ol.sections-name > li::before {
464+
ol.sections-name>li::before {
459465
border-bottom-left-radius: 5px;
460466
border-top-left-radius: 5px;
461467
}
468+
462469
ol.sections-name .lesson-details {
463470
border-bottom-right-radius: 5px;
464471
border-top-right-radius: 5px;
@@ -484,6 +491,19 @@ ol.sections-name .lesson-details {
484491
margin-bottom: 20px;
485492
}
486493

494+
.lesson-content h3 {
495+
margin-top: 2rem;
496+
}
497+
498+
.lesson-content h3::after {
499+
content: "";
500+
display: block;
501+
height: 1px;
502+
margin-top: 5px;
503+
background: var(--text-header);
504+
max-width: 300px;
505+
}
506+
487507
.lesson-links {
488508
margin-top: 45px;
489509
margin-bottom: 80px;
@@ -534,4 +554,4 @@ pre code.hljs {
534554
pre code,
535555
pre code.hljs {
536556
display: block;
537-
}
557+
}

0 commit comments

Comments
 (0)