Skip to content

Commit

Permalink
Merge branch 'main' into tobbe-use-route-name
Browse files Browse the repository at this point in the history
  • Loading branch information
Tobbe committed Dec 27, 2023
2 parents 73abfdf + 39cc3aa commit a0b1120
Show file tree
Hide file tree
Showing 12 changed files with 147 additions and 83 deletions.
6 changes: 6 additions & 0 deletions __fixtures__/test-project/api/db/schema.prisma
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
// Don't forget to tell Prisma about your edits to this file using
// `yarn rw prisma migrate dev` or `yarn rw prisma db push`.
// `migrate` is like committing while `push` is for prototyping.
// Read more about both here:
// https://www.prisma.io/docs/orm/prisma-migrate

datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
Expand Down
53 changes: 49 additions & 4 deletions docs/docs/auth/supertokens.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,37 @@ For now, let's focus on SuperTokens's side of things.

When you run the setup command it configures your app to support both email+password logins as well as social auth logins (Apple, GitHub and Google). Working with those social auth logins does require quite a few environment variables. And SuperTokens itself needs a couple variables too. Thankfully SuperTokens makes this very easy to setup as they provide values we can use for testing.

So just copy this to your project's `.env` file.
# Environment variables

```bash title=".env"
The environment variables have to be added either to your project's `.env` file (when running in development environment), or to the environment variables of your hosting provider (when running in production).

## Base setup

```bash
SUPERTOKENS_APP_NAME="Redwoodjs App" # this will be used in the email template for password reset or email verification emails.
SUPERTOKENS_JWKS_URL=http://localhost:8910/.redwood/functions/auth/jwt/jwks.json
SUPERTOKENS_CONNECTION_URI=https://try.supertokens.io # set to the correct connection uri
```

## Production setup

Assuming that your web side is hosted on `https://myapp.com`:

```bash
SUPERTOKENS_WEBSITE_DOMAIN=https://myapp.com
SUPERTOKENS_JWKS_URL=https://myapp.com/.redwood/functions/auth/jwt/jwks.json
```

## Managed Supertokens service setup

```bash
SUPERTOKENS_API_KEY=your-api-key # The value can be omitted when self-hosting Supertokens
```

SUPERTOKENS_CONNECTION_URI=https://try.supertokens.io
## Social login setup
The following environment variables have to be set up (depending on the social login options):

```bash
SUPERTOKENS_APPLE_CLIENT_ID=4398792-io.supertokens.example.service
SUPERTOKENS_APPLE_SECRET_KEY_ID=7M48Y4RYDL
SUPERTOKENS_APPLE_SECRET_PRIVATE_KEY=-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgu8gXs+XYkqXD6Ala9Sf/iJXzhbwcoG5dMh1OonpdJUmgCgYIKoZIzj0DAQehRANCAASfrvlFbFCYqn3I2zeknYXLwtH30JuOKestDbSfZYxZNMqhF/OzdZFTV0zc5u5s3eN+oCWbnvl0hM+9IW0UlkdA\n-----END PRIVATE KEY-----
Expand All @@ -40,7 +64,24 @@ SUPERTOKENS_GOOGLE_CLIENT_ID=1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps
SUPERTOKENS_GOOGLE_CLIENT_SECRET=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
```

That should be enough; now, things should just work.
## `redwood.toml` setup

Make sure to modify `redwood.toml` to pass the required environment variables to the web side:

```toml
[web]
...
includeEnvironmentVariables = [
'SUPERTOKENS_WEBSITE_DOMAIN',
'SUPERTOKENS_API_DOMAIN',
'SUPERTOKENS_API_GATEWAY_PATH',
'SUPERTOKENS_APP_NAME'
]
```


# Page setup

Let's make sure: if this is a brand new project, generate a home page.
There we'll try to sign up by destructuring `signUp` from the `useAuth` hook (import that from `'src/auth'`). We'll also destructure and display `isAuthenticated` to see if it worked:

Expand Down Expand Up @@ -72,3 +113,7 @@ Clicking sign up should navigate you to `/auth` where SuperToken's default login
<img width="463" height="696" alt="SuperTokens default UI" src="https://user-images.githubusercontent.com/30793/215893664-d367eb3d-566e-4541-a01a-5772d95cc9c7.png" />

After you sign up, you should be redirected back to your Redwood app, and you should see `{"isAuthenticated":true}` on the page.

## Troubleshooting

If going to `http://localhost:8910/auth` results in the plain Javascript file being served instead of the expected auth page, rename the `web/src/auth.tsx` file to `web/src/authentication.tsx`, and update the imports (related to https://github.com/redwoodjs/redwood/issues/9740).
6 changes: 3 additions & 3 deletions docs/docs/router.md
Original file line number Diff line number Diff line change
Expand Up @@ -611,9 +611,9 @@ Or if the variable passed as a prop to a component can't be found:

![fatal_error_message_query](/img/router/fatal_error_message_query.png)

And if the page has a Cell, you'll see the Cell's request and response which may have contributed to the error:
And if the page has a Cell, you'll see the Cell's request which may have contributed to the error - but will depend on how your Suspense boundary is setup:

![fatal_error_message_request](/img/router/fatal_error_request.png)
![cell_error_request](/img/router/cell_req_error.png)

### In Production

Expand Down Expand Up @@ -675,7 +675,7 @@ Note that if you're copy-pasting this example, it uses [Tailwind CSS](https://ta

:::note Can I customize the development one?

As it's part of the RedwoodJS framework, you can't. But if there's a feature you want to add, let us know on the [forums](https://community.redwoodjs.com/).
As it's part of the RedwoodJS framework, you can't _change_ the dev fatal error page - but you can always build your own that takes the same props. If there's a feature you want to add to the built-in version, let us know on the [forums](https://community.redwoodjs.com/).
:::
Expand Down
Binary file added docs/static/img/router/cell_req_error.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ const apiGatewayPath =
process.env.SUPERTOKENS_API_GATEWAY_PATH || '/.redwood/functions'

export const config: TypeInput = {
# The below options are ok here even if you're not running on top of AWS Lambda, since Redwood internally translates Fastify request/response objects to and from the AWS Lambda format.
framework: 'awsLambda',
isInServerlessEnv: true,
appInfo: {
appName: 'SuperTokens RedwoodJS',
appName: process.env.SUPERTOKENS_APP_NAME,
apiDomain,
websiteDomain,
apiGatewayPath,
Expand All @@ -21,6 +22,7 @@ export const config: TypeInput = {
},
supertokens: {
connectionURI: process.env.SUPERTOKENS_CONNECTION_URI,
apiKey: process.env.SUPERTOKENS_API_KEY,
},
recipeList: [
ThirdPartyEmailPassword.init({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export const PreBuiltUI = [ThirdPartyEmailPasswordPreBuiltUI]
isBrowser &&
SuperTokens.init({
appInfo: {
appName: 'SuperTokens RedwoodJS',
appName: process.env.SUPERTOKENS_APP_NAME,
apiDomain,
websiteDomain,
apiGatewayPath,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export const config = {
},
supertokens: {
connectionURI: process.env.SUPERTOKENS_CONNECTION_URI,
apiKey: process.env.SUPERTOKENS_API_KEY,
},
recipeList: [
ThirdPartyEmailPassword.init({
Expand Down
6 changes: 6 additions & 0 deletions packages/create-redwood-app/templates/js/api/db/schema.prisma
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
// Don't forget to tell Prisma about your edits to this file using
// `yarn rw prisma migrate dev` or `yarn rw prisma db push`.
// `migrate` is like committing while `push` is for prototyping.
// Read more about both here:
// https://www.prisma.io/docs/orm/prisma-migrate

datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
Expand Down
6 changes: 6 additions & 0 deletions packages/create-redwood-app/templates/ts/api/db/schema.prisma
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
// Don't forget to tell Prisma about your edits to this file using
// `yarn rw prisma migrate dev` or `yarn rw prisma db push`.
// `migrate` is like committing while `push` is for prototyping.
// Read more about both here:
// https://www.prisma.io/docs/orm/prisma-migrate

datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
Expand Down
50 changes: 33 additions & 17 deletions packages/web/src/apollo/links.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { HttpOptions } from '@apollo/client'
import { ApolloLink, HttpLink } from '@apollo/client'
import type { HttpOptions, Operation } from '@apollo/client'
import { ApolloLink, HttpLink, Observable } from '@apollo/client'
import { setContext } from '@apollo/client/link/context'
import { print } from 'graphql/language/printer'

Expand All @@ -8,28 +8,44 @@ export function createHttpLink(
httpLinkConfig: HttpOptions | undefined
) {
return new HttpLink({
// @MARK: we have to construct the absoltue url for SSR
uri,
...httpLinkConfig,
// you can disable result caching here if you want to
// (this does not work if you are rendering your page with `export const dynamic = "force-static"`)
// @TODO: this is probably NextJS specific. Revisit once we have our own apollo package
fetchOptions: { cache: 'no-store' },
})
}
export function createUpdateDataLink(data: any) {
return new ApolloLink((operation, forward) => {
const { operationName, query, variables } = operation

data.mostRecentRequest = {}
data.mostRecentRequest.operationName = operationName
data.mostRecentRequest.operationKind = query?.kind.toString()
data.mostRecentRequest.variables = variables
data.mostRecentRequest.query = query && print(operation.query)
function enhanceError(operation: Operation, error: any) {
const { operationName, query, variables } = operation

return forward(operation).map((result) => {
data.mostRecentResponse = result
error.__RedwoodEnhancedError = {
operationName,
operationKind: query?.kind.toString(),
variables,
query: query && print(query),
}

return result
return error
}

export function createUpdateDataLink() {
return new ApolloLink((operation, forward) => {
return new Observable((observer) => {
forward(operation).subscribe({
next(result) {
if (result.errors) {
result.errors.forEach((error) => {
enhanceError(operation, error)
})
}
observer.next(result)
},
error(error: any) {
observer.error(enhanceError(operation, error))
},
complete: observer.complete.bind(observer),
})
})
})
}
Expand Down Expand Up @@ -96,7 +112,7 @@ export function createFinalLink({
export type RedwoodApolloLinkName =
| 'withToken'
| 'authMiddleware'
| 'updateDataApolloLink'
| 'enhanceErrorLink'
| 'httpLink'

export type RedwoodApolloLink<
Expand All @@ -110,7 +126,7 @@ export type RedwoodApolloLink<
export type RedwoodApolloLinks = [
RedwoodApolloLink<'withToken'>,
RedwoodApolloLink<'authMiddleware'>,
RedwoodApolloLink<'updateDataApolloLink'>,
RedwoodApolloLink<'enhanceErrorLink'>,
RedwoodApolloLink<'httpLink', HttpLink>
]

Expand Down
44 changes: 6 additions & 38 deletions packages/web/src/apollo/suspense.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
import type {
ApolloCache,
ApolloClientOptions,
ApolloLink,
HttpOptions,
InMemoryCacheConfig,
setLogVerbosity,
ApolloLink,
} from '@apollo/client'
import {
setLogVerbosity as apolloSetLogVerbosity,
Expand All @@ -24,10 +24,10 @@ import {
ApolloNextAppProvider,
NextSSRApolloClient,
NextSSRInMemoryCache,
useSuspenseQuery,
useBackgroundQuery,
useReadQuery,
useQuery,
useReadQuery,
useSuspenseQuery,
} from '@apollo/experimental-nextjs-app-support/ssr'

import type { UseAuth } from '@redwoodjs/auth'
Expand Down Expand Up @@ -127,13 +127,6 @@ const ApolloProviderWithFetchConfig: React.FunctionComponent<{
// See https://www.apollographql.com/docs/react/api/link/introduction.
const { getToken, type: authProviderType } = useAuth()

// `updateDataApolloLink` keeps track of the most recent req/res data so they can be passed to
// any errors passed up to an error boundary.
const data = {
mostRecentRequest: undefined,
mostRecentResponse: undefined,
} as any

const { headers, uri } = useFetchConfig()

const getGraphqlUrl = () => {
Expand All @@ -157,17 +150,11 @@ const ApolloProviderWithFetchConfig: React.FunctionComponent<{
name: 'authMiddleware',
link: createAuthApolloLink(authProviderType, headers),
},
// @TODO: do we need this in prod? I think it's only for dev errors
{ name: 'updateDataApolloLink', link: createUpdateDataLink(data) },
// @REVIEW: Should we take this out for prod?
{ name: 'enhanceErrorLink', link: createUpdateDataLink() },
{ name: 'httpLink', link: createHttpLink(getGraphqlUrl(), httpLinkConfig) },
]

const extendErrorAndRethrow = (error: any, _errorInfo: React.ErrorInfo) => {
error['mostRecentRequest'] = data.mostRecentRequest
error['mostRecentResponse'] = data.mostRecentResponse
throw error
}

function makeClient() {
// @MARK use special Apollo client
return new NextSSRApolloClient({
Expand All @@ -181,30 +168,11 @@ const ApolloProviderWithFetchConfig: React.FunctionComponent<{

return (
<ApolloNextAppProvider makeClient={makeClient}>
<ErrorBoundary onError={extendErrorAndRethrow}>{children}</ErrorBoundary>
{children}
</ApolloNextAppProvider>
)
}

type ComponentDidCatch = React.ComponentLifecycle<any, any>['componentDidCatch']

interface ErrorBoundaryProps {
error?: unknown
onError: NonNullable<ComponentDidCatch>
children: React.ReactNode
}

class ErrorBoundary extends React.Component<ErrorBoundaryProps> {
componentDidCatch(...args: Parameters<NonNullable<ComponentDidCatch>>) {
this.setState({})
this.props.onError(...args)
}

render() {
return this.props.children
}
}

export const RedwoodApolloProvider: React.FunctionComponent<{
graphQLClientConfig?: GraphQLClientConfigProp
useAuth?: UseAuth
Expand Down
Loading

0 comments on commit a0b1120

Please sign in to comment.