Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions apps/docs/content/_partials/project_setup.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,13 @@ Now set up the database schema. You can use the "User Management Starter" quicks
<TabPanel id="dashboard" label="Dashboard">

1. Go to the [SQL Editor](https://supabase.com/dashboard/project/_/sql) page in the Dashboard.
2. Click **User Management Starter**.
2. Click **User Management Starter** under the **Community > Quickstarts** tab.
3. Click **Run**.

<Admonition type="note">

You can pull the database schema down to your local project by running the `db pull` command. Read the [local development docs](/docs/guides/cli/local-development#link-your-project) for detailed instructions.

{/* TODO: discuss */}

```bash
supabase link --project-ref <project-id>
# You can get <project-id> from your project's dashboard URL: https://supabase.com/dashboard/project/<project-id>
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/content/_partials/quickstart_intro.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ This tutorial demonstrates how to build a basic user management app. The app aut

- [Supabase Database](/docs/guides/database) - a Postgres database for storing your user data and [Row Level Security](/docs/guides/auth#row-level-security) so data is protected and users can only access their own information.
- [Supabase Auth](/docs/guides/auth) - allow users to sign up and log in.
- [Supabase Storage](/docs/guides/storage) - users can upload a profile photo.
- [Supabase Storage](/docs/guides/storage) - allow users to upload a profile photo.
73 changes: 34 additions & 39 deletions apps/docs/content/guides/getting-started/tutorials/with-nextjs.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ If you get stuck while working through this guide, refer to the [full example on

## Building the app

Let's start building the Next.js app from scratch.
Start building the Next.js app from scratch.

### Initialize a Next.js app

We can use [`create-next-app`](https://nextjs.org/docs/getting-started) to initialize an app called `supabase-nextjs`:
Use [`create-next-app`](https://nextjs.org/docs/getting-started) to initialize an app called `supabase-nextjs`:

<Tabs
scrollable
Expand Down Expand Up @@ -54,8 +54,7 @@ Then install the Supabase client library: [supabase-js](https://github.com/supab
npm install @supabase/supabase-js
```

And finally we want to save the environment variables in a `.env.local`.
Create a `.env.local` file at the root of the project, and paste the API URL and the `anon` key that you copied [earlier](#get-the-api-keys).
Save the environment variables in a `.env.local` file at the root of the project, and paste the API URL and the `anon` key that you copied [earlier](#get-the-api-keys).

```bash .env.local
NEXT_PUBLIC_SUPABASE_URL=YOUR_SUPABASE_URL
Expand All @@ -65,13 +64,13 @@ NEXT_PUBLIC_SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY
### App styling (optional)

An optional step is to update the CSS file `app/globals.css` to make the app look nice.
You can find the full contents of this file [here](https://raw.githubusercontent.com/supabase/supabase/master/examples/user-management/nextjs-user-management/app/globals.css).
You can find the full contents of this file [in the example repository](https://raw.githubusercontent.com/supabase/supabase/master/examples/user-management/nextjs-user-management/app/globals.css).

### Supabase Server-Side Auth

Next.js is a highly versatile framework offering pre-rendering at build time (SSG), server-side rendering at request time (SSR), API routes, and middleware edge-functions.

To better integrate with the framework, we've created the `@supabase/ssr` package for Server-Side Auth. It has all the functionalities to quickly configure your Supabase project to use cookies for storing user sessions. See the [Next.js Server-Side Auth guide](/docs/guides/auth/server-side/nextjs) for more information.
To better integrate with the framework, we've created the `@supabase/ssr` package for Server-Side Auth. It has all the functionalities to quickly configure your Supabase project to use cookies for storing user sessions. Read the [Next.js Server-Side Auth guide](/docs/guides/auth/server-side/nextjs) for more information.

Install the package for Next.js.

Expand Down Expand Up @@ -184,11 +183,11 @@ Since Server Components can't write cookies, you need middleware to refresh expi
- Passing the refreshed Auth token to Server Components through `request.cookies.set`, so they don't attempt to refresh the same token themselves.
- Passing the refreshed Auth token to the browser, so it replaces the old token. This is done with `response.cookies.set`.

You could also add a matcher, so that the middleware only runs on route that access Supabase. For more information, check out this [documentation](https://nextjs.org/docs/app/building-your-application/routing/middleware#matching-paths).
You could also add a matcher, so that the middleware only runs on routes that access Supabase. For more information, read [the Next.js matcher documentation](https://nextjs.org/docs/app/api-reference/file-conventions/middleware#matcher).

<Admonition type="danger">

Be careful when protecting pages. The server gets the user session from the cookies, which can be spoofed by anyone.
Be careful when protecting pages. The server gets the user session from the cookies, which anyone can spoof.

Always use `supabase.auth.getUser()` to protect pages and user data.

Expand Down Expand Up @@ -358,17 +357,17 @@ meta="name=app/login/page.tsx"

</Tabs>

Navigate to `http://localhost:3000/login`. You should see your login form, but it's not yet hooked up to the actual login function. Next, you need to create the login/signup actions. They will:
Next, you need to create the login/signup actions to hook up the form to the function. Which does the following:

- Retrieve the user's information.
- Send that information to Supabase as a signup request, which in turns will send a confirmation email.
- Send that information to Supabase as a signup request, which in turns sends a confirmation email.
- Handle any error that arises.

<Admonition type="caution">

Note that cookies is called before any calls to Supabase, which opts fetch calls out of Next.js's caching. This is important for authenticated data fetches, to ensure that users get access only to their own data.
The `cookies` method is called before any calls to Supabase, which takes fetch calls out of Next.js's caching. This is important for authenticated data fetches, to ensure that users get access only to their own data.

See the Next.js docs to learn more about [opting out of data caching](https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating#opting-out-of-data-caching).
Read the Next.js docs to learn more about [opting out of data caching](https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating#opting-out-of-data-caching).

</Admonition>

Expand All @@ -382,6 +381,8 @@ See the Next.js docs to learn more about [opting out of data caching](https://ne

<TabPanel id="js" label="JavaScript">

Create the `action.js` file in the `app/login` folder, which contains the login and signup functions and the `error/page.jsx` file, and displays an error message if the login or signup fails.

<$CodeTabs>

```js name=app/login/actions.js
Expand Down Expand Up @@ -409,7 +410,6 @@ export async function login(formData) {
}

revalidatePath('/', 'layout')
redirect('/account')
}

export async function signup(formData) {
Expand All @@ -427,7 +427,6 @@ export async function signup(formData) {
}

revalidatePath('/', 'layout')
redirect('/account')
}
```

Expand All @@ -443,6 +442,8 @@ export default function ErrorPage() {

<TabPanel id="ts" label="TypeScript">

Create the `action.ts` file in the `app/login` folder, which contains the login and signup functions and the `error/page.tsx` file, which displays an error message if the login or signup fails.

<$CodeTabs>

<$CodeSample
Expand All @@ -461,35 +462,31 @@ meta="name=app/error/page.tsx"

</TabPanel>

When you enter your email and password, you will receive an email with the title **Confirm Your Signup**. Congrats 🎉!!!

</Tabs>

### Email template

Change the email template to support a server-side authentication flow.

Before we proceed, let's change the email template to support sending a token hash:
Before proceeding, change the email template to support support a server-side authentication flow that sends a token hash:

- Go to the [Auth templates](/dashboard/project/_/auth/templates) page in your dashboard.
- Select `Confirm signup` template.
- Select the **Confirm signup** template.
- Change `{{ .ConfirmationURL }}` to `{{ .SiteURL }}/auth/confirm?token_hash={{ .TokenHash }}&type=email`.

<Admonition type="tip">

Did you know? You could also customize emails sent out to new users, including the email's looks, content, and query parameters. Check out the [settings of your project](/dashboard/project/_/auth/templates).
**Did you know?** You can also customize other emails sent out to new users, including the email's looks, content, and query parameters. Check out the [settings of your project](/dashboard/project/_/auth/templates).

</Admonition>

### Confirmation endpoint

As we are working in a server-side rendering (SSR) environment, it is necessary to create a server endpoint responsible for exchanging the `token_hash` for a session.
As you are working in a server-side rendering (SSR) environment, you need to create a server endpoint responsible for exchanging the `token_hash` for a session.

In the following code snippet, we perform the following steps:
The code performs the following steps:

- Retrieve the code sent back from the Supabase Auth server using the `token_hash` query parameter.
- Exchange this code for a session, which we store in our chosen storage mechanism (in this case, cookies).
- Finally, we redirect the user to the `account` page.
- Retrieves the code sent back from the Supabase Auth server using the `token_hash` query parameter.
- Exchanges this code for a session, which you store in your chosen storage mechanism (in this case, cookies).
- Finally, redirects the user to the `account` page.

<Tabs
scrollable
Expand All @@ -505,7 +502,6 @@ In the following code snippet, we perform the following steps:

```js name=app/auth/confirm/route.js
import { NextResponse } from 'next/server'

import { createClient } from '@/utils/supabase/server'

// Creating a handler to a GET request to route /auth/confirm
Expand Down Expand Up @@ -562,9 +558,9 @@ meta="name=app/auth/confirm/route.ts"

### Account page

After a user is signed in we can allow them to edit their profile details and manage their account.
After a user signs in, allow them to edit their profile details and manage their account.

Let's create a new component for that called `AccountForm` within the `app/account` folder.
Create a new component for that called `AccountForm` within the `app/account` folder.

<Tabs
scrollable
Expand All @@ -589,7 +585,6 @@ export default function AccountForm({ user }) {
const [fullname, setFullname] = useState(null)
const [username, setUsername] = useState(null)
const [website, setWebsite] = useState(null)
const [avatar_url, setAvatarUrl] = useState(null)

const getProfile = useCallback(async () => {
try {
Expand All @@ -609,7 +604,6 @@ export default function AccountForm({ user }) {
setFullname(data.full_name)
setUsername(data.username)
setWebsite(data.website)
setAvatarUrl(data.avatar_url)
}
} catch (error) {
alert('Error loading user data!')
Expand All @@ -631,7 +625,6 @@ export default function AccountForm({ user }) {
full_name: fullname,
username,
website,
avatar_url,
updated_at: new Date().toISOString(),
})
if (error) throw error
Expand Down Expand Up @@ -680,7 +673,7 @@ export default function AccountForm({ user }) {
<div>
<button
className="button primary block"
onClick={() => updateProfile({ fullname, username, website, avatar_url })}
onClick={() => updateProfile({ fullname, username, website })}
disabled={loading}
>
{loading ? 'Loading ...' : 'Update'}
Expand Down Expand Up @@ -719,7 +712,7 @@ meta="name=app/account/account-form.tsx"

</Tabs>

Create an account page for the `AccountForm` component we just created
Create an account page for the `AccountForm` component you just created

<Tabs
scrollable
Expand Down Expand Up @@ -769,7 +762,7 @@ meta="name=app/account/page.tsx"

### Sign out

Let's create a route handler to handle the signout from the server side. Make sure to check if the user is logged in first!
Create a route handler to handle the sign out from the server side, making sure to check if the user is logged in first.

<Tabs
scrollable
Expand Down Expand Up @@ -829,13 +822,15 @@ meta="name=app/auth/signout/route.ts"

### Launch!

Now that we have all the pages, route handlers and components in place, let's run this in a terminal window:
Now you have all the pages, route handlers, and components in place, run the following in a terminal window:

```bash
npm run dev
```

And then open the browser to [localhost:3000](http://localhost:3000) and you should see the completed app.
And then open the browser to [localhost:3000/login](http://localhost:3000/login) and you should see the completed app.

When you enter your email and password, you will receive an email with the title **Confirm Your Signup**. Congrats 🎉!!!

## Bonus: Profile photos

Expand All @@ -844,7 +839,7 @@ photos and videos.

### Create an upload widget

Let's create an avatar widget for the user so that they can upload a profile photo. We can start by creating a new component:
Create an avatar widget for the user so that they can upload a profile photo. Start by creating a new component:

<Tabs
scrollable
Expand Down Expand Up @@ -970,7 +965,7 @@ meta="name=app/account/avatar.tsx"

### Add the new widget

And then we can add the widget to the `AccountForm` component:
Then add the widget to the `AccountForm` component:

<Tabs
scrollable
Expand Down
Loading
Loading