/saas-kit

A reusable set of micro-services for a multi-tenant SAAS backend.

Primary LanguageGoMIT LicenseMIT

go SaaS kit

A reusable set of micro-services for a multi-tenant SaaS backend.

go SaaS kit

demo | introductory blog post | API playground

Services & features

A note on the frontend

This project is integrated with a compatible frontend built with Flutter (for web). It can be used with any frontend technology since the exposed services offer complete REST APIs.

Getting started

Environment

Choose your environment dev (default), test or prod:

export SAAS_KIT_ENV="test"

Create an environment file .env.dev or .env.test for the environments used, where you might want to override some env-specific variables.

Database

Create a local postgres instance or use the provided docker-compose.yml file to set it up:

docker network create saas-kit-net

docker volume create --name=saas-kit-pg

docker-compose --env-file .env.dev up

Initialize separate database schemas, one for each service (in production you have service-local databases with only one schema):

go run ./cmd/dev init

Migrate services:

go run ./cmd/auth migrate

go run ./cmd/event migrate

Run service(s)

Run single service

go run ./cmd/auth

Run all sevices by separate go routines (migrations are not applied!):

go run ./cmd/dev

Create some necessary data

Load fixtures from yaml file with a default instance:

go run ./cmd/auth fixture ./pkg/auth/fixtures/instances.yml

Create super user:

go run ./cmd/auth adduser -name=Simon -email=simon@smartnuance.com -password=f00bartest -instance=smartnuance.com

Interact with auth service

Use exposed endpoint to signup user (with no permissions to start with):

http PUT :8801/signup instance="smartnuance.com" name=Bob email=bob@smartnuance.com password=alice

Test login and save refresh/access tokens:

RES=$(http POST :8801/login email=simon@smartnuance.com password=f00bartest instance=smartnuance.com -v -b)

RT=$(echo $RES | jq -r '.refreshToken')

AT=$(echo $RES | jq -r '.accessToken')

Refresh token:

http -v POST :8801/refresh refreshToken=$RT

Revoke token:

http -v DELETE :8801/revoke/ Authorization:"Bearer $AT"

Revoke all tokens:

http -v DELETE :8801/revoke/all Authorization:"Bearer $AT"

For a while, I can still use the access token, for example to rerun the idem-potent revoke:

http -v DELETE :8801/revoke/ Authorization:"Bearer $AT"

But if we try to use the revoked refresh token in a refresh call, this will fail:

http -v POST :8801/refresh refreshToken=$RT

Interact with event service

Since no implicit switch from the super admin is allowed, we provide the role header to temporarily switch to the event organizer role:

http -v PUT :8802/workshop Authorization:"Bearer $AT" role:"event organizer" instance:"c5263570ono4ui8qfhgg" title=Bachata locationName=Ponto

Packages used

API framework:

Database interaction:

Token handling:

Logging:

Asset handling:

Environment & Building:

Testing:

Contribute

Development tools

All tools necessary for development like installing code generators are listed in tools.go. (a subset of those tools are installed in CI build step of Earthfile)

To run all generators recursively:

go generate ./...

This should also trigger the protobuf generation with protoc defined in go:generatein tools.go.

Migrate database

To interact with database, we use a schema first approach with sqlboiler. It generates type-safe code to interact with the DB.

To start from an empty database (and test down migrations):

go run ./cmd/auth migrate -down

Or if you messed the database up and want to start from scratch, you can reinit the database (data will be lost!):

go run ./cmd/dev deinit

go run ./cmd/dev init

Migrate services:

go run ./cmd/auth migrate

go run ./cmd/event migrate

When database is on newest version, we have to generated git-versioned DB models by

go generate ./pkg/auth/db.go

Build service(s)

To include build information we use the govvv utility:

govvv build -pkg github.com/smartnuance/saas-kit/pkg/auth -o ./bin/ ./cmd/auth

To create reproducable builds, you can use EARTHLY:

earthly --build-arg service=auth +build

With either way the resulting runnable is executed by:

./bin/auth

Deploy services

Again with EARTHLY, we can switch the target to deploy:

earthly --build-arg service=dev +publish

This will not yet push the created single-binary docker image.

You can try to run the docker image locally, using the host network (so the ports used by the services needs to be free):

docker run --rm --network host -v $PWD/test/data:/app/test/data ghcr.io/smartnuance/saas-kit:latest

Configure linter

We are using the configurable staticcheck linter.

staticcheck ./...

Check that your VScode workspace settings contain

"go.lintTool": "staticcheck"

Configure testing

If your IDE (like VScode) shows broken test output because it does not support colored output, add this to your workspace settings:

"go.testEnvVars": {
    "TESTDEEP_COLOR": "off"
},