:point_up: A Newer Version… |
---|
If this interests you, check out this newer version, built with SvelteKit2, Svelte5, and SQLite - |
https://github.com/ClaytonFarr/justship |
A SaaS starter boilerplate with elegant UX to use as reference or point to build upon.
The focus here was to create a serverless app that utilizes modern tooling, infrastructure, and is centralized to as few platforms/expertise as possible.
There are a few remaining baseline items you should considering adding, but hopefully this provides a solid head start if you're choosing to work with these tools or platforms.
There are a few areas I didn't had time to tie up in this repo, but would recommend looking into –
sessionExpired
flags in code for start of this)touchTime
and authExpires
in code, and methods to get and (re)save refresh token in Fauna for start of this)The current Netlify Adapter has 2 primary limitations:
context.clientContext
data. This contains information needed to interact with services like Netlify Identity for admin actions. Detailsrender
function, preventing specifically named endpoints/functions from being available post build. DetailsCurrent Workaround
context.clientContext
data, create a separate, custom serverless function that is copied post-build† into the final functions
directory:/src/additional_functions/delete-identity.js
(a function called explicitly by SK endpoints)/src/additional_functions/handle-subscription-change.js
(a webhook triggered by an external event [Stripe subscription update])functions
directory:/src/additional_functions/identity-signup.js
(function called automatically when a new user completes sign-up process)† The Netlify Adapter currently aggregate all endpoints (i.e. individual SvelteKit serverless functions) into a single serverless function called render
within your target 'functions' directory for Netlify. To add additional functions to this directory, package.json is configured to run a post-build script (that utilizes the cpy-cli
package) to copy items and npm-run-all
to execute multiple scripts when the 'build' script is called locally or on deploy to Netlify.
There's a lot of outdated and inaccurate information online when it comes to how to setup authentication correctly and securely. This can get even more confusing when you're trying to implement authentication in a static or serverless site.
The long and short of it is -
In this implementation that amounts to –
request.locals
(example)load
redirecting authorized / unauthorized visitors as needed (example); since this is done in client code it may be possible to circumvent (not certain if that applies to load functions or not), but at most they would see empty shells for authorized routes.This implementation utilizes Netlify's authentication service, Identity (built on their GoTrue library) to help limit the number of platforms in use and also leverage the benefits of using sibling services on the same platform.
Identity offers a couple benefits in how it can interact with serverless functions that are also deployed on Netlify, but this could be replaced with a different authentication service if wanted.
At the time of creating this, I wasn't able to get Identity's GoTrue Javascript library to play nice with SvelteKit / serverless since it had dependencies that required access to window
.
I created custom helper methods to access the same underlying GoTrue functionality in auth-api-methods.js. Most of these directly access the API endpoints for your Identity instance. A couple require admin privilege (e.g. deleting a user). This is provided by calling a custom function (within 'additional_functions) where Netlify grants an Identity admin token to access the final endpoint and complete the request.
In this project, I wanted to leverage the functionality of each platform to it's fullest before reaching for additional tooling / infrastructure, where possible.
For form inputs & client-side validation the platform of the browser itself offers a great deal of ability and control - via HTML5 inputs and the browser's Constraint validation API.
Within the form components set, there are several core components that mirror their HTML counterparts: Input, Textarea, Select, Button. Each of these includes a broad range of configuration options for their functionality and styling.
Additionally, there are several pre-built configurations of the core components for common, special uses: CheckboxGroup, RadioGroup, InputPassword, InputUrl, PrefixedInput.
Each of these inputs can implement standard and custom validation rules. 'Standard' validation is provided by the input's semantics (e.g 'type=email'). 'Custom' validation can be prescribed by adding custom validation regex (example) and/or prescribing a set of custom errors and/or warnings (example).
These hooks for prescribing validation rules also allow easily creating and enforcing validation across multiple inputs (example).
Lastly, the configurable Form component will by default intelligently check whether it's inputs are valid or not and updates it's action buttons accordingly.
The form, modal, and notifications components demo'd and included in this repo should be easily transferable to other projects. Copying and pasting these as discrete files, rather than installing as a package, allows you to tinker with and adjust them as needed for your specific needs.
The components have some interdependencies currently in this repo (e.g the modal component includes some form components), but these can be removed if wanted.
Inspect the components' exported variables and comments to see what configuration options are available and search for instances of the component across this code base to see additional examples of their use.
Tailwind has a robust range of colors available out of the box. In addition to these, it's helpful to also be able to use semantic colors (e.g. for actions, brand related content) and also still leverage Tailwind's utility classes.
This is done via a helper function (cssVarHslHelper
) and addition of color objects (brand
, action
) in tailwind.config.cjs.
These values these new colors are then defined via CSS variables in app.css.
Once defined they cane be used the same way you would use Tailwind's built in colors: e.g. bg-indigo-500
can be replaced with bg-brand-dark
, etc. (Note that the colors variants defined with as DEFAULT
in tailwind.config.js do can be called with the base name alone; e.g. bg-action
).
At time of publishing, current versions in use are -
If things don't work or project cannot build successfully, either
To implement a fully working copy of this repo, follow the steps below –
Create accounts if needed for -
Install & authenticate Netlify CLI
Clone repo locally & publish copy to own GitHub account
Within terminal & directory of local copy of repo:
$ ntl init
Enable & Configure Netlify Identity
$ ntl open:admin
/auth-email-templates/invited.html
/auth-email-templates/confirm.html
/auth-email-templates/password-recovery.html
/auth-email-templates/email-change.html
Install and authenticate Netlify Fauna DB addon
$ ntl addons:create fauna
$ ntl addons:auth fauna
$ ntl env:list
Configure Fauna database
/src/lib/apis/db-api-schema.gql
Configure Stripe products
Configure Stripe Customer Portal
Create Stripe subscription update webhook
customer.subscription.updated
Add Stripe keys as environment variables
$ ntl env:set STRIPE_DEFAULT_PRICE_PLAN your-product-API-ID
$ ntl env:set STRIPE_SECRET_KEY your-stripe-secret-key
$ ntl env:set STRIPE_UPDATES_WEBHOOK_SECRET your-webhook-signing-secret
$ ntl env:list
you should now see 6 variables for:Rebuild / deploy site
Create and test new user
To run site locally
$ npm i
$ ntl dev