├── .editorconfig ├── .github ├── dependabot.yml └── workflows │ └── ci.yml ├── .gitignore ├── .npmignore ├── .taprc ├── LICENSE ├── README.md ├── package.json ├── plugin.d.ts ├── plugin.js └── test ├── plugin.test.js └── types └── plugin.test-d.ts /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | indent_size = 2 8 | indent_style = space 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "npm" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "daily" 12 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: GitHub CI 2 | 3 | on: 4 | push: 5 | paths-ignore: 6 | - 'docs/**' 7 | - '*.md' 8 | pull_request: 9 | paths-ignore: 10 | - 'docs/**' 11 | - '*.md' 12 | 13 | jobs: 14 | linter: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v3 18 | - name: Use Node.js 19 | uses: actions/setup-node@v3 20 | with: 21 | node-version: 16 22 | - name: Install dependencies 23 | run: npm install 24 | - name: Lint code 25 | run: npm run lint 26 | 27 | test: 28 | needs: linter 29 | runs-on: ${{ matrix.os }} 30 | strategy: 31 | matrix: 32 | node-version: [14, 16, 17] 33 | os: [ubuntu-latest, windows-latest, macos-latest] 34 | steps: 35 | - uses: actions/checkout@v3 36 | - name: Setup Node.js 37 | uses: actions/setup-node@v3 38 | with: 39 | node-version: ${{ matrix.node-version }} 40 | - name: Install dependencies 41 | run: npm install --ignore-scripts 42 | - name: Run tests & generate coverage 43 | env: 44 | SUPABASE_API_KEY: ${{ secrets.SUPABASE_API_KEY }} 45 | SUPABASE_PROJECT_URL: ${{ secrets.SUPABASE_PROJECT_URL }} 46 | run: npm run test:ci 47 | - name: Coveralls Parallel 48 | uses: coverallsapp/github-action@master 49 | with: 50 | github-token: ${{ secrets.GITHUB_TOKEN }} 51 | parallel: true 52 | 53 | coverage: 54 | needs: test 55 | runs-on: ubuntu-latest 56 | steps: 57 | - name: Coveralls Finished 58 | uses: coverallsapp/github-action@master 59 | with: 60 | github-token: ${{ secrets.GITHUB_TOKEN }} 61 | parallel-finished: true 62 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | 63 | # 0x 64 | .__browserify_string_empty.js 65 | profile-* 66 | 67 | # JetBrains IntelliJ IDEA 68 | .idea/ 69 | *.iml 70 | 71 | # VS Code 72 | .vscode/ 73 | 74 | # lock files 75 | package-lock.json 76 | yarn.lock 77 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | 63 | # 0x 64 | .__browserify_string_empty.js 65 | profile-* 66 | 67 | # JetBrains IntelliJ IDEA 68 | .idea/ 69 | *.iml 70 | 71 | # VS Code 72 | .vscode/ 73 | 74 | # lock files 75 | package-lock.json 76 | yarn.lock 77 | 78 | .travis.yml 79 | .editorconfig 80 | .gitattributes 81 | .gitignore 82 | .github 83 | -------------------------------------------------------------------------------- /.taprc: -------------------------------------------------------------------------------- 1 | ts: false 2 | jsx: false 3 | flow: false 4 | coverage: true 5 | check-coverage: true 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 - 2022 Jean-Michel Coghe 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fastify-supabase 2 | 3 | [![NPM version](https://img.shields.io/npm/v/fastify-supabase.svg?style=flat)](https://www.npmjs.com/package/fastify-supabase) 4 | [![GitHub CI](https://github.com/coopflow/fastify-supabase/workflows/GitHub%20CI/badge.svg)](https://github.com/coopflow/fastify-supabase/actions?workflow=GitHub+CI) 5 | [![Coverage Status](https://coveralls.io/repos/github/coopflow/fastify-supabase/badge.svg?branch=main)](https://coveralls.io/github/coopflow/fastify-supabase?branch=main) 6 | [![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat)](http://standardjs.com/) 7 | 8 | [Supabase client](https://github.com/supabase/supabase-js) initialization and encapsulation in [fastify](https://github.com/fastify/fastify) framework. 9 | 10 | ## Install 11 | 12 | Install the package with: 13 | ```sh 14 | npm i fastify-supabase --save 15 | ``` 16 | 17 | 18 | ## Usage 19 | 20 | The package needs to be added to your project with `register` and you must at least configure your Supabase API key and your Supabase URL wich are available in your Supabase project settings then call the Supabase API and you are done. 21 | ```js 22 | const fastify = require('fastify')({ logger: true }) 23 | 24 | fastify.register(require('fastify-supabase'), { 25 | supabaseKey: 'public-anon-key', 26 | supabaseUrl: 'https://xyzcompany.supabase.co' 27 | }) 28 | 29 | fastify.get('/read', async (request, reply) => { 30 | const { supabase } = fastify 31 | 32 | const { data, error } = await supabase.from('cities').select() 33 | 34 | return { data, error } 35 | }) 36 | 37 | fastify.listen(3000, (err) => { 38 | if (err) { 39 | fastify.log.error(err) 40 | process.exit(1) 41 | } 42 | }) 43 | ``` 44 | 45 | ### Options 46 | 47 | * `supabaseKey` **[ required ]** ``: The unique Supabase Key which is supplied when you create a new project in your project dashboard. 48 | 49 | * `supabaseUrl` **[ required ]** ``: The unique Supabase URL which is supplied when you create a new project in your project dashboard. 50 | 51 | * `namespace` **[ optional ]** ``: Through this option `fastify-supabase` lets you define multiple Supabase singular instances (with different options parameters if you wish) that you can later use in your application. 52 | ```js 53 | const fastify = require('fastify')({ logger: true }) 54 | 55 | fastify.register(require('fastify-supabase'), { 56 | namespace: 'one', 57 | supabaseKey: 'public-anon-key-one', 58 | supabaseUrl: 'https://xyzcompanyprojectone.supabase.co' 59 | }) 60 | 61 | fastify.register(require('fastify-supabase'), { 62 | namespace: 'two', 63 | supabaseKey: 'public-anon-key-two', 64 | supabaseUrl: 'https://xyzcompanyprojecttwo.supabase.co' 65 | }) 66 | 67 | fastify.get('/fetch-from-one', async (request, reply) => { 68 | const { supabase } = fastify 69 | 70 | const { data, error } = await supabase.one.from('project_one_table').select() 71 | 72 | return { data, error } 73 | }) 74 | 75 | fastify.get('/fetch-from-two', async (request, reply) => { 76 | const { supabase } = fastify 77 | 78 | const { data, error } = await supabase.two.from('project_two_table').select() 79 | 80 | return { data, error } 81 | }) 82 | 83 | fastify.listen(3000, (err) => { 84 | if (err) { 85 | fastify.log.error(err) 86 | process.exit(1) 87 | } 88 | }) 89 | ``` 90 | 91 | * `schema` **[ optional ]** ``: The Postgres schema which your tables belong to. Must be on the list of exposed schemas in Supabase. Defaults to 'public'. 92 | 93 | * `headers` **[ optional ]** `<{ [key: string]: string }>`: Optional headers for initializing the client. 94 | 95 | * `autoRefreshToken` **[ optional ]** ``: Automatically refreshes the token for logged in users. 96 | 97 | * `persistSession` **[ optional ]** ``: Whether to persist a logged in session to storage. 98 | 99 | * `detectSessionInUrl` **[ optional ]** ``: Detect a session from the URL. Used for OAuth login callbacks. 100 | 101 | * `localStorage` **[ optional ]** ``: A storage provider. Used to store the logged in session. 102 | 103 | * `realtime` **[ optional ]** ``: Options passed to the realtime-js instance. 104 | 105 | *__Note for TypeScript users__: If you are a TypeScript user, take a look at [Supabase Generating Types documentation](https://supabase.io/docs/reference/javascript/generating-types).* 106 | 107 | ## Documentation 108 | 109 | See the [Supabase reference documentation](https://supabase.io/docs/reference/javascript/supabase-client). 110 | 111 | ## Testing 112 | 113 | - Create a test table in your [Supabase](https://app.supabase.io) project database with: 114 | ```SQL 115 | CREATE TABLE "public"."fastify_supabase_test" ( 116 | "id" uuid DEFAULT GEN_RANDOM_UUID() NOT NULL, 117 | "job" uuid NOT NULL, 118 | "name" character varying NOT NULL, 119 | "created_at" timestamptz DEFAULT CURRENT_TIMESTAMP NOT NULL, 120 | CONSTRAINT "fastify_supabase_test_id__pkey" PRIMARY KEY ("id") 121 | ) WITH (oids = false); 122 | ``` 123 | - Create a file named `.env` (at the root of this project) providing your `supabaseKey` and `supabaseUrl`: 124 | ```sh 125 | SUPABASE_API_KEY=public-anon-key-of-your-project 126 | SUPABASE_PROJECT_URL=https://xyzcompany.supabase.co 127 | ``` 128 | - Finally run tests with: 129 | ```sh 130 | npm run test 131 | ``` 132 | 133 | ## Acknowledgements 134 | 135 | - [Ruan Martinelli](https://ruanmartinelli.com/) for kindly transferring the ownership of the package name. 136 | - This project is kindly sponsored by [coopflow](https://www.coopflow.com). 137 | 138 | 139 | ## License 140 | 141 | Licensed under [MIT](https://github.com/coopflow/fastify-supabase/blob/main/LICENSE) 142 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fastify-supabase", 3 | "version": "1.2.1", 4 | "description": "Supabase client initialization and encapsulation in fastify framework", 5 | "main": "plugin.js", 6 | "types": "plugin.d.ts", 7 | "scripts": { 8 | "lint": "standard --verbose", 9 | "lint:fix": "standard --fix", 10 | "test": "npm run lint && npm run unit && npm run test:typescript", 11 | "test:ci": "npm run unit:coverage && npm run test:typescript", 12 | "test:typescript": "tsd", 13 | "unit": "tap -J test/plugin.test.js", 14 | "unit:coverage": "npm run unit -- --cov --coverage-report=lcovonly --no-browser", 15 | "unit:report": "npm run unit -- --cov --coverage-report=html", 16 | "unit:verbose": "npm run unit -- -Rspec" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "git+ssh://git@github.com/coopflow/fastify-supabase.git" 21 | }, 22 | "keywords": [ 23 | "fastify", 24 | "fastify-plugin", 25 | "fastify-supabase", 26 | "postgreSQL", 27 | "supabase" 28 | ], 29 | "author": "Jean-Michel Coghe ", 30 | "license": "MIT", 31 | "bugs": { 32 | "url": "https://github.com/coopflow/fastify-supabase/issues" 33 | }, 34 | "homepage": "https://github.com/coopflow/fastify-supabase#readme", 35 | "engines": { 36 | "node": ">=14" 37 | }, 38 | "dependencies": { 39 | "@supabase/supabase-js": "^1.30.7", 40 | "fastify-plugin": "^3.0.1" 41 | }, 42 | "devDependencies": { 43 | "coveralls": "^3.1.1", 44 | "dotenv": "^16.0.0", 45 | "fastify": "^3.27.2", 46 | "standard": "^16.0.4", 47 | "tap": "^15.1.6", 48 | "tsd": "^0.19.1", 49 | "typescript": "^4.6.2" 50 | }, 51 | "tsd": { 52 | "directory": "test/types" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /plugin.d.ts: -------------------------------------------------------------------------------- 1 | import { SupabaseClient, SupabaseClientOptions } from "@supabase/supabase-js"; 2 | import { FastifyPluginCallback } from "fastify"; 3 | 4 | export interface FastifySupabasePluginOptions { 5 | /** 6 | * fastify-supabase name of the instance 7 | */ 8 | namespace?: string; 9 | supabaseClientOptions?: SupabaseClientOptions; 10 | 11 | /** 12 | * The unique Supabase Key which is supplied when you create a new project in your project dashboard 13 | */ 14 | supabaseKey: string; 15 | 16 | /** 17 | * The unique Supabase URL which is supplied when you create a new project in your project dashboard 18 | */ 19 | supabaseUrl: string; 20 | } 21 | 22 | export interface FastifySupabaseNamedInstance { 23 | [namespace: string]: SupabaseClient; 24 | } 25 | 26 | export type FastifySupabase = FastifySupabaseNamedInstance & SupabaseClient; 27 | 28 | declare module "fastify" { 29 | interface FastifyInstance { 30 | supabase: FastifySupabase; 31 | } 32 | } 33 | 34 | export const FastifySupabasePlugin: FastifyPluginCallback; 35 | export default FastifySupabasePlugin; 36 | -------------------------------------------------------------------------------- /plugin.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const fp = require('fastify-plugin') 4 | const { createClient } = require('@supabase/supabase-js') 5 | 6 | function fastifySupabase (fastify, options, next) { 7 | const { namespace, supabaseKey, supabaseUrl, ...supabaseOptions } = options 8 | 9 | if (!supabaseKey) { 10 | return next(new Error('You must provide a Supabase API key')) 11 | } 12 | 13 | if (!supabaseUrl) { 14 | return next(new Error('You must provide a Supabase Project URL')) 15 | } 16 | 17 | const supabase = createClient(supabaseUrl, supabaseKey, supabaseOptions) 18 | 19 | if (namespace) { 20 | if (supabase[namespace]) { 21 | return next(new Error(`fastify-supabase '${namespace}' is a reserved keyword`)) 22 | } else if (!fastify.supabase) { 23 | fastify.decorate('supabase', Object.create(null)) 24 | } else if (Object.prototype.hasOwnProperty.call(fastify.supabase, namespace)) { 25 | return next(new Error(`Supabase client '${namespace}' instance name has already been registered`)) 26 | } 27 | 28 | fastify.supabase[namespace] = supabase 29 | } else { 30 | if (fastify.supabase) { 31 | return next(new Error('fastify-supabase has already been registered')) 32 | } else { 33 | fastify.decorate('supabase', supabase) 34 | } 35 | } 36 | 37 | next() 38 | } 39 | 40 | module.exports = fp(fastifySupabase, { 41 | fastify: '>=3.0.0', 42 | name: 'fastify-supabase' 43 | }) 44 | -------------------------------------------------------------------------------- /test/plugin.test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { randomUUID } = require('crypto') 4 | const { beforeEach, test } = require('tap') 5 | const Fastify = require('fastify') 6 | const fastifySupabase = require('../plugin') 7 | 8 | require('dotenv').config() 9 | 10 | const uuid = randomUUID() 11 | 12 | beforeEach(async () => { 13 | const fastify = Fastify() 14 | 15 | fastify.register(fastifySupabase, { 16 | supabaseKey: process.env.SUPABASE_API_KEY, 17 | supabaseUrl: process.env.SUPABASE_PROJECT_URL 18 | }) 19 | 20 | await fastify.ready() 21 | await fastify.supabase.from('fastify_supabase_test') 22 | .delete() 23 | .eq('job', uuid) 24 | await fastify.close() 25 | }) 26 | 27 | test('fastify.supabase namespace should exist', async (t) => { 28 | t.plan(1) 29 | 30 | const fastify = Fastify() 31 | t.teardown(fastify.close.bind(fastify)) 32 | 33 | await fastify.register(fastifySupabase, { 34 | supabaseKey: process.env.SUPABASE_API_KEY, 35 | supabaseUrl: process.env.SUPABASE_PROJECT_URL 36 | }) 37 | 38 | await fastify.ready() 39 | 40 | t.ok(fastify.supabase) 41 | }) 42 | 43 | test('fastify.supabase.test namespace should exist', async (t) => { 44 | t.plan(2) 45 | 46 | const fastify = Fastify() 47 | t.teardown(fastify.close.bind(fastify)) 48 | 49 | await fastify.register(fastifySupabase, { 50 | namespace: 'test', 51 | supabaseKey: process.env.SUPABASE_API_KEY, 52 | supabaseUrl: process.env.SUPABASE_PROJECT_URL 53 | }) 54 | 55 | await fastify.ready() 56 | 57 | t.ok(fastify.supabase) 58 | t.ok(fastify.supabase.test) 59 | }) 60 | 61 | test('fastify-supabase should be able to access Supabase functionalities when registered without a namespaced instance', async (t) => { 62 | t.plan(5) 63 | 64 | const fastify = Fastify() 65 | t.teardown(fastify.close.bind(fastify)) 66 | 67 | await fastify.register(fastifySupabase, { 68 | supabaseKey: process.env.SUPABASE_API_KEY, 69 | supabaseUrl: process.env.SUPABASE_PROJECT_URL 70 | }) 71 | 72 | await fastify.ready() 73 | 74 | t.ok(fastify.supabase) 75 | 76 | const { 77 | data: insertedData, 78 | error: insertError 79 | } = await fastify.supabase.from('fastify_supabase_test') 80 | .insert({ 81 | job: uuid, 82 | name: `01-coopflow:${uuid}` 83 | }) 84 | t.equal(insertError, null) 85 | t.equal(insertedData[0].name, `01-coopflow:${uuid}`) 86 | 87 | const { 88 | data: selectedData, 89 | error: selectError 90 | } = await fastify.supabase.from('fastify_supabase_test') 91 | .select('name') 92 | .eq('job', uuid) 93 | t.equal(selectError, null) 94 | t.equal(selectedData[0].name, `01-coopflow:${uuid}`) 95 | }) 96 | 97 | test('fastify-supabase should be able to access Supabase functionalities within multiple namespaced Supabase instance', async (t) => { 98 | t.plan(6) 99 | 100 | const fastify = Fastify() 101 | t.teardown(fastify.close.bind(fastify)) 102 | 103 | await fastify.register(fastifySupabase, { 104 | namespace: 'prod', 105 | supabaseKey: process.env.SUPABASE_API_KEY, 106 | supabaseUrl: process.env.SUPABASE_PROJECT_URL 107 | }) 108 | 109 | await fastify.register(fastifySupabase, { 110 | namespace: 'test', 111 | supabaseKey: process.env.SUPABASE_API_KEY, 112 | supabaseUrl: process.env.SUPABASE_PROJECT_URL 113 | }) 114 | 115 | await fastify.ready() 116 | 117 | t.ok(fastify.supabase) 118 | t.ok(fastify.supabase.test.auth) 119 | 120 | const { 121 | data: insertedData, 122 | error: insertError 123 | } = await fastify.supabase.prod.from('fastify_supabase_test') 124 | .insert({ 125 | job: uuid, 126 | name: `02-coopflow:${uuid}` 127 | }) 128 | t.equal(insertError, null) 129 | t.equal(insertedData[0].name, `02-coopflow:${uuid}`) 130 | 131 | const { 132 | data: selectedData, 133 | error: selectError 134 | } = await fastify.supabase.test.from('fastify_supabase_test') 135 | .select('name') 136 | .eq('job', uuid) 137 | t.equal(selectError, null) 138 | t.equal(selectedData[0].name, `02-coopflow:${uuid}`) 139 | }) 140 | 141 | test('fastify-supabase should throw if registered without an API key', async (t) => { 142 | t.plan(2) 143 | 144 | const fastify = Fastify() 145 | t.teardown(fastify.close.bind(fastify)) 146 | 147 | fastify.register(fastifySupabase, { supabaseUrl: process.env.SUPABASE_PROJECT_URL }) 148 | 149 | try { 150 | await fastify.ready() 151 | } catch (err) { 152 | t.ok(err) 153 | t.equal(err.message, 'You must provide a Supabase API key') 154 | } 155 | }) 156 | 157 | test('fastify-supabase should throw if registered without a Supabase project URL', async (t) => { 158 | t.plan(2) 159 | 160 | const fastify = Fastify() 161 | t.teardown(fastify.close.bind(fastify)) 162 | 163 | fastify.register(fastifySupabase, { supabaseKey: process.env.SUPABASE_API_KEY }) 164 | 165 | try { 166 | await fastify.ready() 167 | } catch (err) { 168 | t.ok(err) 169 | t.equal(err.message, 'You must provide a Supabase Project URL') 170 | } 171 | }) 172 | 173 | test('fastify-supabase should throw with duplicate instance names', async (t) => { 174 | t.plan(2) 175 | 176 | const fastify = Fastify() 177 | t.teardown(fastify.close.bind(fastify)) 178 | 179 | const namespace = 'test' 180 | 181 | fastify 182 | .register(fastifySupabase, { 183 | namespace, 184 | supabaseKey: process.env.SUPABASE_API_KEY, 185 | supabaseUrl: process.env.SUPABASE_PROJECT_URL 186 | }) 187 | .register(fastifySupabase, { 188 | namespace, 189 | supabaseKey: process.env.SUPABASE_API_KEY, 190 | supabaseUrl: process.env.SUPABASE_PROJECT_URL 191 | }) 192 | 193 | try { 194 | await fastify.ready() 195 | } catch (err) { 196 | t.ok(err) 197 | t.equal(err.message, `Supabase client '${namespace}' instance name has already been registered`) 198 | } 199 | }) 200 | 201 | test('fastify-supabase should throw when trying to register an instance with a reserved `namespace` keyword', async (t) => { 202 | t.plan(2) 203 | 204 | const fastify = Fastify() 205 | t.teardown(fastify.close.bind(fastify)) 206 | 207 | const namespace = 'auth' 208 | 209 | fastify.register(fastifySupabase, { 210 | namespace, 211 | supabaseKey: process.env.SUPABASE_API_KEY, 212 | supabaseUrl: process.env.SUPABASE_PROJECT_URL 213 | }) 214 | 215 | try { 216 | await fastify.ready() 217 | } catch (err) { 218 | t.ok(err) 219 | t.equal(err.message, `fastify-supabase '${namespace}' is a reserved keyword`) 220 | } 221 | }) 222 | 223 | test('fastify-supabase should throw when trying to register multiple instances without giving a name', async (t) => { 224 | t.plan(2) 225 | 226 | const fastify = Fastify() 227 | t.teardown(fastify.close.bind(fastify)) 228 | 229 | fastify 230 | .register(fastifySupabase, { 231 | supabaseKey: process.env.SUPABASE_API_KEY, 232 | supabaseUrl: process.env.SUPABASE_PROJECT_URL 233 | }) 234 | .register(fastifySupabase, { 235 | supabaseKey: process.env.SUPABASE_API_KEY, 236 | supabaseUrl: process.env.SUPABASE_PROJECT_URL 237 | }) 238 | 239 | try { 240 | await fastify.ready() 241 | } catch (err) { 242 | t.ok(err) 243 | t.equal(err.message, 'fastify-supabase has already been registered') 244 | } 245 | }) 246 | 247 | test('fastify-supabase should not throw if registered within different scopes (with and without namespaced instances)', (t) => { 248 | t.plan(2) 249 | 250 | const fastify = Fastify() 251 | t.teardown(fastify.close.bind(fastify)) 252 | 253 | fastify.register(function scopeOne (instance, opts, next) { 254 | instance.register(fastifySupabase, { 255 | supabaseKey: process.env.SUPABASE_API_KEY, 256 | supabaseUrl: process.env.SUPABASE_PROJECT_URL 257 | }) 258 | 259 | next() 260 | }) 261 | 262 | fastify.register(function scopeTwo (instance, opts, next) { 263 | instance.register(fastifySupabase, { 264 | namespace: 'test', 265 | supabaseKey: process.env.SUPABASE_API_KEY, 266 | supabaseUrl: process.env.SUPABASE_PROJECT_URL 267 | }) 268 | 269 | instance.register(fastifySupabase, { 270 | namespace: 'anotherTest', 271 | supabaseKey: process.env.SUPABASE_API_KEY, 272 | supabaseUrl: process.env.SUPABASE_PROJECT_URL 273 | }) 274 | 275 | next() 276 | }) 277 | 278 | fastify.ready((err) => { 279 | t.error(err) 280 | t.equal(err, undefined) 281 | }) 282 | }) 283 | 284 | test('fastify-supabase should be able to register multiple instances (with and without namespaced instances)', async (t) => { 285 | t.plan(2) 286 | 287 | const fastify = Fastify() 288 | t.teardown(fastify.close.bind(fastify)) 289 | 290 | await fastify.register(fastifySupabase, { 291 | supabaseKey: process.env.SUPABASE_API_KEY, 292 | supabaseUrl: process.env.SUPABASE_PROJECT_URL 293 | }) 294 | 295 | await fastify.register(fastifySupabase, { 296 | namespace: 'one', 297 | supabaseKey: process.env.SUPABASE_API_KEY, 298 | supabaseUrl: process.env.SUPABASE_PROJECT_URL 299 | }) 300 | 301 | await fastify.ready() 302 | 303 | t.ok(fastify.supabase) 304 | t.ok(fastify.supabase.one) 305 | }) 306 | -------------------------------------------------------------------------------- /test/types/plugin.test-d.ts: -------------------------------------------------------------------------------- 1 | import { RealtimeSubscription, SupabaseClient } from "@supabase/supabase-js" 2 | import { config } from "dotenv" 3 | import Fastify from "fastify" 4 | import { expectAssignable, expectError, expectType } from "tsd" 5 | import fastifySupabase, { 6 | FastifySupabase, 7 | FastifySupabaseNamedInstance 8 | } from "../../plugin" 9 | 10 | const { parsed: env } = config() 11 | 12 | if (!env) { 13 | throw new Error("No environment variables defined") 14 | } 15 | 16 | const app = Fastify(); 17 | 18 | app 19 | .register(fastifySupabase, { 20 | supabaseKey: env.SUPABASE_API_KEY, 21 | supabaseUrl: env.SUPABASE_PROJECT_URL, 22 | }) 23 | .register(fastifySupabase, { 24 | namespace: "one", 25 | supabaseKey: env.SUPABASE_API_KEY, 26 | supabaseUrl: env.SUPABASE_PROJECT_URL 27 | }) 28 | 29 | expectError(app.register(fastifySupabase, { 30 | namespace: "triggerTypescriptError", 31 | supabaseKey: env.SUPABASE_API_KEY, 32 | supabaseUrl: env.SUPABASE_PROJECT_URL, 33 | unknwonOption: 'this should trigger a typescript error' 34 | })) 35 | 36 | app.after(() => { 37 | expectAssignable(app.supabase) 38 | expectType(app.supabase) 39 | 40 | expectAssignable(app.supabase) 41 | expectType(app.supabase.one) 42 | expectType(app.supabase.one.getSubscriptions()) 43 | }) 44 | --------------------------------------------------------------------------------