The Remix Stack for deploying to Fly with Supabase, authentication, testing, linting, formatting, etc.

Overview

Remix Supa Fly Stack

The Remix Indie Stack

Learn more about Remix Stacks.

npx create-remix --template rphlmr/supa-fly-stack

What's in the stack

Not a fan of bits of the stack? Fork it, change it, and use npx create-remix --template your/repo! Make it your own.

Development

  • Download and run Docker Desktop

    Note: Needed to create a shadow database for prisma

    Note: Shadow database is local and run by docker-compose.yml

  • Create a Supabase Database (Free tiers gives you 2 databases)

    Note: Only one for playing around with Supabase or 2 for staging and production

    Note: Used all your free tiers ? Also works with Supabase CLI and local self-hosting

    Note: Create a strong database password, but prefer a passphrase, it'll be more easy to use in connection string (no need to escape special char)

    example : my_strong_passphrase

  • Go to https://app.supabase.io/project/{PROJECT}/settings/api to find your secrets

  • "Project API keys"

  • Add your SUPABASE_URL, SERVER_URL, SUPABASE_SERVICE_ROLE (aka service_role secret), SUPABASE_ANON_PUBLIC (aka anon public) and DATABASE_URL in the .env file

    Note: SERVER_URL is your localhost on dev. It'll work for magic link login

DATABASE_URL="postgres://postgres:{STAGING_POSTGRES_PASSWORD}@db.{STAGING_YOUR_INSTANCE_NAME}.supabase.co:5432/postgres"
SHADOW_DATABASE_URL="postgresql://postgres:postgres@localhost:12345/postgres"
SUPABASE_ANON_PUBLIC="{ANON_PUBLIC}"
SUPABASE_SERVICE_ROLE="{SERVICE_ROLE}"
SUPABASE_URL="https://{STAGING_YOUR_INSTANCE_NAME}.supabase.co"
SESSION_SECRET="super-duper-s3cret"
SERVER_URL="http://localhost:3000"
  • Start Shadow database (Docker needs some time at initial run)

    npm run shadow-db:setup
  • Wait until you see supabase-shadow_1 | PostgreSQL init process complete; ready for start up. in console output

  • You can now ctrl + c to kill process

  • Initial setup:

    npm run setup
  • Start dev server:

    npm run dev

This starts your app in development mode, rebuilding assets on file changes.

The database seed script creates a new user with some data you can use to get started:

Relevant code:

This is a pretty simple note-taking app, but it's a good example of how you can build a full stack app with Prisma, Supabase and Remix. The main functionality is creating users, logging in and out (handling access and refresh tokens + refresh on expire), and creating and deleting notes.

Deployment

Do what you know if you are a Fly.io expert.

This Remix Stack comes with two GitHub Actions that handle automatically deploying your app to production and staging environments.

Prior to your first deployment, you'll need to do a few things:

  • Install Fly

  • Sign up and log in to Fly

    fly auth signup

    Note: If you have more than one Fly account, ensure that you are signed into the same account in the Fly CLI as you are in the browser. In your terminal, run fly auth whoami and ensure the email matches the Fly account signed into the browser.

  • Create two apps on Fly, one for staging and one for production:

    fly create supa-fly-stack-template
    fly create supa-fly-stack-template-staging
    • Initialize Git.
    git init
  • Create a new GitHub Repository, and then add it as the remote for your project. Do not push your app yet!

    git remote add origin <ORIGIN_URL>
  • Add a FLY_API_TOKEN to your GitHub repo. To do this, go to your user settings on Fly and create a new token, then add it to your repo secrets with the name FLY_API_TOKEN.

  • Add a SESSION_SECRET, SUPABASE_URL, SUPABASE_SERVICE_ROLE,SUPABASE_ANON_PUBLIC, SERVER_URL and DATABASE_URL to your fly app secrets

    To do this you can run the following commands:

    # staging
    fly secrets set SESSION_SECRET=$(openssl rand -hex 32) --app supa-fly-stack-template-staging
    fly secrets set SUPABASE_URL="https://{YOUR_STAGING_INSTANCE_NAME}.supabase.co" --app supa-fly-stack-template-staging
    fly secrets set SUPABASE_SERVICE_ROLE="{STAGING_SUPABASE_SERVICE_ROLE}" --app supa-fly-stack-template-staging
    fly secrets set SUPABASE_ANON_PUBLIC="{STAGING_SUPABASE_ANON_PUBLIC}" --app supa-fly-stack-template-staging
    fly secrets set DATABASE_URL="postgres://postgres:{STAGING_POSTGRES_PASSWORD}@db.{STAGING_YOUR_INSTANCE_NAME}.supabase.co:5432/postgres" --app supa-fly-stack-template-staging
    fly secrets set SERVER_URL="https://{YOUR_STAGING_SERVEUR_URL}" --app supa-fly-stack-template-staging
    
    # production
    fly secrets set SESSION_SECRET=$(openssl rand -hex 32) --app supa-fly-stack-template
    fly secrets set SUPABASE_URL="https://{YOUR_INSTANCE_NAME}.supabase.co" --app supa-fly-stack-template
    fly secrets set SUPABASE_SERVICE_ROLE="{SUPABASE_SERVICE_ROLE}" --app supa-fly-stack-template
    fly secrets set SUPABASE_ANON_PUBLIC="{SUPABASE_ANON_PUBLIC}" --app supa-fly-stack-template
    fly secrets set DATABASE_URL="postgres://postgres:{POSTGRES_PASSWORD}@db.{YOUR_INSTANCE_NAME}.supabase.co:5432/postgres" --app supa-fly-stack-template
    fly secrets set SERVER_URL="https://{YOUR_STAGING_SERVEUR_URL}" --app supa-fly-stack-template

    If you don't have openssl installed, you can also use 1password to generate a random secret, just replace $(openssl rand -hex 32) with the generated secret.

Now that everything is set up you can commit and push your changes to your repo. Every commit to your main branch will trigger a deployment to your production environment, and every commit to your dev branch will trigger a deployment to your staging environment.

GitHub Actions

DISCLAIMER : Github actions ==> I'm not an expert about that. Read carefully before using it

We use GitHub Actions for continuous integration and deployment. Anything that gets into the main branch will be deployed to production after running tests/build/etc. Anything in the dev branch will be deployed to staging.

👉 You have to add some env secrets for cypress. 👈

Add a SESSION_SECRET, SUPABASE_URL, SUPABASE_SERVICE_ROLE,SUPABASE_ANON_PUBLIC, SERVER_URL and DATABASE_URL to your repo secrets

Testing

Cypress

We use Cypress for our End-to-End tests in this project. You'll find those in the cypress directory. As you make changes, add to an existing file or create a new file in the cypress/e2e directory to test your changes.

We use @testing-library/cypress for selecting elements on the page semantically.

To run these tests in development, complete your .env and run npm run test:e2e:dev which will start the dev server for the app as well as the Cypress client. Make sure the database is running in docker as described above.

We also have a utility to auto-delete the user at the end of your test. Just make sure to add this in each test file:

afterEach(() => {
  cy.cleanupUser();
});

That way, we can keep your test db clean and keep your tests isolated from one another.

Vitest

For lower level tests of utilities and individual components, we use vitest. We have DOM-specific assertion helpers via @testing-library/jest-dom.

Type Checking

This project uses TypeScript. It's recommended to get TypeScript set up for your editor to get a really great in-editor experience with type checking and auto-complete. To run type checking across the whole project, run npm run typecheck.

Linting

This project uses ESLint for linting. That is configured in .eslintrc.js.

Formatting

We use Prettier for auto-formatting in this project. It's recommended to install an editor plugin (like the VSCode Prettier plugin) to get auto-formatting on save. There's also a npm run format script you can run to format all files in the project.

CC BY-NC-SA 4.0


Comments
  • Question - JWT Token Expiring Error When Querying Supabase Directly

    Question - JWT Token Expiring Error When Querying Supabase Directly

    First off thank you for the application, I am using it as a reference for a solution I am creating.

    I am running into a problem where I am getting an error saying

    [1] { message: 'JWT expired', code: 'PGRST301', details: null, hint: null }
    

    when doing queries in my application. I am confused because the call to getAuthSession is returning a valid session but when get the supabase client

        const supabaseClient = supabase(authSession?.accessToken as string);
    

    and make a database call, it errors out

    question 
    opened by aaronksaunders 11
  • Support supabase-js v2 & future of this stack

    Support supabase-js v2 & future of this stack

    I'll start working on migrating this stack to supabase-js v2. It's still a release candidate so it'll not be merged to master until it's stable 😉 I think it's possible to create a remix project from a stack's branch name, so, you'll be able to test this in advance.

    Maybe some changes will happen on main to prepare for this upgrade.

    I plan to :

    • make a better readme
    • clean some messy things like rewriting RLS and real-time examples, extracting components, etc...
    • add some doc on how to delete what you don't want to start your new project

    No release date, doing my best 😅

    enhancement 
    opened by rphlmr 7
  • Possible issues with Cypress mocking?

    Possible issues with Cypress mocking?

    Hi again, not sure if I'm doing anything wrong but when I run the headless cypress test, the mocking doesn't seem to work and it calls my Supabase API. I can see the users and notes being created and cleaned up in my Supabase dashboard during the tests.

    And since it called the actual api, test cases were failing and I had to add this check to wait for the api finishes and route changed to make it pass. Screen Shot 2022-07-23 at 20 41 52 smoke tests -- should allow you to make a note (failed) smoke tests -- should allow you to register and login (failed)

    Steps to reproduce:

    1. npx create-remix --template rphlmr/supa-fly-stack
    2. Correct the .env file
    3. npm run setup
    4. npm run test:e2e:run
    opened by itsmetambui 7
  • Access token refresh not working

    Access token refresh not working

    The refreshAccessToken function calls into supabase-js using

    https://github.com/rphlmr/supa-fly-stack/blob/bd100e0754adae1922fab4ea6a3f412ea7bf1268/app/modules/auth/service.server.ts#L60-L63

    (note access_token: "") However, inside supabase we hit the code path

    /**
       * Sets the session data from the current session. If the current session is expired, setSession will take care of refreshing it to obtain a new session.
       * If the refresh token or access token in the current session is invalid, an error will be thrown.
       * @param currentSession The current session that minimally contains an access token and refresh token.
       */
      async setSession(currentSession: {
        access_token: string
        refresh_token: string
      }): Promise<AuthResponse> {
        try {
          if (!currentSession.access_token || !currentSession.refresh_token) {
            throw new AuthSessionMissingError()
          }
    
    

    So the code always errors (file can be found at node_modules/.pnpm/@[email protected]/node_modules/@supabase/gotrue-js/src/GoTrueClient.ts lines 626 and following)

    bug 
    opened by saschatimme 5
  • Debugging / Sourcemaps?

    Debugging / Sourcemaps?

    When debugging in vs code, breakpoints are shown in ./build/index.js and not in the original files. Is this expected / Is there a way to get full source map support?

    enhancement question 
    opened by Froskk 5
  • add security fix in oauth.callback

    add security fix in oauth.callback

    Change what happens in oauth.callback and verifyAuthSession Linked to https://github.com/rphlmr/supa-fly-stack/issues/45 from @Benjamin-Dobell

    What's new :

    • oauth.callback action no more trust authSession it receives from the client
    • oauth.callback (client side) only sends refresh_token found in onAuthStateChange
    • oauth.callback action uses authSession returned by refreshAccessToken, called with the refresh_token received
    • verifyAuthSession compares authSession user id and user id returned by getAuthAccountByAccessToken. It's only a last guard against session cookie secret leak
    opened by rphlmr 4
  • Feature : replace hand-crafted auth things with Supabase Remix's package ?

    Feature : replace hand-crafted auth things with Supabase Remix's package ?

    Hello there,

    Someone ask this and maybe it's a good idea to not scatter people with multiple ways to handle auth in Remix with Supabase.

    Maybe I should rewrite auth with https://supabase.com/docs/guides/auth/auth-helpers/remix.

    I don't want to replace Prisma 🥶 but only the Auth part.

    After that, it should be a breeze to replace Prisma with Supabase to query your database if this It what you need for your project.

    I'll probably do some abstraction to keep helpers like requireAuthSession and keep things simple to use in protected loaders/actions.

    Is It something you are interested in or do you prefer to be able to modify what you want with the actual auth implementation?

    opened by rphlmr 3
  • LICENSE file

    LICENSE file

    Just noticed CC BY-NC-SA 4.0 tacked on down the bottom of the README. A copyleft non-commercial license seems highly unusual for a template, however it's your project so absolutely at your discretion.

    That said, could you please add a LICENSE file so that Github prominently displays the license?

    opened by Benjamin-Dobell 3
  • Auth Oddities / Vulnerabilities

    Auth Oddities / Vulnerabilities

    There's a (minor?) security issue and a few oddities I noticed with the way auth is being handled.

    OAuth Callback Auth Session Vulnerability

    The action in oauth.callback.tsx accepts a form data encoded AuthSession from the client and trusts its contents. Consequently, you can submit a real email with a fake access token etc. and side effects will be triggered.

    If an account exists for the email, the server will commit (sign) the bogus auth session data and store it in a cookie used for future requests. If a user doesn't exist for the email, one will be created and we'll also end up with bogus auth session cookie as above.

    Whilst not great, this is fairly safe at present since requireAuthSession() is being used on endpoints, and it calls through to verifyAuthSession(), and consequently it'll catch the bad access token. That does assume APIs are calling through to verifyAuthSession(). However, that leads into another concern, which is admittedly more of a query.

    Why create our own auth session cookie at all?

    The Supabase JS library is already creating its own sb-access-token cookie which contains all the same data. The main difference is it's created/verifiable with the Supabase instance's JWT secret key. Thus, in order to validate the token we need do one of:

    1. Perform a round trip to Supabase as verifyAuthSession() does.
    2. Grab the JWT secret from the Supabase dashboard and add it as a server-only environment variable. Then we can then validate the JWTs directly on our server without the round trip.

    I guess the advantage of our own session cookie is that we can validate it ourselves on the server without needing to grab the Supabase JWT secret and save on a round trip i.e. we can trust its contents. However, we're not doing that, we are performing round trips via requireAuthSession(). Just as well because of the aforementioned OAuth Callback vulnerability. However, it does make the contents of the cookie redundant, since we can't trust the contents without verifying the access token, and in doing so, we're fetching the same data that's found in the cookie anyway.

    Email Addresses as Unique Keys

    Email addresses are being used as unique keys. This stack doesn't claim to support oauth beyond magic links, so it's fine. However, this approach becomes problematic when integrating social OAuth - which is what I was doing when I discovered all the above. Basically, users can change the email address associated with their OAuth provider. Say for example someone signs up via Github social auth whilst their email is [email protected]. If they proceed to change their primary email on Github to [email protected], when that user returns to a site built on this stack and attempts to authenticate, no account will be found.

    Again, the stack doesn't claim to support social auth, so it's by no means a bug. However, that part of the auth design could probably be shored up a bit to make it more robust/extensible.

    Thank you

    After me ranting about problems, just wanted to say thanks for all the hard work! Despite the above, this is my first time using Remix and this repo is, at the very least, super informative.

    security 
    opened by Benjamin-Dobell 3
  • Prisma 3.12.0 breaks seed.ts

    Prisma 3.12.0 breaks seed.ts

    Just a heads up I updated prisma and @prisma/client to 3.12.0 and began getting a

    An error occured while running the seed command:
    Error: Command was killed with SIGKILL (Forced termination): ts-node --require tsconfig-paths/register prisma/seed.ts
    

    I downgraded back to 3.11.1 and everything worked as expected.

    opened by sms-swe 3
  • V2

    V2

    Major changes :

    • Supabase JS SDK V2 😇
    • Folder structure
      • Previous module's mutations/queries are now in a service.server (collocate all the module's service in one place)
    • Auth session is reworked to align with folder structure changes
    • requireAuthSession no longer call Supabase Auth API by default to verify user access token (it's still safe, we signed our cookie)
      • requireAuthSession takes a new optional param called verify (boolean) to work as before 😉
    • No more needing to start a shadow database to create a Prisma migration (I don't know why maybe it's thanks to Supabase). It results in a huge time saving!
    opened by rphlmr 2
  • feat : add Supabase provider for Browser + RLS example

    feat : add Supabase provider for Browser + RLS example

    Add Supabase Provider to use Supabase client (with RLS support) in the browser. Add a /rls route example to illustrate. Add /refresh-session route to enable session refresh (triggered by the Provider on expires). Add doc in a code comment to explain how It works. Add a migration to create a RLS table, for demo purposes. Add a type Database to have typing completion (It's only to illustrate how it works with supabase cli)

    bug Not working 
    opened by rphlmr 12
Releases(v2.0.1)
  • v2.0.1(Jan 2, 2023)

  • v2.0.0(Oct 23, 2022)

    Major changes :

    • Supabase JS SDK V2 😇
    • Folder structure
      • Previous module's mutations/queries are now in a service.server (collocate all the module's service in one place)
    • Auth session is reworked to align with folder structure changes
    • requireAuthSession no longer call Supabase Auth API by default to verify user access token (it's still safe, we signed our cookie)
      • requireAuthSession takes a new optional param called verify (boolean) to work as before 😉
    • No more needing to start a shadow database to create a Prisma migration (I don't know why maybe it's thanks to Supabase). It results in a huge time saving!
    • Removed RLS example (view readme) and Realtime example (coming back is planned, but it's quite similar as V1)
    Source code(tar.gz)
    Source code(zip)
  • v1.0.0(Oct 21, 2022)

Owner
Raphaël Moreau
⚡️
Raphaël Moreau
The Remix Stack for deploying to Fly with PostgreSQL, authentication, testing, linting, formatting, etc.

The Remix Stack for deploying to Fly with PostgreSQL, authentication, testing, linting, formatting, etc.

Remix 677 Jan 2, 2023
The Remix Blog Stack for deploying to Fly with MDX, SQLite, testing, linting, formatting, etc.

Remix Speed Metal Stack Learn more about Remix Stacks. npx create-remix --template Girish21/speed-metal-stack Remix Blog ?? This blog starter template

Girish 141 Jan 2, 2023
The Remix Stack for deploying to AWS with DynamoDB, authentication, testing, linting, formatting, etc.

The Remix Stack for deploying to AWS with DynamoDB, authentication, testing, linting, formatting, etc.

Remix 311 Jan 1, 2023
Remix Stack for deploying to Vercel with remix-auth, Planetscale, Radix UI, TailwindCSS, formatting, linting etc. Written in Typescript.

Remix Synthwave Stack Learn more about Remix Stacks. npx create-remix --template ilangorajagopal/synthwave-stack What's in the stack Vercel deploymen

Ilango 56 Dec 25, 2022
The Remix Stack for Web2 apps and Web3 DApps with authentication with Magic, testing, linting, formatting, etc.

Remix French House Stack Learn more about Remix Stacks. npx create-remix --template janhesters/french-house-stack What's in the Stack? The French Hou

Jan Hesters 26 Dec 26, 2022
The Remix Stack for deploying to Vercel with testing, linting, formatting, structure and mock for 3rd party API integration.

Remix DnB Stack See it live: https://dnb-stack.vercel.app/ Learn more about Remix Stacks. npx create-remix --template robipop22/dnb-stack What's in th

Robert Pop 61 Dec 13, 2022
The Remix Stack with Clerk authentication, Supabase database, Chakra UI, testing, linting, and more.

Remix Bossa Nova Stack Learn more about Remix Stacks. What's in the stack User management with Clerk Database with Supabase Styling with Chakra UI Dep

Clerk 32 Jan 2, 2023
A testing focused Remix Stack, that integrates E2E & Unit testing with Playwright, Vitest, MSW and Testing Library. Driven by Prisma ORM. Deploys to Fly.io

Live Demo · Twitter A testing focused Remix Stack, that integrates E2E & Unit testing with Playwright, Vitest, MSW and Testing Library. Driven by Pris

Remix Stacks 18 Oct 31, 2022
An Open Source Remix template that integrates Stripe Subscriptions, Social Authentication, Testing and a few more features. PostgreSQL version. Deploys to Fly.io

Live Demo · Twitter An open source Remix Stack that integrates Stripe Subscriptions, Social Authentication, Testing and a few more features. PostgreSQ

xo 25 Dec 7, 2022
An Open Source Remix template that integrates Stripe Subscriptions, Social Authentication, Testing and a few more features. SQLite version. Deploys to Fly.io

Live Demo · Twitter An Open Source Remix template that integrates Stripe Subscriptions, Social Authentication, Testing and a few more features. SQLite

xo 135 Dec 31, 2022
Visual Studio Code extension for formatting and linting Django/Jinja HTML templates using djLint

Visual Studio Code extension for formatting and linting Django/Jinja HTML templates using djLint

Almaz 25 Dec 15, 2022
A string of four operations of the library, can solve the js digital calculation accuracy of scientific notation and formatting problems, support for thousands of decimal point formatting output operations

A string of four operations of the library, can solve the js digital calculation accuracy of scientific notation and formatting problems, support for thousands of decimal point formatting output operations

null 10 Apr 6, 2022
Tiny JavaScript library (1kB) by CurrencyRate.today, providing simple way and advanced number, money and currency formatting and removes all formatting/cruft and returns the raw float value.

Zero dependency tiny JavaScript library (1kB bytes) by CurrencyRate.today, providing simple way and advanced number, money and currency formatting and removes all formatting/cruft and returns the raw float value.

Yurii De 11 Nov 8, 2022
Grupprojekt för kurserna 'Javascript med Ramverk' och 'Agil Utveckling'

JavaScript-med-Ramverk-Laboration-3 Grupprojektet för kurserna Javascript med Ramverk och Agil Utveckling. Utvecklingsguide För information om hur utv

Svante Jonsson IT-Högskolan 3 May 18, 2022
Hemsida för personer i Sverige som kan och vill erbjuda boende till människor på flykt

Getting Started with Create React App This project was bootstrapped with Create React App. Available Scripts In the project directory, you can run: np

null 4 May 3, 2022
Kurs-repo för kursen Webbserver och Databaser

Webbserver och databaser This repository is meant for CME students to access exercises and codealongs that happen throughout the course. I hope you wi

null 14 Jan 3, 2023
Custom Remix stack using Clerk for authentication and full user management.

New Wave Stack Learn more about Remix Stacks. For more on our thoughts on the New Wave Stack check out our blog post. To view this template in deploym

Charles Wefso 11 Oct 11, 2022
Remix+EdgeDB+Tailwind+Fly.io=🖤

Remix Chop Suey Stack Forked from Supa Fly Stack. Learn more about Remix Stacks. Quickstart npx create-remix --template jkcorrea/remix-chop-suey-stack

Jake Correa 56 Dec 22, 2022
A solid create-remix app, that applies best practices into a clean, batteries included template. SQLite version. Deploys to Fly.io

Remix Barebones Stack npx create-remix --template dev-xo/barebones-stack A solid create-remix app, that follows community guidelines and applies best

Dev XO 97 Dec 30, 2022