Chapter 10: Environmental variables, production/development. Logger. APP server. API server. SEO - robots.txt, sitemap.xml. Server-side caching. Heroku. Testing application in production. AWS Elastic Beanstalk.
We keep our book up to date with recent libraries and packages.
The section below is a preview of SaaS Boilerplate Book. To read the full text, you will need to purchase the book.
In Chapter 10, you will start with the codebase in the 10-begin folder of our saas repo and end up with the codebase in the 10-end folder. We will cover the following topics in this chapter:
We will cover the following topics in this chapter:
Environmental variables, production/development
Logger
APP server
API server
SEO - robots.txt, sitemap.xml
Server-side caching
Google Analytics
Heroku
Testing application in production
AWS Elastic Beanstalk
In this last chapter of the book we work on preparing code to be deployed into production. In addition to preparing code to be deployed, we also add a few features along the way. You may or may not find these features useful for your particular product. For example, we will discuss and build server-side caching and add Google Analytics to your SaaS boilerplate among other features. As your SaaS business matures you may need more sophisticated caching and analytics but server-side caching and Google Analytics are good starting points.
Environmental variables, production/development link
In this section we are working on preparing our application for deployment. So far we only ran both APP
and API
projects locally. Locally, value of dev
is true
:
const dev = process.env.NODE_ENV !== 'production';
In production, after we deploy both projects to either Heroku or AWS Elastic Beanstalk, value of dev
is false
. This allows to have two sets of environmental variables, one set for development and one set for production. We recommend having development and production versions for each environmental variable for your real world SaaS application.
One of the most important is to set separate MongoDB for production. Currently, our API
server connects to database with URL with value MONGO_URL
. In Chapter 4, we created a free database inside free cluster M0 Sandbox (Shared RAM, 512 MB Storage)
using MongoDB Atlas service. Go ahead and create a new database using instructions from Chapter 4. This time select a tier that you think fits your needs the best. How much load do you expect your production application to have? Remember that you can always upgrade tier after creating cluster and overpaying for database might be costly over long period of time. Once you create a new MongoDB for production, assign its value to MONGO_URL
environmental variable. Take the old value of MONGO_URL
and assign it to new environmental variable MONGO_URL_TEST
.
Make sure that you have both env (environmental) variables with corresponding values inside book/10-begin/api/.env
file. Next step is to edit code to reflect that we want to use different env variables for development and production:
Open filebook/10-begin/api/server/server.ts
, replace line:
mongoose.connect(process.env.MONGO_URL_TEST);
With block:
mongoose.connect(dev ? process.env.MONGO_URL_TEST : process.env.MONGO_URL);
Remember to define dev
under import section as const dev = process.env.NODE_ENV !== 'production';
.
You may guessed it right by now - we will make a heavy use of conditional operator condition ? exprIfTrue : exprIfFalse
for passing development and production versions of env variable.
Next, open file book/10-begin/lambda/handler.ts
, replace block:
await mongoose.connect(process.env.MONGO_URL_TEST);
With:
await mongoose.connect(dev ? process.env.MONGO_URL_TEST : process.env.MONGO_URL);
Remember to define dev
under import section as const dev = process.env.NODE_ENV !== 'production';
.
Open file book/10-begin/lambda/api/test/server/utils/slugify.test.ts
. Here you need to decide whether you plan to test slugify
method in production or locally. It's unlikely, in our experience, that you will need to run testing suit in both environments. You are free to add conditional operator or simply use value for development:
await mongoose.connect(process.env.MONGO_URL_TEST);
We are done with databases' URLs. In Chapter 9, we already created two versions (live/test, production/development) of env variables for Stripe API. Next task is to update projects' URLs. Currently we access APP
at http://localhost:3000
and API
at http://localhost:8000
. Once deployed, APP
will be accessible at https://saas-app.async-await.com
and API
at https://saas-api.builderbook.org
. Here you need to decide and use your domain and URLs.
We already have URL_APP
and URL_API
env variables. Add two new env variables, PRODUCTION_URL_APP
and PRODUCTION_URL_API
to both files:
- book/10-begin/api/.env
- book/10-begin/app/.env
Make sure to prepend these two env variables with &aquot;NEXT_PUBLIC_" in the .env
file of your app
project to make them universally available.
Open file book/10-begin/api/server/server.ts
again and make following updates.
Replace:
cors({ origin: process.env.URL_APP, methods: 'GET,HEAD,PUT,PATCH,POST,DELETE', credentials: true, }),
With:
cors({ origin: dev ? process.env.URL_APP : process.env.PRODUCTION_URL_APP, methods: 'GET,HEAD,PUT,PATCH,POST,DELETE', credentials: true, }),
Replace:
setupSockets({ httpServer, origin: process.env.URL_APP, sessionMiddleware });
With:
setupSockets({
httpServer,
origin: dev ? process.env.URL_APP : process.env.PRODUCTION_URL_APP,
sessionMiddleware,
});
Replace:
http.listen(process.env.PORT_API, () => {
console.log(`> Ready on ${process.env.URL_API}`);
});
With:
http.listen(process.env.PORT_API, () => {
logger.info(`> Ready on ${dev ? process.env.URL_API : process.env.PRODUCTION_URL_API}`);
});
Open file book/10-begin/api/server/google-auth.ts
and make following updates.
Replace:
passport.use(
new Strategy(
{
clientID: process.env.GOOGLE_CLIENTID,
clientSecret: process.env.GOOGLE_CLIENTSECRET,
callbackURL: `${process.env.URL_API}/oauth2callback`,
passReqToCallback: true,
},
verify,
),
);
With:
passport.use(
new Strategy(
{
clientID: process.env.GOOGLE_CLIENTID,
clientSecret: process.env.GOOGLE_CLIENTSECRET,
callbackURL: `${dev ? process.env.URL_API : process.env.PRODUCTION_URL_API}/oauth2callback`,
passReqToCallback: true,
},
verify,
),
);
Please remember to define dev
under import section:
const dev = process.env.NODE_ENV !== 'production';
In Chapter 5 you added two values to Authorized JavaScript origins
at Google Cloud Platform dashboard. Make sure that you added your actual value for production URL of API
server.
Replace
res.redirect(`${process.env.URL_APP}${redirectUrlAfterLogin}`);
With:
res.redirect(
`${dev ? process.env.URL_APP : process.env.PRODUCTION_URL_API}${redirectUrlAfterLogin}`,
);
Open file book/10-begin/api/server/passwordless-auth.ts
and make following updates.
Replace:
loginURL: `${
process.env.URL_API
}/auth/logged_in?token=${tokenToSend}&uid=${encodeURIComponent(uidToSend)}`,
With:
loginURL: `${
dev ? process.env.URL_API : process.env.PRODUCTION_URL_API
}/auth/logged_in?token=${tokenToSend}&uid=${encodeURIComponent(uidToSend)}`,
Replace:
res.redirect(`${process.env.URL_APP}${redirectUrlAfterLogin}`);
With:
res.redirect(
`${dev ? process.env.URL_APP : process.env.PRODUCTION_URL_APP}${redirectUrlAfterLogin}`,
);
Replace:
res.redirect(`${process.env.URL_APP}/login`);
You've reached the end of the Chapter 10 preview. To continue reading, you will need to purchase the book.
We keep our book up to date with recent libraries and packages.