Builder Book logo

Book: SaaS Boilerplate

  1. Introduction. Project structure.
  2. Setup. GitHub and Git. Visual Studio code editor. Node, Yarn. package.json. TypeScript. ESLint, Prettier. Next.js. Server-side rendering. Project structure. Document HOC. App HOC. Index page. Testing. Environmental variables.
  3. Material-UI. Client-side and server-side rendered pages. Dark theme, CssBaseline. Shared layout. Adding styles. Shared components. MenuWithLinks. Notifier. Confirmer. Nprogress. Mobile browser.
  4. HTTP, request, response. APP project. Fetch method. API method at Index page. Next-Express server. Express route. Asynchronous function, Promise, async/await. API server. New project API. Updating APP.
  5. Infrastructure for User. MongoDB database. MongoDB index. Jest testing for TypeScript. Your Settings page. API infrastructure for uploading file.
  6. Login page. Session and cookie. Google OAuth API. Authentication HOC withAuth. firstGridItem logic in App HOC.
  7. AWS SES API. Passwordless OAuth API. Mailchimp API.
  8. Application state, App HOC, store and MobX. Toggle theme API. Team API. Invitation API.
  9. Discussion API. Post API. Websockets for Discussion and Post.
  10. Stripe API - API project. Stripe API - APP project. Setup at Stripe dashboard and environmental variables. Email notification for new post API - API project. Amazon API Gateway and AWS Lambda.
  11. 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.

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 regularly update the codebase with recent syntax and stable versions for libraries. The latest update was May 2021.


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, options);

With block:

mongoose.connect(dev ? process.env.MONGO_URL_TEST : process.env.MONGO_URL, options);

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, {
    useNewUrlParser: true,
    useCreateIndex: true,
    useFindAndModify: false,
    useUnifiedTopology: true,
});

With:

await mongoose.connect(dev ? process.env.MONGO_URL_TEST : process.env.MONGO_URL, {
    useNewUrlParser: true,
    useCreateIndex: true,
    useFindAndModify: false,
    useUnifiedTopology: true,
});

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, options);

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

To make these two env variables universally available in APP project, add them to book/10-begin/app/next.config.js file.

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`,
        },
        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`,
        },
        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 regularly update the codebase with recent syntax and stable versions for libraries. The latest update was May 2021.