Wait is an self-hostable CORS-enabled headless waitlist system that connects to Google Sheets. No database is needed.
Imagine you have a static landing page that you want to collect emails from the visitors who are interested. Wait is the perfect system for that.
Wait is headless and supports CORS. Your static page owns the UI completely and invokes fetch(..) to the Wait server from a different domain.
This means your static page doesn't need a backend and can be hosted for free anywhere e.g. Netlify, Github Pages. Since your static page owns the UI (not tunneling to an iframe), the styling and customization is extremely flexible.
Wait supports multiple groups. You can host one Wait server that serves multiple websites and landing pages.
This is the cheapest option for a waiting list system. You can get a ~$4/month VPS to host Wait and power many websites and landing pages at the same time.
Let's assume you host a Wait server at: waitserver.com
On your yournewproduct.com, you can have the following code that performs a cross-domain fetch(..) to the Wait server:
<form id="form" method="POST">
<input type="email" name="email" />
<button name="submitButton" type="submit">Join our waitlist</button>
</form>
<script>
var form = document.getElementById("waitlistJsonForm");
var emailInput = form.querySelector('[name="email"]');
form.addEventListener('submit', async (e) => {
e.preventDefault();
try {
const response = await fetch('/write', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({email: emailInput.value, group: 'test-wait-json-data'}),
});
if (response.status === 200) {
// Succeeded. Show the success message or perform any other action.
} else if (response.status === 400) {
const json = await response.json();
alert(json.error);
// Show the error message appropriately.
} else {
alert('Unknown error occurred. Please kindly open an issue at https://github.com/tanin47/wait');
}
} catch (error) {
console.log(error);
alert('Unknown error occurred. Please kindly open an issue at https://github.com/tanin47/wait');
}
});
</script>
Please see: src/resources/html/index.html for a working example.
Next, we want to set up a Google Sheet and run a Wait server!
You will need to set up a service account and a Google Sheet.
- Go to https://console.cloud.google.com/apis/library/sheets.googleapis.com and enable Google Sheets API.
- Go to https://console.cloud.google.com/apis/credentials and add a service account.
- Go into the service account. Go to the Keys tab. Create a JSON key.
- Wait for the JSON key file to finish being downloaded.
- Store the content of the JSON key file in the environment variable:
GOOGLE_SHEET_SERVICE_ACCOUNT_KEY_JSON. We'll later use it.
We've set up a service account successfully.
Next, we'll set up a Google Sheet.
- Go to https://docs.google.com/spreadsheets/u/0/ and create a new sheet.
- Share the sheet with the service account's email that you previously created. Ensure the service account is an editor.
- Extract the Google Sheet ID from the url:
https://docs.google.com/spreadsheets/d/<GOOGLE_SHEET_ID>/edit?gid=0#gid=0. Store the sheet ID in the env variable:GOOGLE_SHEET_ID. - Note the sheet name that you want to use in the Google Sheet doc. The default is
Sheet1. Store the sheet name in the env variable:GOOGLE_SHEET_NAME.
The output of this step is 3 environment variables:
GOOGLE_SHEET_SERVICE_ACCOUNT_KEY_JSONGOOGLE_SHEET_IDGOOGLE_SHEET_NAME
There are 2 ways to run Wait:
- Run as a standalone: Docker, Render.com, and JAR
- Embed your website into a larger system
Use Docker
The docker image is here: https://hub.docker.com/repository/docker/tanin47/wait
export GOOGLE_SHEET_SERVICE_ACCOUNT_KEY_JSON=`cat service_account_key.json`
export GOOGLE_SHEET_ID=<your_google_sheet_id>
export GOOGLE_SHEET_NAME=<your_google_sheet_name>
docker run -p 9090:9090 \
--pull always \
-e GOOGLE_SHEET_SERVICE_ACCOUNT_KEY_JSON \
-e GOOGLE_SHEET_ID \
-e GOOGLE_SHEET_NAME \
tanin47/wait:0.2.1
Use Render.com
Set the following environment variables:
- GOOGLE_SHEET_SERVICE_ACCOUNT_KEY_JSON
- GOOGLE_SHEET_ID
- GOOGLE_SHEET_NAME
The file render.yaml shows a blueprint example of how to run Wait on Render.
Run from the JAR file
First, you can download the wait-VERSION.jar file from
the Releases page.
Then, you can run the command below:
export GOOGLE_SHEET_SERVICE_ACCOUNT_KEY_JSON=`cat service_account_key.json`
export GOOGLE_SERVICE_ACCOUNT_SHEET_ID=<your_google_sheet_id>
export GOOGLE_SERVICE_ACCOUNT_SHEET_PRIVATE_KEY=<your_google_sheet_name>
java -jar wait-0.2.1.jar
Then, you can visit http://localhost:9090
If you are using JVM, you can embed Wait into your already running system. No need for a separate deployment.
- The larger system should include your fat JAR as a dependency by adding the below dependency:
<dependency>
<groupId>io.github.tanin47</groupId>
<artifactId>wait</artifactId>
<version>0.2.1</version>
</dependency>
- Instantiate the website with the port 9090 when the larger system initializes:
var main = new tanin.wait.WaitServer(
9090,
googleServiceAccountKeyJsonInString,
googleSheetId,
googleSheetName
);
main.start();- Visit http://localhost:9090 to confirm that the server is working.
- Run
./gradlew runto run the web server. - Visit http://localhost:9090
This flow has been set up as the Github Actions workflow: publish-jar.
EJWF is a template repository with collections of libraries and conventions. It's important that you understand each build process and are able to customize to your needs.
Here's how you can build your fat JAR:
- Set up
~/.jreleaser/config.tomlwithJRELEASER_MAVENCENTRAL_USERNAMEandJRELEASER_MAVENCENTRAL_PASSWORD - Run
./gradlew clean publish jreleaserDeploy. This step is IMPORTANT to clean out the previous versions.
This flow has been set up as a part of the Github Actions workflow: create-release-and-docker.
- Run
docker buildx build --platform linux/amd64,linux/arm64 -t wait:0.2.1 . - Test locally with:
docker run -p 9090:9090 --entrypoint "" wait:0.2.1 java -jar wait-0.2.1.jar -port 9090 - Run:
docker tag wait:0.2.1 tanin47/wait:0.2.1 - Run:
docker push tanin47/wait:0.2.1 - Go to Render.com, sync the blueprint, and test that it works
- Create an empty release with a new tag. The tag must follow the format:
vX.Y.Z. - Go to Actions and wait for the
create-release-and-docker(which is triggered automatically) workflow to finish. - Test the docker with
docker run -p 9090:9090 --entrypoint "" tanin47/wait:0.2.1 java -jar wait-0.2.1.jar -port 9090. - Go to Actions and trigger the workflow
publish-jaron the tagvX.Y.Zin order to publish the JAR to Central Sonatype.
