Open source web app that saves you weeks of work when building your own SaaS product.
- The boilerplate app comes with many basic SaaS features (see Features below) so that you are able to focus on features that differentiate your product.
- We built this boilerplate for ourselves to focus more on what matters. We've used it to quickly launch async, builderbook, and other real-world SaaS web apps.
- Features
- Run locally
- Deploy
- Built with
- Screenshots
- Showcase
- Contributing
- Team
- License
- Project structure
- Server-side rendering for fast initial load and SEO.
- User authentication with Google, cookie, and session.
- Production-ready Express server with compression, parser, and helmet.
- Transactional emails (
AWS SES): welcome, team invitation, and payment. - Adding email addresses to newsletter lists (
Mailchimp): new users, paying users. - File upload, load, and deletion (
AWS S3) with pre-signed request for: Posts, Team Profile, and User Profile. - Team creation, Team Member invitation, and settings for Team and User.
- Opinionated architecture:
- keeping babel and webpack configurations under the hood,
- striving to minimize number of configurations,
withAuthHOC to pass user prop and control user access to pages,withLayoutHOC for shared layout and to pass additional data to pages,withStoreHOC, developer-friendly state management withMobX,- server-side rendering with
Material-UI, - model-specific components in addition to common components.
- Universally-available environmental variables at runtime.
- Server-side environmental variables managed with
dotenv. - Custom logger (configure what not to print in production).
- Useful components for any web app:
ActiveLink,AutoComplete,Confirm,Notifier,MenuWithLinks, and more. - Analytics with
Google Analytics. - Production-ready, scalable architecture:
app- user-facing web app with Next/Express server, responsible for rendering pages (either client-side or server-side).appsends requests via API methods and fetch toapiserver's Express routes.api- server-only web app with Express server, responsible for processing requests for internal and external APIs.- we prepared both apps for easy deployment to
nowby Zeit.
- Subscriptions with
Stripe:- subscribe/unsubscribe Team to plan,
- update card information,
- verified Stripe webhook for failed payment for subscription.
To run locally, you will need to run two apps: api and app.
-
Before running, create a
.envfile inside theapifolder with the environmental variables listed below.
This file must have values for therequiredvariables.
To use all features and third-party integrations, also add theoptionalvariables..env:# Used in api/server/app.ts MONGO_URL="xxxxxx" MONGO_URL_TEST="xxxxxx" SESSION_NAME="xxxxxx" SESSION_SECRET="xxxxxx" # Used in api/server/google.ts Google_clientID="xxxxxx" Google_clientSecret="xxxxxx" # Used in api/server/aws-s3.ts and api/server/aws-ses.ts Amazon_accessKeyId="xxxxxx" Amazon_secretAccessKey="xxxxxx" # Used in api/server/models/Invitation.ts and api/server/models/User.ts EMAIL_SUPPORT_FROM_ADDRESS="xxxxxx" # Used in api/server/mailchimp.ts MAILCHIMP_API_KEY="xxxxxx" MAILCHIMP_REGION="xxxx" MAILCHIMP_SAAS_ALL_LIST_ID="xxxxxx" # 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_PublishableKey="pk_test_xxxxxx" Stripe_Live_PublishableKey="pk_live_xxxxxx" Stripe_Test_PlanId="plan_xxxxxx" Stripe_Live_PlanId="plan_xxxxxx" Stripe_Live_EndpointSecret="whsec_xxxxxx" PRODUCTION_URL_APP="https://saas-app.async-await.com" PRODUCTION_URL_API="https://saas-api.async-await.com"Important: The above environmental variables are available on the server only. You should add your
.envfile to.gitignoreinside theapifolder so that your secret keys are not stored on a remote Github repo.- To get
MONGO_URLandMONGO_URL_TEST, we recommend a free MongoDB at mLab. - Specify your own name and secret keys for Express session: SESSION_NAME and SESSION_SECRET
- Get
Google_clientIDandGoogle_clientSecretby following the official OAuth tutorial.
Important: For Google OAuth app, callback URL is: http://localhost:8000/oauth2callback
Important: You have to enable Google+ API in your Google Cloud Platform account.
- To get
-
Once
.envis created, you can run theapiapp. Navigate to theapifolder, runyarnto add all packages, then run the command below:yarn dev
- Navigate to the
appfolder, runyarnto add all packages, then run the command below and navigate tohttp://localhost:3000:GA_TRACKING_ID=UA-xxxxxxxxx-x StripePublishableKey=pk_test_xxxxxxxxxxxxxxx BUCKET_FOR_POSTS=xxxxxx BUCKET_FOR_TEAM_AVATARS=xxxxxx yarn dev- To get
GA_TRACKING_ID, set up Google Analytics and follow these instructions to find your tracking ID. - To get
StripePublishableKey, go to your Stripe dashboard, clickDevelopers, then clickAPI keys.
- To get
As you can see, we don't have PRODUCTION_URL_APP and PRODUCTION_URL_API env variables in the above command. In dev environment, corresponding values arehttp://localhost:3000 and http://localhost:8000.
For successful file uploading, make sure your buckets have proper CORS configuration. Go to your AWS account, find your bucket, go to Permissions > CORS configuration, add:
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
<AllowedOrigin>http://localhost:3000</AllowedOrigin>
<AllowedOrigin>https://saas-app.async-await.com</AllowedOrigin>
<AllowedMethod>POST</AllowedMethod>
<AllowedMethod>GET</AllowedMethod>
<AllowedMethod>PUT</AllowedMethod>
<AllowedMethod>DELETE</AllowedMethod>
<AllowedMethod>HEAD</AllowedMethod>
<ExposeHeader>ETag</ExposeHeader>
<ExposeHeader>x-amz-meta-custom-header</ExposeHeader>
<AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>
Make sure to update allowed origin with your actual PRODUCTION_URL_APP. In our case, it's https://saas-app.async-await.com.
To deploy the two apps (api and app), follow the instructions below.
-
Inside the
apifolder, create anow.jsonfile with the following content:{ "env": { "NODE_ENV": "production" }, "dotenv": true, "alias": "your-api-url.com", "scale": { "sfo1": { "min": 1, "max": 1 } } }Remember to edit
now.jsonso it reflects your domain. -
Inside the
appfolder, create anow.jsonfile with the following content:{ "env": { "NODE_ENV": "production", "GA_TRACKING_ID": "UA-xxxxxxxxx-x", "StripePublishableKey": "pk_live_xxxxxx", "PRODUCTION_URL_APP": "https://your-app-url.com", "PRODUCTION_URL_API": "https://your-api-url.com", "BUCKET_FOR_POSTS": "xxxxxx", "BUCKET_FOR_TEAM_AVATARS": "xxxxxx", "LAMBDA_API_ENDPOINT": "xxxxxx", }, "alias": "your-app-url.com", "scale": { "sfo1": { "min": 1, "max": 1 } } }Remember to edit
now.jsonso it reflects yourGA_TRACKING_IDand domains. -
Follow these simple steps to deploy each app to
Nowcloud by Zeit.
Learn how to configure and scale your deployment: Now docs.
You are welcome to deploy to any cloud provider. We plan to publish a tutorial for AWS Elastic Beanstalk.
For more detail, check package.json files in both app and api folders.
To customize styles, check this guide.
Dashboard showing Discussion > Posts:
Adding a Post, Markdown vs. HTML view:
Settings for Personal Profile:
Menu dropdown to switch between Teams:
Check out projects built with the code in this open source app. Feel free to add your own project by creating a pull request.
- Async: asynchronous communication and project management tool for small teams of software engineers.
- Retaino by Earl Lee : Save, annotate, review, and share great web content. Receive smart email digests to retain key information.
- Builder Book: Open source web app to publish documentation or books. Built with React, Material-UI, Next, Express, Mongoose, MongoDB.
- Harbor: Open source web app that allows anyone with a Gmail account to automatically charge for advice sent via email.
If you'd like to contribute, check our todo list for features you can discuss and add. To report a bug, create an issue.
Want to support this project? Sign up at async and/or buy our book.
You can contact us at [email protected].
All code in this repository is provided under the MIT License.
├── server
│ ├── api
│ │ ├── index.ts
│ │ ├── public.ts
│ │ ├── team-leader.ts
│ │ ├── team-member.ts
│ ├── models
│ │ ├── Discussion.ts
│ │ ├── EmailTemplate.ts
│ │ ├── Invitation.ts
│ │ ├── Post.ts
│ │ ├── Purchase.ts
│ │ ├── Team.ts
│ │ ├── User.ts
│ ├── utils
│ │ ├── slugify.ts
│ ├── app.ts
│ ├── aws-s3.ts
│ ├── aws-ses.ts
│ ├── google.ts
│ ├── logs.ts
│ ├── mailchimp.ts
│ ├── stripe.ts
├── static
├── test/server/utils
├── .eslintrc.js
├── .gitignore
├── .npmignore
├── nodemon.js
├── package.json
├── tsconfig.json
├── yarn.lock
├── components
│ ├── common
│ │ ├── ActiveLink.tsx
│ │ ├── AutoComplete.tsx
│ │ ├── AvatarwithMenu.tsx
│ │ ├── Confirm.tsx
│ │ ├── Loading.tsx
│ │ ├── LoginButton.tsx
│ │ ├── MenuWithLinks.tsx
│ │ ├── MenuWithMenuItems.tsx
│ │ ├── Notifier.tsx
│ │ ├── SettingList.tsx
│ ├── discussions
│ │ ├── CreateDiscussionForm.tsx
│ │ ├── DiscussionActionMenu.tsx
│ │ ├── DiscussionList.tsx
│ │ ├── DiscussionListItem.tsx
│ │ ├── EditDiscussionForm.tsx
│ ├── posts
│ │ ├── PostContent.tsx
│ │ ├── PostDetail.tsx
│ │ ├── PostEditor.tsx
│ │ ├── PostForm.tsx
│ ├── teams
│ │ ├── InviteMember.tsx
│ ├── users
│ │ ├── MemberChooser.tsx
├── lib
│ ├── api
│ │ ├── getRootUrl.ts
│ │ ├── 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
│ ├── context.ts
│ ├── env.js
│ ├── gtag.js
│ ├── notifier.ts
│ ├── resizeImage.ts
│ ├── sharedStyles.ts
│ ├── withAuth.tsx
│ ├── withLayout.tsx
│ ├── withStore.tsx
├── pages
│ ├── settings
│ │ ├── team-billing.tsx
│ │ ├── team-members.tsx
│ │ ├── team-profile.tsx
│ │ ├── your-profile.tsx
│ ├── _document.tsx
│ ├── create-team.tsx
│ ├── discussion.tsx
│ ├── invitation.tsx
│ ├── login.tsx
├── server
│ ├── app.ts
│ ├── routesWithSlug.ts
├── static
│ ├── robots.txt
├── .babelrc
├── .eslintrc.js
├── .gitignore
├── .npmignore
├── next.config.js
├── nodemon.json
├── package.json
├── tsconfig.json
├── tsconfig.server.json
├── .tslint.json
├── yarn.lock