Skip to content

dthyresson/supagraphql

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

45 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

supagraphql

supagraphql

supagraphql is an example GraphQL Server implemented using Supabase, GraphQL Helix, Envelop and Fastify.

This example relies on the Supabase sample Countries data being loaded into the countries table and row-level-security (RLS) to secure mutations.

You can try a demo at https://supagraphql-production.up.railway.app/graphql

Or, if you want your own:

Deploy on Railway

Deploy

Envelop and GraphQL-Helix

This example uses the following Envelop plugins:

  • useLogger to log GraphQL lifecycle activity
  • useExtendContext to inject info into context, such as the authenticatiojn access_token JWT
  • useGenericAuth implement a custom authentication flow that checks for the @auth directive on queries or mutations and a valid Supabase JWT. We'll use this to authenticate operations protected by RLS.
  • useMaskedErrors to prevent sensitive information from leaking in error message responses
  • useSchema to load your GraphQL schema
  • [enableInternalTracing] to inject timing traces for each phase

Setup

  1. Create new project on Supabase
  2. Create the Quick Start sample Countries data

Countries Quick Start

  1. Or, create the countries table and data using the sql/countries.sql script

  2. Since we want to secure the countries data, we'll use Row Level Security (RLS) and create the following policies so that everyone can read (SELECT), but only authenticated users can add (INSERT) or edit (UPDATE) -- and no one can delete:

--
-- Name: countries Enable access to all users; Type: POLICY; Schema: public; Owner: -
--

CREATE POLICY "Enable access to all users" ON public.countries FOR SELECT USING (true);

--
-- Name: countries Enable insert for authenticated users only; Type: POLICY; Schema: public; Owner: -
--

CREATE POLICY "Enable insert for authenticated users only" ON public.countries FOR INSERT WITH CHECK ((auth.role() = 'authenticated'::text));

--
-- Name: countries Enable update for users based auth; Type: POLICY; Schema: public; Owner: -
--

CREATE POLICY "Enable update for users based auth" ON public.countries FOR UPDATE USING ((auth.role() = 'authenticated'::text)) WITH CHECK ((auth.role() = 'authenticated'::text));

--
-- Name: countries; Type: ROW SECURITY; Schema: public; Owner: -
--

ALTER TABLE public.countries ENABLE ROW LEVEL SECURITY;

Note: these policies can be found in sql.countries_rls.sql

Running the GraphQL Server

  1. Install all dependencies from the root of the repo (using yarn)
  2. Configure .env with your Supabase client SUPABASE_URL, SUPABASE_KEY, SUPABASE_JWT_SECRET from the Supabase API settings
  3. cd into that folder,
  4. To generate types, run yarn gen.
  5. To start the server, run yarn start
  6. Open http://localhost:3000/graphql in your browser, and try to run:

query { hello }

Hello

You should get the response back:

{
  "data": {
    "hello": "Hello!"
  }
}

Build and serve

If you deploy this project, you'll need to compile the typescript and build into /dist:

  1. To build the typescripts into dist, run yarn build
  2. To serve when deploying, run yarn serve

Example Queries and Mutations

Queries can be performed by all users.

Mutations on countries are protected by Envelop's generic auth plugin that checks the Authorization header for a valid, unexpired and verified Bearer token. This is a JWT (JSON Web Token) set as the session, or returns as part of the Sign In.

Note: The SUPABASE_JWT_SECRET is needed to verify the JWT.

Get Countries

query COUNTRIES {
  countries {
    id
    name
    iso2
  }
}

Returns

Countries

Get Country

query GET_COUNTRY($id: Int!) {
  country(id: $id) {
    id
    name
    iso2
    continent
  }
}

Variables

{ "id": 234 }

Returns

Country

Update a Country

Note: This Mutation requires an authenticated user, ie a valid Bearer JWT in the Authorization header.

mutation UPDATE_COUNTRY($id: Int!, $input: UpdateCountryInput!) {
  updateCountry(id: $id, input: $input) {
    name
    iso2
    local_name
  }
}

Variables

{ "id": 1, "input": { "name": "London is a Country", "iso2": "LIAC" } }

Headers

{
  "Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNjM0MzQ0NTU1LCJzdWIiOiI0NWUyOWJiNS00NTA3LTQ0NTktOTFkNC03NDMxNDU0OGUzODkiLCJlbWFpbCI6ImR0aHlyZXNzb24rc2JnM0BnbWFpbC5jb20iLCJwaG9uZSI6IiIsImFwcF9tZXRhZGF0YSI6eyJwcm92aWRlciI6ImVtYWlsIn0sInVzZXJfbWV0YWRhdGEiOnt9LCJyb2xlIjoiYXV0aGVudGljYXRlZCJ9.f050nI-sCynXBQ3vSkacUFQsQgumClmFpM5PuKe6hek"
}

Create a Country

Note: This Mutation requires an authenticated user, ie a valid Bearer JWT in the Authorization header.

mutation ADD_COUNTRY($input: CreateCountryInput!) {
  createCountry(input: $input) {
    id
    name
    iso2
    continent
  }
}

Variables

{
  "input": {
    "name": "In a Big Country",
    "iso2": "BIG"
  }
}

Headers

{
  "Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNjM0MzQ0NTU1LCJzdWIiOiI0NWUyOWJiNS00NTA3LTQ0NTktOTFkNC03NDMxNDU0OGUzODkiLCJlbWFpbCI6ImR0aHlyZXNzb24rc2JnM0BnbWFpbC5jb20iLCJwaG9uZSI6IiIsImFwcF9tZXRhZGF0YSI6eyJwcm92aWRlciI6ImVtYWlsIn0sInVzZXJfbWV0YWRhdGEiOnt9LCJyb2xlIjoiYXV0aGVudGljYXRlZCJ9.f050nI-sCynXBQ3vSkacUFQsQgumClmFpM5PuKe6hek"
}

Delete a Country

Note: This Mutation requires an authenticated user, ie a valid Bearer JWT in the Authorization header.

mutation DELETE_COUNTRY($id: Int!) {
  deleteCountry(id: $id) {
    id
    name
    iso2
    continent
  }
}

Variables

{
  "id": 92
}

Headers

{
  "Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNjM0MzQ0NTU1LCJzdWIiOiI0NWUyOWJiNS00NTA3LTQ0NTktOTFkNC03NDMxNDU0OGUzODkiLCJlbWFpbCI6ImR0aHlyZXNzb24rc2JnM0BnbWFpbC5jb20iLCJwaG9uZSI6IiIsImFwcF9tZXRhZGF0YSI6eyJwcm92aWRlciI6ImVtYWlsIn0sInVzZXJfbWV0YWRhdGEiOnt9LCJyb2xlIjoiYXV0aGVudGljYXRlZCJ9.f050nI-sCynXBQ3vSkacUFQsQgumClmFpM5PuKe6hek"
}

Sign Up

mutation SIGNUP($email: String!, $password: String!) {
  signUp(email: $email, password: $password) {
    id
    email
  }
}

Variables

{ "email": "someone@example.com", "password": "12345678" }

Sign In

mutation SIGNIN($email: String!, $password: String!) {
  signIn(email: $email, password: $password) {
    id
    email
    access_token
  }
}

Variables

{ "email": "someone@example.com", "password": "12345678" }

GraphQL Code Generation

The command yarn gen

  • Generates types from the SDL in typedefs
  • Generates anintrospection schema in json format

You will want to regenerate types when modifying the schema (types or queries/mutations).

About

GraphQL server using Supabase, GraphQL Helix, Envelop and Fastify

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published