Skip to content

Commit

Permalink
feat: supa-4 nextjs supabase integration (#10)
Browse files Browse the repository at this point in the history
* feat: install supabase, add generate types script, add generated db types

* feat: implement supabase auth boilerplate and supabase sdks for runtimes. lint database.ts

* chore: move supabase folder into lib/services

* feat: spotify oauth

* chore: use env.mjs for env variables

* chore: remove unnecessary package
  • Loading branch information
okanisildar authored Dec 10, 2023
1 parent c970e26 commit b35d43d
Show file tree
Hide file tree
Showing 13 changed files with 642 additions and 8 deletions.
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
NEXT_PUBLIC_SUPABASE_URL=
NEXT_PUBLIC_SUPABASE_ANON_KEY=
19 changes: 19 additions & 0 deletions app/(auth)/auth/callback/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { NextRequest } from 'next/server';

import { createSupabaseServerClient } from '@/lib/services/supabase/server';
import { cookies } from 'next/headers';
import { NextResponse } from 'next/server';

export async function GET(request: NextRequest) {
const requestUrl = new URL(request.url);
const code = requestUrl.searchParams.get('code');
if (code) {
const cookieStore = cookies();
const supabase = createSupabaseServerClient(cookieStore);

await supabase.auth.exchangeCodeForSession(code);
}

// URL to redirect to after sign in process completes
return NextResponse.redirect(requestUrl.origin);
}
23 changes: 23 additions & 0 deletions app/(auth)/auth/sign-in/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import type { NextRequest } from 'next/server';

import { createSupabaseServerClient } from '@/lib/services/supabase/server';
import { cookies } from 'next/headers';
import { NextResponse } from 'next/server';

export async function POST(request: NextRequest) {
const supabase = createSupabaseServerClient(cookies());
const redirectURL = new URL('/auth/callback', request.url);

const scopes = 'user-read-email user-read-playback-position';
const result = await supabase.auth.signInWithOAuth({
options: {
redirectTo: redirectURL.toString(),
scopes,
},
provider: 'spotify',
});

return NextResponse.redirect(result.data.url!, {

Check warning on line 20 in app/(auth)/auth/sign-in/route.ts

View workflow job for this annotation

GitHub Actions / lint_and_typecheck

Forbidden non-null assertion
status: 301,
});
}
9 changes: 9 additions & 0 deletions app/(auth)/sign-in/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Button } from '@radix-ui/themes';

export default function Page() {
return (
<form action="/auth/sign-in" method="POST">
<Button type="submit">Sign in with Spotify</Button>
</form>
);
}
5 changes: 5 additions & 0 deletions env.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ const getGitSha = () => {

const envSchema = z.object({
GIT_SHA: z.string().min(1).default(getGitSha()),
NEXT_PUBLIC_SUPABASE_ANON_KEY: z.string(),
NEXT_PUBLIC_SUPABASE_URL: z.string(),
// We don't have these configurations yet.
SUPABASE_SERVICE_ROLE_KEY: z.string().default(''),
SUPABASE_URL: z.string().default(''),
});

const envValidation = envSchema.safeParse(process.env);
Expand Down
63 changes: 63 additions & 0 deletions lib/services/supabase/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import type { Database } from '@/types/database';
import type { CookieOptions } from '@supabase/ssr';
import type { NextRequest } from 'next/server';

import { env } from '@/env.mjs';
import { createServerClient } from '@supabase/ssr';
import { NextResponse } from 'next/server';

export const getSupabaseAuthSession = async (request: NextRequest) => {
let response = NextResponse.next({
request: {
headers: request.headers,
},
});

const supabase = createServerClient<Database>(
env.NEXT_PUBLIC_SUPABASE_URL,
env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
{
cookies: {
get(name: string) {
return request.cookies.get(name)?.value;
},
remove(name, options) {
request.cookies.delete({
name,
...options,
});
response = NextResponse.next({
request: {
headers: request.headers,
},
});
response.cookies.delete({
name,
...options,
});
},
set(name: string, value: string, options: CookieOptions) {
request.cookies.set({
name,
value,
...options,
});
response = NextResponse.next({
request: {
headers: request.headers,
},
});
response.cookies.set({
name,
value,
...options,
});
},
},
},
);

const authSession = await supabase.auth.getSession();

return { authSession, response };
};
11 changes: 11 additions & 0 deletions lib/services/supabase/browser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
'use client';
import type { Database } from '@/types/database';

import { env } from '@/env.mjs';
import { createBrowserClient } from '@supabase/ssr';

export const createSupabaseBrowserClient = () =>
createBrowserClient<Database>(
env.NEXT_PUBLIC_SUPABASE_URL,
env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
);
39 changes: 39 additions & 0 deletions lib/services/supabase/server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import type { Database } from '@/types/database';
import type { CookieOptions } from '@supabase/ssr';
import type { cookies } from 'next/headers';

import { env } from '@/env.mjs';
import { createServerClient } from '@supabase/ssr';

export const createSupabaseServerClient = (
cookieStore: ReturnType<typeof cookies>,
) =>
createServerClient<Database>(
env.NEXT_PUBLIC_SUPABASE_URL,
env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
{
cookies: {
get(name: string) {
return cookieStore.get(name)?.value;
},
remove(name: string, options: CookieOptions) {
try {
cookieStore.delete({ name, ...options });
} catch (error) {
// The `delete` method was called from a Server Component.
// This can be ignored if you have middleware refreshing
// user sessions.
}
},
set(name: string, value: string, options: CookieOptions) {
try {
cookieStore.set({ name, value, ...options });
} catch (error) {
// The `set` method was called from a Server Component.
// This can be ignored if you have middleware refreshing
// user sessions.
}
},
},
},
);
7 changes: 7 additions & 0 deletions lib/services/supabase/service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type { Database } from '@/types/database';

import { env } from '@/env.mjs';
import { createClient } from '@supabase/supabase-js';

export const createSupabaseServiceClient = () =>
createClient<Database>(env.SUPABASE_URL, env.SUPABASE_SERVICE_ROLE_KEY);
29 changes: 29 additions & 0 deletions middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import type { NextRequest } from 'next/server';

import { getSupabaseAuthSession } from '@/lib/services/supabase/auth';
import { NextResponse } from 'next/server';

const publicRoutes = ['/', '/auth/callback', '/auth/sign-in', '/sign-in'];

export async function middleware(request: NextRequest) {
const { pathname } = new URL(request.url);

if (publicRoutes.includes(pathname)) {
return NextResponse.next({
request: {
headers: request.headers,
},
});
}

const { authSession, response } = await getSupabaseAuthSession(request);
if (authSession.data.session) {
return response;
}

return NextResponse.redirect(new URL('/sign-in', request.url));
}

export const config = {
matcher: ['/((?!_next/static|_next/image|favicon.ico|logo.png|logo.svg).*)'],
};
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,16 @@
"prepare": "is-ci || husky install",
"start": "next start",
"storybook": "storybook dev -p 6006",
"supabase:login": "supabase login",
"supabase:types": "supabase gen types typescript --project-id zcetythxtznisfgaakyk > types/database.ts",
"typecheck": "tsc --noEmit"
},
"prettier": "@vercel/style-guide/prettier",
"dependencies": {
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/themes": "^2.0.2",
"@supabase/ssr": "^0.0.10",
"@supabase/supabase-js": "^2.39.0",
"next": "14.0.4",
"next-themes": "1.0.0-beta.0",
"react": "^18",
Expand Down
Loading

0 comments on commit b35d43d

Please sign in to comment.