Navigation
LaunchPad Docs
Configure, run, and integrate with our reusable app backend
Introduction
You're reading the developer documentation for the pre-release version of the LaunchPad backend. This guide will walk you through many of the core things such as setting up your local environment, understanding the components, and integrating with external services. Our goal is to provide a clear and efficient pathway for you to fully understand what is happening under the hood and how to best use LaunchPad.
Important Note: LaunchPad is still in pre-release version 0.2.x. As such, not everything is documented yet, and the documentation (and features themselves) are subject to significantly change until the product stabilizes with the version 1.0 release. Use with care.
Local Development Setup
To get started with local development, follow these steps:
- Prerequisites:
- Go (ver. ≥1.23)
- Docker Desktop (ver. ≥4.37)
- Git
- postgresql: ≥17.5
- Clone the Repository:
git clone git@github.com:jubulah/launch-pad.git - Environment Variables:
Navigate into the cloned launch-pad directory and create an
.envfile in the root directory. Configure necessary environment variables. In the pre-release the there are two environment files, an.envfile which is already included in the repo, and a.env_git_ignoredAs the name implies, this second env file is not tracked by git. This is where you should store yourJWT_PRIVATE_KEYandSTRIPE_SECRET_KEYvalues.ENVIRONMENT="development" PORT=8080 APPLICATION_NAME=users-service-bin # DB_HOST=host.docker.internal # To Locally Run in Docker DB_HOST=localhost # To Locally Run Outside of Docker DB_PORT=5432 DB_USER=YOUR_DATABASE_USERNAME DB_PASSWORD=YOUR_SECURE_PASSWORD DB_NAME=user_db DB_TABLE_USERS=users # Logging LOG_LEVEL=DEBUG # Keys JWT_PUBLIC_KEY=YOUR_PUB_KEY_HERE STRIPE_PUBLISHABLE_KEY=YOUR_PUB_KEY_HERE -
git setup: Copy your user's global .gitconfig to the repo dir (
cp ~/.gitconfig ./[PATH_TO_REPO]). This file is .gitignore'ed so it won't get committed. This is so that go inside of Docker will use your github credentials for downloading private go modules. If your config does not already have these lines, add them to the bottom of the config:[url "ssh://git@github.com/"] insteadOf = https://github.com/ - Run Docker Containers:
In production, this runs inside of docker. If you'd like to run it the same way in your local system, you can do so with:
docker build --ssh default=/Users/YOUR_USERNAME_HERE/.ssh/id_ecdsa -t dkr-users-service . && docker run -p 8080:8080 --env-file .env_git_tracked --env-file .env_git_ignored --name dkr-users-service dkr-users-service - Bypass Docker:
For local development, you may want to bypass docker to speed up builds and/or limit bandwidth useage. In that case, update your env file use a local database host with
DB_HOST=localhostinstead ofDB_HOST=host.docker.internalfor use with docker. Source your enviroment variables into your current terminal session, install dependencies (go mod tidy) and then build with:go build -o users-service-bin && ./users-service-bin
The backend server should now be running on http://localhost:8080 (or your configured port).
Managing Seed Data
Seed data is provided for populating your local development database with initial or test data. This allows you to work with a realistic dataset without manually entering information.
Using Seed Data
To apply the default seed data, execute the following command from the project root:
bash ./scripts/db_init.sh
This command will populate the database with users, products, and other necessary entities as defined in the seed data scripts.
To wipe your database and start over, just re-run this same script.
To go back to a completely blank database (as though you'd never run the seed script), re-run this same script, but select n when
the script asks if it should reapply the User database initialization.
WARNING: We recommend that you do not allow this seed script file to be on any production server. It contains database DROP commands which will delete all your production data if a developer accidentally runs the script there.
Changing Seed Data
LaunchPad is currently in pre-release version 0.2.x. and doesn't yet support modifying the seed data.
You still might want to modify the data. The most likely things you'll want to change are the string labels for the
roles and organizations tables.
Unsupported Steps to Modify the Data (with a pre-release version):
-
Manually change the data in the seed file. This is currently located in
scripts/db_init_commands.sql. You can change this however you want, but note that you will cause yourself merge conflicts when updating to the newest version, and until release version 1.0, we do not guaranteed this file will not radically change (including being removed entirely). - Directly modify the data after populating it:
For example, to use a more education focused role system, run:
update roles set role = 'teacher' where role = 'user';andupdate roles set role = 'student' where role = 'subscriber';The IDs will remain the same, so you can change name strings for organizations and/or roles without breaking anything. Note that if you rerun the seed population script, it will undo all these changes.
Troubleshooting FAQs
postgres:
- installation: guide assumes you installed postgres with homebrew.
brew install postgresql@17 -
login: You can login to the db with:
psql -U USERNAME_HERE -d DATABASE_HEREIf you can't login, confirm postgres is running withbrew services list, if it's not running start it withbrew services start postgresql@17 - psql command: if the
psqlis not available, be sure that after installation, you added it to your user's pathway with aexport PATH="/opt/homebrew/opt/postgresql@17/bin:$PATH"line in your rc file (i.e..zshrc)
API Endpoints
The LaunchPad user service provides RESTful API endpoints for managing users, organizations, and integrations. All endpoints require JWT authentication unless otherwise specified.
Authentication
Most endpoints require a valid JWT token in the Authorization header. The token must have the appropriate permissions for the requested operation.
The middleware will allow your JWT to have almost any data shape, but it does require the following:
id,role.id,organization.id: This requires an id value at the root level for the user and a nested id value for organization and for role.permissions: This requires an array of permission strings. (Note: You can inject the perm strings before sending them to the middleware to keep them out of the token sent to the user.)exp/iat: These will be validated to confirm a proper issue time and that the token is not expired.
Example minimum acceptable token:
{
"exp": 1791842936,
"iat": 1738880336,
"permissions": [ "string", "string", "string" ],
"organization": { "id": "string" },
"role": { "id": "string" },
"id": "string"
}
Users API
Endpoints for managing user accounts and authentication.
| Method | Endpoint | Description | Permissions Required | Authentication |
|---|---|---|---|---|
| POST | /api/v1/user | Create a new user account | Users.Create | JWT Required |
| GET | /api/v1/user | Retrieve all users | Users.Read | JWT Required |
| GET | /api/v1/user/:userID | Retrieve user by ID | Users.Read | JWT Required |
| PATCH | /api/v1/user/:userID | Update user information | Users.Update | JWT Required |
| DELETE | /api/v1/user/:userID | Delete user account | Users.Delete | JWT Required |
| POST | /api/v1/user/login | User login (returns JWT token) | None | No Auth Required |
| POST | /api/v1/user/renew_token | Renew JWT token | Users.Read | JWT Required |
Organizations API
Endpoints for managing organization/company information.
| Method | Endpoint | Description | Permissions Required | Authentication |
|---|---|---|---|---|
| POST | /api/v1/org | Create a new organization | Orgs.Create | JWT Required |
| GET | /api/v1/org | Retrieve all organizations | Orgs.Read | JWT Required |
| GET | /api/v1/org/:orgID | Retrieve organization by ID | Orgs.Read | JWT Required |
| PATCH | /api/v1/org/:orgID | Update organization information | Orgs.Update | JWT Required |
| DELETE | /api/v1/org/:orgID | Delete organization | Orgs.Delete | JWT Required |
User Integrations API
Endpoints for managing external service integrations, particularly Stripe Connect accounts.
| Method | Endpoint | Description | Permissions Required | Authentication |
|---|---|---|---|---|
| GET | /api/v1/user_integration/status | Check if service is running | Users.Read | JWT Required |
| POST | /api/v1/user_integration/stripe/accounts | Create Stripe Connect account | Users.Read | JWT Required |
| POST | /api/v1/user_integration/stripe/:externalID/account_links | Generate Stripe onboarding link | Users.Read | JWT Required |
| POST | /api/v1/user_integration/stripe/webhook | Stripe webhook endpoint | None | No Auth Required |
API Response Format
All API responses follow a consistent format:
{
"success": true|false,
"message": "Response message",
"data": { ... },
"error": null
}
Error Handling
The API returns appropriate HTTP status codes and error messages:
- 200 OK: Request successful
- 201 Created: Resource created successfully
- 400 Bad Request: Invalid request data
- 401 Unauthorized: Authentication required or invalid
- 403 Forbidden: Insufficient permissions
- 404 Not Found: Resource not found
- 500 Internal Server Error: Server error
Database Schema
The LaunchPad user service uses a PostgreSQL database with the following table structure:
Users Table
The core users table stores user account information and authentication details.
| Column | Type | Nullable | Default | Description |
|---|---|---|---|---|
| id | uuid | NOT NULL | uuid_generate_v4() | Primary key |
| text | User email address (unique) | |||
| password | character varying(255) | NOT NULL | Hashed password | |
| name_first | character varying(255) | First name | ||
| name_last | character varying(255) | Last name | ||
| birthdate | text | Date of birth | ||
| address_line_1 | text | Primary address line | ||
| address_line_2 | text | Secondary address line | ||
| address_city | text | City | ||
| address_state | text | State/Province | ||
| address_postal_code | text | Postal/ZIP code | ||
| address_country | text | Country | ||
| organization_id | uuid | NOT NULL | Foreign key to organizations table | |
| role_id | uuid | NOT NULL | Foreign key to roles table | |
| status | integer | NOT NULL | User account status | |
| created_at | timestamp with time zone | CURRENT_TIMESTAMP | Record creation timestamp | |
| updated_at | timestamp with time zone | CURRENT_TIMESTAMP | Record update timestamp | |
| deleted_at | timestamp with time zone | Soft delete timestamp |
Indexes: Primary key on id, unique constraint on email
Foreign Keys: References organizations(id) and roles(id)
Organizations Table
Stores organization/company information that users belong to.
| Column | Type | Nullable | Default | Description |
|---|---|---|---|---|
| id | uuid | NOT NULL | uuid_generate_v4() | Primary key |
| org_name | text | Organization name | ||
| address_1 | text | Primary address line | ||
| address_2 | text | Secondary address line | ||
| city | text | City | ||
| state | text | State/Province | ||
| zip | text | Postal/ZIP code | ||
| phone_number | text | Contact phone number | ||
| created_at | timestamp with time zone | Record creation timestamp | ||
| updated_at | timestamp with time zone | Record update timestamp | ||
| deleted_at | timestamp with time zone | Soft delete timestamp |
Indexes: Primary key on id
Referenced by: users.organization_id foreign key
Roles Table
Defines user roles and permissions within the system.
| Column | Type | Nullable | Default | Description |
|---|---|---|---|---|
| id | uuid | NOT NULL | uuid_generate_v4() | Primary key |
| role | text | Role name/description | ||
| created_at | timestamp with time zone | Record creation timestamp | ||
| updated_at | timestamp with time zone | Record update timestamp | ||
| deleted_at | timestamp with time zone | Soft delete timestamp |
Indexes: Primary key on id
Referenced by: users.role_id foreign key
User Integrations Table
Stores external service integrations for users (e.g., Stripe accounts).
| Column | Type | Nullable | Default | Description |
|---|---|---|---|---|
| id | uuid | NOT NULL | uuid_generate_v4() | Primary key |
| user_id | uuid | NOT NULL | Foreign key to users table | |
| external_id | character varying(255) | External service identifier | ||
| integration_provider | character varying(255) | Integration service name | ||
| status | integer | NOT NULL | Integration status | |
| created_at | timestamp with time zone | CURRENT_TIMESTAMP | Record creation timestamp | |
| updated_at | timestamp with time zone | CURRENT_TIMESTAMP | Record update timestamp | |
| deleted_at | timestamp with time zone | Soft delete timestamp |
Indexes: Primary key on id
Foreign Keys: References users(id)
Database Relationships
- Users → Organizations: Many-to-one relationship via
organization_id - Users → Roles: Many-to-one relationship via
role_id - User Integrations → Users: Many-to-one relationship via
user_id
Note: All tables use soft deletes with deleted_at timestamps and include standard audit fields (created_at, updated_at).
Stripe Integration
LaunchPad supports letting your users create individual Express Stripe accounts which are linked as a subset of your centralized stripe account.
Onboarding Process
You must direct your users through a multi-step process.
- Optional: The more information you've collected in advance from your user, the more info that will be sent during their Stripe account creation. This is optional because Stripe will collect the any info you've left out, but by collecting it in advance, your users will only have to provide their details to once.
-
Create an Account:
/user_integration/stripe_createThe endpoint will allow you to create an account for your user inside of your own business stripe account. Stripe returns an ID value which is stored for your user asuser_integrations.external_id. -
Link an Account:
/user_integration/stripe_linkThis endpoint generates the URL you need to provide to your user to do complete the Stripe onboarding process. Your users will not be eligible to receive payments until they have completed this inside of Stripe's system. This URL expires relatively quickly, and can only be visited once. You will have to call/stripe_linkagain to generate a new URL if the user doesn't complete their Stripe onboarding in a timely manner.
Webhooks
LaunchPad natively supports receiving webhooks at /user_integration/stripe_connect_webhook. You must manually configure your stripe account to use this web hook in your Stripe Developer Dashboard.
There are over 270 individual event types that Stripe makes available to you via a webhook. LaunchPad supports the specific event types required to ensure you can manage your connected users and get them paid. You should add listeners for these specific events:
Account:
account.updated: Occurs whenever an account status or property has changed.account.application.deauthorized: Occurs whenever a user deauthorizes an application. Sent to the related application only.
Balance:
balance.available: Occurs whenever your Stripe balance has been updated (e.g., when a charge is available to be paid out).
By default, Stripe automatically transfers funds in your balance to your bank account on a daily basis. This event is not fired for negative transactions.
Capability:
capability.updated: Occurs whenever a capability has new requirements or a new status.
Charge:
charge.failed: Occurs whenever a failed charge attempt occurs.charge.refunded: Occurs whenever a charge is refunded, including partial refunds.charge.succeeded: Occurs whenever a charge is successful.charge.dispute.created: Occurs whenever a customer disputes a charge with their bank.
Payout:
payout.failed: Occurs whenever a payout attempt fails.payout.paid: Occurs whenever a payout is expected to be available in the destination account.
If the payout fails, apayout.failednotification is also sent, at a later time.
We integrate with Sripe's Go API v82.3.x. For additional information on Stripe processes and requirements see: Stripe Docs > Stripe-hosted Onboarding
Architecture Overview
Launch Independently of Your Existing Infra
You can use LaunchPad as a standalone addition to your existing apps.
Launch Everything Together / Mix Match
You can alternatively use the IaC provided with LaunchPad to create an AWS instance with everything you need to run
your own apps alongside LaunchPad. You can also have a combination of both your own cloud and a LaunchPad cloud.
Here's an example nginx config for routing traffic to LaunchPad.
This is one viable way, but you can route traffic into LaunchPad however you are currently handling your other apps.
# NGINX Config: LaunchPad
server {
server_name launchpad.YOUR_DOMAIN.com;
# You don't need root unless you're also hosting web files on this subdomain.
# root /SERVER_LOCATION/launchpad;
# index index.html;
location / {
proxy_pass http://localhost:8080;
}
# Use the HTTPS configuration appropriate for your setup
listen [::]:443 ssl;
listen 443 ssl;
# setup your ssl_certificate
# setup your ssl_certificate_key
# setup your ssl nginx conf
# setup your ssl_dhparam
}
# HTTP to HTTPS Redirection
server {
if ($host = launchpad.YOUR_DOMAIN.com) {
return 301 https://$host$request_uri;
}
listen 80;
listen [::]:80;
server_name launchpad.YOUR_DOMAIN.com;
return 404;
}
Infrastructure as Code (IaC)
Work In Progress: Not Yet Fully Documented
The IaC included with Jubulah's Launch Kit add-on currently contains two configuration options.
- terraform-mvp: This creates a production ready AWS setup.
- terraform-prototype: This is a less complex (and cheaper) setup that will work, but should only be used for prototyping.
terraform-mvp
The goal of terraform-mvp is to provide a modern, automated, and scalable solution to the launching MVP apps.
Est. Cloud Costs: $70 - $85 / month
- Elastic Cloud Compute (EC2): $6-11
- Virtual Private Cloud (VPC): $4
- Elastic IP for instance: $4
- Elastic IP for NAT Gateway: $4
- NAT Gateway: $33
- Relational Database Service (RDS): $15-25
- Tax: $4
. . . . . . . . . . . . . . . . . . . . . . . . . .
terraform-prototype
The goal of terraform-prototype is to provide a secure, low-cost, bare-bones server install for you to test your ideas. When your project is ready for launch, you should switch to terraform-mvp.
Est. Cloud Costs: $20 / month
- Elastic Cloud Compute (EC2): $10
- Elastic IP: $4.50
- Virtual Private Cloud (VPC): $4
- Tax: $1
Deployments
Work In Progress: Not Yet Fully Documented
This assumes you are running Intel/AMD 64 (X86). If running ARM, then use GOARCH=arm64 instead.
GOOS=linux GOARCH=amd64 go build -o launchpad-x86_64_binscpthat to your server- Run the binary from your server (in a session persistent manner)