async-labs

Saas

Build your own SaaS business with SaaS boilerplate. Productive stack: React, Material-UI, Next, MobX, WebSockets, Express, Node, Mongoose, MongoDB. Written with TypeScript.
Under MIT License
By async-labs

mongodb typescript react nextjs aws-s3 aws-ses mongoose express material-ui boilerplate google-api saas passport mobx mailchimp saas-boilerplate saas-business saas-product

SaaS Boilerplate

Open source web app that saves you many days of work when building your own SaaS product. The boilerplate comes with many basic SaaS features (see Features below) so that you can focus on features that differentiate your product.


If you want to learn how to build this project from scratch, check out our book: https://builderbook.org/book


The open source project is located in the saas folder. If you purchased our book, codebases for each of the book's chapters are located in the book folder.


Live demo:

Showcase

Check out projects built with the help of this open source app. Feel free to add your own project by creating a pull request.



Contents

Features

Running api locally:

api/.env:


```
# Used in api/server/server.ts
MONGO_URL_TEST=
MONGO_URL=
SESSION_NAME=
SESSION_SECRET=
COOKIE_DOMAIN=


# Used in api/server/google.ts
GOOGLE_CLIENTID=
GOOGLE_CLIENTSECRET=


# Used in api/server/aws-s3.ts and api/server/aws-ses.ts
AWS_REGION=
AWS_ACCESSKEYID=
AWS_SECRETACCESSKEY=


# Used in api/server/models/Invitation.ts and api/server/models/User.ts
EMAIL_SUPPORT_FROM_ADDRESS=


# Used in api/server/mailchimp.ts
MAILCHIMP_API_KEY=
MAILCHIMP_REGION=
MAILCHIMP_SAAS_ALL_LIST_ID=


# All env variables above this line are needed for successful user signup


# Used in api/server/stripe.ts
STRIPE_TEST_SECRETKEY=sk_test_xxxxxx
STRIPE_LIVE_SECRETKEY=sk_live_xxxxxx


STRIPE_TEST_PLANID=plan_xxxxxx
STRIPE_LIVE_PLANID=plan_xxxxxx


STRIPE_LIVE_ENDPOINTSECRET=whsec_xxxxxx


# Optionally determine the URL
URL_APP=http://localhost:3000
URL_API=http://localhost:8000
PRODUCTION_URL_API=
PRODUCTION_URL_APP=


# in pages/_document.tsx and lib/withAuth.tsx
GA_MEASUREMENT_ID=
```


Important: The above environmental variables are available on the server only. You should add your .env file to .gitignore inside the api folder so that your secret keys are not stored on a remote Github repo.



Running app locally:

```
STRIPE_TEST_PUBLISHABLEKEY=pk_test_xxxxxxxxxxxxxxx
STRIPE_LIVE_PUBLISHABLEKEY=pk_live_xxxxxxxxxxxxxxx


BUCKET_FOR_POSTS=
BUCKET_FOR_TEAM_AVATARS=
BUCKET_FOR_TEAM_LOGOS=

URL_APP=http://localhost:3000
URL_API=http://localhost:8000
PRODUCTION_URL_API=
PRODUCTION_URL_APP=

API_GATEWAY_ENDPOINT=
GA_MEASUREMENT_ID=


```



[
{
"AllowedHeaders":[
"*"
],
"AllowedMethods":[
"PUT",
"POST",
"GET",
"HEAD",
"DELETE"
],
"AllowedOrigins":[
"http://localhost:3000",
"https://saas-app.async-await.com"
],
"ExposeHeaders":[
"ETag",
"x-amz-meta-custom-header"
]
}
]



Deploy with Heroku

To deploy the two apps (api and app), you can follow these instructions to deploy each app individually to Heroku:


https://github.com/async-labs/builderbook/blob/master/README.md#deploy-to-heroku


You are welcome to deploy to any cloud provider. Eventually, we will publish a tutorial for AWS Elastic Beanstalk.


If you need help deploying your SaaS Boilerplate app, or variation of it, you can hire us. Email us for more details: [email protected]


Built with

For more detail, check package.json files in both app and api folders and project's root.


To customize styles, check this guide.


Screenshots

Google or passwordless login:


Dropdown menu for settings:


Personal settings:


Team settings:


Creating a Discussion:


Writing a Post, Markdown vs. HTML view:



Discussion between team members:


Billing settings:


Purchasing a subscription:


Payment history:


Contributing

Want to support this project? Sign up at async and/or buy our book, which teaches you how to build this project from scratch.


Sponsors

Team

You can contact us at [email protected]


License

All code in this repository is provided under the MIT License.


Project structure

├── .elasticbeanstalk
│ └── config.yml
├── .github
│ └── FUNDING.yml
├── .vscode
│ ├── extensions.json
│ ├── launch.json
│ └── settings.json
├── api
│ ├── .elasticbeanstalk
│ │ └── config.yml
│ ├── server
│ │ ├── api
│ │ │ ├── index.ts
│ │ │ ├── public.ts
│ │ │ ├── team-leader.ts
│ │ │ └── team-member.ts
│ │ ├── models
│ │ │ ├── Discussion.ts
│ │ │ ├── EmailTemplate.ts
│ │ │ ├── Invitation.ts
│ │ │ ├── Post.ts
│ │ │ ├── Team.ts
│ │ │ └── User.ts
│ │ ├── utils
│ │ │ ├── slugify.ts
│ │ │ └── sum.ts
│ │ ├── aws-s3.ts
│ │ ├── aws-ses.ts
│ │ ├── google-auth.ts
│ │ ├── logger.ts
│ │ ├── mailchimp.ts
│ │ ├── passwordless-auth.ts
│ │ ├── passwordless-token-mongostore.ts
│ │ ├── server.ts
│ │ ├── sockets.ts
│ │ └── stripe.ts
│ ├── static
│ │ └── robots.txt
│ ├── test/server/utils
│ │ ├── slugify.test.ts
│ │ └── sum.test.ts
│ ├── .eslintignore
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── package.json
│ ├── tsconfig.json
│ ├── tsconfig.server.json
│ └── yarn.lock
├── app
│ ├── .elasticbeanstalk
│ │ └── config.yml
│ ├── components
│ │ ├── common
│ │ │ ├── Confirmer.tsx
│ │ │ ├── LoginButton.tsx
│ │ │ ├── MemberChooser.tsx
│ │ │ ├── MenuWithLinks.tsx
│ │ │ ├── MenuWithMenuItems.tsx
│ │ │ └── Notifier.tsx
│ │ ├── discussions
│ │ │ ├── CreateDiscussionForm.tsx
│ │ │ ├── DiscussionActionMenu.tsx
│ │ │ ├── DiscussionList.tsx
│ │ │ ├── DiscussionListItem.tsx
│ │ │ └── EditDiscussionForm.tsx
│ │ ├── layout
│ │ │ ├── index.tsx
│ │ ├── posts
│ │ │ ├── PostContent.tsx
│ │ │ ├── PostDetail.tsx
│ │ │ ├── PostEditor.tsx
│ │ │ └── PostForm.tsx
│ │ ├── teams
│ │ │ └── InviteMember.tsx
│ ├── lib
│ │ ├── api
│ │ │ ├── makeQueryString.ts
│ │ │ ├── public.ts
│ │ │ ├── sendRequestAndGetResponse.ts
│ │ │ ├── team-leader.ts
│ │ │ └── team-member.ts
│ │ ├── store
│ │ │ ├── discussion.ts
│ │ │ ├── index.ts
│ │ │ ├── invitation.ts
│ │ │ ├── post.ts
│ │ │ ├── team.ts
│ │ │ └── user.ts
│ │ ├── confirm.ts
│ │ ├── isMobile.ts
│ │ ├── notify.ts
│ │ ├── resizeImage.ts
│ │ ├── sharedStyles.ts
│ │ ├── theme.ts
│ │ └── withAuth.tsx
│ ├── pages
│ │ ├── _app.tsx
│ │ ├── _document.tsx
│ │ ├── billing.tsx
│ │ ├── create-team.tsx
│ │ ├── discussion.tsx
│ │ ├── invitation.tsx
│ │ ├── login-cached.tsx
│ │ ├── login.tsx
│ │ ├── team-settings.tsx
│ │ └── your-settings.tsx
│ ├── public
│ │ └── pepe.jpg
│ ├── server
│ │ ├── robots.txt
│ │ ├── routesWithCache.ts
│ │ ├── server.ts
│ │ └── setupSitemapAndRobots.ts
│ ├── .babelrc
│ ├── .eslintignore
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── next.env.d.ts
│ ├── next.config.js
│ ├── package.json
│ ├── tsconfig.json
│ ├── tsconfig.server.json
│ └── yarn.lock
├── book
├── lambda
│ ├── .estlintignore
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── api
│ ├── handler.ts
│ ├── package.json
│ ├── serverless.yml
│ ├── tsconfig.json
│ └── yarn.lock
├── .gitignore
├── LICENSE.md
├── README.md
├── package.json
├── yarn.lock