Builder Book logo

Book: SaaS Boilerplate

  1. Introduction. Project structure.
  2. GitHub. VS Code Editor. Node. Yarn. TypeScript. TSLint. Next.js. Environmental variables.
  3. Material-UI. Theme. Dark theme. Shared layout. Shared styles. Shared components. Mobile browser.
  4. HTTP. APP server. Next-Express server. Fetch method. API methods. async/await. API server. Express server. Environmental variables. Logs.
  5. User model. Mongoose and MongoDB. MongoDB index. Jest testing. Your Settings page. File upload to AWS S3.
  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. Data store for User. Toggle theme API. Team. Invitation.
  9. Discussion API. Post API. Websockets.
  10. Stripe API and paid subscription. Email notification for new post. AWS Lambda. AWS API Gateway.
  11. Environmental variables, production/development. Logger. API server. Server-side caching. SEO - robots.txt, sitemap.xml. Server-side caching. Heroku. AWS Elastic Beanstalk.

Chapter 10: Environmental variables, production/development. Logger. API server. Server-side caching. SEO - robots.txt, sitemap.xml. Server-side caching. Heroku. AWS Elastic Beanstalk.

The price will become $249 at midnight of December 31, 2020.


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.

link Environmental variables, production/development

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, 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, {
    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.builderbook.org 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:

server.use(cors({ origin: process.env.URL_APP, credentials: true }));

With:

server.use(
cors({ origin: dev ? process.env.URL_APP : process.env.PRODUCTION_URL_APP, credentials: true }),
);

Replace:

setupSockets({ http, origin: process.env.URL_APP, sessionMiddleware });

With:

setupSockets({
    http,
    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`);

With:

res.redirect(`${dev ? process.env.URL_APP : process.env.PRODUCTION_URL_APP}/login`);

Define dev in this location, so it has enough scope to be available in the above three locations:

const mongoStore = new PasswordlessMongoStore();

const dev = process.env.NODE_ENV !== 'production';

You've reached the end of the Chapter 10 preview. To continue reading, you will need to purchase the book.

The price will become $249 at midnight of December 31, 2020.


format_list_bulleted
help_outline
lens