From f149f9f58bdcab4bd99b718732ebea065b1f3722 Mon Sep 17 00:00:00 2001 From: Hui Zhao Date: Fri, 10 May 2024 17:13:15 -0700 Subject: [PATCH 1/9] chore: add Gen2 specific doc for using SSR adapter with Nuxt 3 --- src/directory/directory.mjs | 3 + .../server-side-rendering/nuxt/index.mdx | 626 ++++++++++++++++++ 2 files changed, 629 insertions(+) create mode 100644 src/pages/[platform]/build-a-backend/server-side-rendering/nuxt/index.mdx diff --git a/src/directory/directory.mjs b/src/directory/directory.mjs index ef3a5e7fbc5..8a85bd6023f 100644 --- a/src/directory/directory.mjs +++ b/src/directory/directory.mjs @@ -397,6 +397,9 @@ export const directory = { children: [ { path: 'src/pages/[platform]/build-a-backend/server-side-rendering/nextjs-app-router-server-components/index.mdx' + }, + { + path: 'src/pages/[platform]/build-a-backend/server-side-rendering/nuxt/index.mdx' } ] }, diff --git a/src/pages/[platform]/build-a-backend/server-side-rendering/nuxt/index.mdx b/src/pages/[platform]/build-a-backend/server-side-rendering/nuxt/index.mdx new file mode 100644 index 00000000000..03efd1aa5ad --- /dev/null +++ b/src/pages/[platform]/build-a-backend/server-side-rendering/nuxt/index.mdx @@ -0,0 +1,626 @@ +import { getCustomStaticPath } from '@/utils/getCustomStaticPath'; +import { getChildPageNodes } from '@/utils/getChildPageNodes'; + +export const meta = { + title: 'Use Amplify categories APIs from Nuxt 3', + description: 'Use Amplify categories APIs from Nuxt 3', + platforms: [ + 'javascript', + 'vue' + ], +}; + +export const getStaticPaths = async () => { + return getCustomStaticPath(meta.platforms); +}; + +export function getStaticProps(context) { + const childPageNodes = getChildPageNodes(meta.route); + return { + props: { + platform: context.params.platform, + meta, + childPageNodes + } + }; +} + +If you have not already done so, please read the introduction documentation, [Use Amplify Categories APIs in Server Side Rendering](/gen1/[platform]/build-a-backend/server-side-rendering/), to learn about how to use Amplify categories APIs in server-side rendering. + +This documentation provides a getting-started guide to using the generic `runWithAmplifyServerContext` adapter (exported from `aws-amplify/adapter-core`) to enable Amplify Gen2 in a Nuxt 3 project. The examples in this documentation may not present best practices for your Nuxt project. You are welcome to provide suggestions and contributions to improve this documentation or to create a Nuxt adapter package for Amplify and let others use it. + + + +**NOTE:** This guide assumes that you have a deep knowledge of Nuxt 3. + + + +## Start Using Amplify Gen2 in Your Nuxt 3 project + +You can install Amplify Gen2 following this guide, [Manual installation](/[platform]/start/manual-installation/). + +## Set Up the AmplifyAPIs Plugin + +Nuxt 3 offers universal rendering by default, where your data fetching logic may be executed on both the client and server sides. Amplify offers APIs that are capable of running within a server context to support use cases such as server-side rendering (SSR) and static site generation (SSG), though Amplify's client-side APIs and server-side APIs of Amplify are slightly different. You can set up an `AmplifyAPIs` plugin to make your data fetching logic run smoothly across the client and server. + +1. If you haven’t already done so, create a `plugins` directory under the root of your Nuxt project +2. Create two files `01.amplify-apis.client.ts` and `01.amplify-apis.server.ts` under the `plugins` directory + + + +**NOTE:** the leading number in the files name indicate the plugin loading order, details see https://nuxt.com/docs/guide/directory-structure/plugins#registration-order. The `.client` and `.server` indicate the runtime that the logic contained in the file will run on, client or server. For details see: https://nuxt.com/docs/guide/directory-structure/plugins + + + +In these files, you will register both client-specific and server-specific Amplify APIs that you will use in your Nuxt project as a plugin. You can then access these APIs via the `useNuxtApp` composable. + +### Implement `01.amplify-apis.client.ts` + +Example implementation: + +```ts title="plugins/01.amplify-apis.client.ts" +import { Amplify } from 'aws-amplify'; +import { + fetchAuthSession, + fetchUserAttributes, + signIn, + signOut +} from 'aws-amplify/auth'; +import { list } from 'aws-amplify/storage'; +import { generateClient } from 'aws-amplify/api'; +import type { Schema } from '~/amplify/data/resource'; +import config from '../amplify_outputs.json'; + +const client = generateClient(); + +export default defineNuxtPlugin({ + name: 'AmplifyAPIs', + enforce: 'pre', + + setup() { + // This configures Amplify on the client side of your Nuxt app + Amplify.configure(config, { ssr: true }); + + return { + provide: { + // You can add the Amplify APIs that you will use on the client side + // of your Nuxt app here. + // + // You can call the API by via the composable `useNuxtApp()`. For example: + // `useNuxtApp().$Amplify.Auth.fetchAuthSession()` + Amplify: { + Auth: { + fetchAuthSession, + fetchUserAttributes, + signIn, + signOut + }, + Storage: { + list + }, + GraphQL: { + client + } + } + } + }; + } +}); +``` + + + +Make sure you call `Amplify.configure` as early as possible in your application’s life-cycle. A missing configuration or `NoCredentials` error is thrown if `Amplify.configure` has not been called before other Amplify JavaScript APIs. Review the [Library Not Configured Troubleshooting guide](/gen1/[platform]/build-a-backend/troubleshooting/library-not-configured/) for possible causes of this issue. + + + +### Implement `01.amplify-apis.server.ts` + +Example implementation: + +```ts title="plugins/01.amplify-apis.server.ts" +import type { CookieRef } from 'nuxt/app'; +import { + createKeyValueStorageFromCookieStorageAdapter, + createUserPoolsTokenProvider, + createAWSCredentialsAndIdentityIdProvider, + runWithAmplifyServerContext +} from 'aws-amplify/adapter-core'; +import { parseAmplifyConfig } from 'aws-amplify/utils'; +import { + fetchAuthSession, + fetchUserAttributes, + getCurrentUser +} from 'aws-amplify/auth/server'; +import { list } from 'aws-amplify/storage/server'; +import { generateClient } from 'aws-amplify/api/server'; +import type { ListPaginateWithPathInput } from 'aws-amplify/storage'; +import type { + LibraryOptions, + FetchAuthSessionOptions +} from '@aws-amplify/core'; +import type { Schema } from '~/amplify/data/resource'; + +import config from '../amplify_outputs.json'; + +// parse the content of `amplify_outputs.json` into the shape of ResourceConfig +const amplifyConfig = parseAmplifyConfig(config); + +// create the Amplify used token cookies names array +const userPoolClientId = amplifyConfig.Auth!.Cognito.userPoolClientId; +const lastAuthUserCookieName = `CognitoIdentityServiceProvider.${userPoolClientId}.LastAuthUser`; + +// create a GraphQL client that can be used in a server context +const gqlServerClient = generateClient({ config: amplifyConfig }); + +// Extra the model operation function types for creating wrapper function later +type RemoveFirstParam = Params extends [infer _, ...infer Rest] ? Rest : never; +type TodoListInput = RemoveFirstParam>; +type TodoCreateInput = RemoveFirstParam>; +type TodoUpdateInput = RemoveFirstParam>; + +const getAmplifyAuthKeys = (lastAuthUser: string) => + ['idToken', 'accessToken', 'refreshToken', 'clockDrift'] + .map( + (key) => + `CognitoIdentityServiceProvider.${userPoolClientId}.${lastAuthUser}.${key}` + ) + .concat(lastAuthUserCookieName); + +// define the plugin +export default defineNuxtPlugin({ + name: 'AmplifyAPIs', + enforce: 'pre', + setup() { + // The Nuxt composable `useCookie` is capable of sending cookies to the + // client via the `SetCookie` header. If the `expires` option is left empty, + // it sets a cookie as a session cookie. If you need to persist the cookie + // on the client side after your end user closes your Web app, you need to + // specify an `expires` value. + // + // We use 30 days here as an example (the default Cognito refreshToken + // expiration time). + const expires = new Date(); + expires.setDate(expires.getDate() + 30); + + // Get the last auth user cookie value + // + // We use `sameSite: 'lax'` in this example, which allows the cookie to be + // sent to your Nuxt server when your end user gets redirected to your Web + // app from a different domain. You should choose an appropriate value for + // your own use cases. + const lastAuthUserCookie = useCookie(lastAuthUserCookieName, { + sameSite: 'lax', + expires, + secure: true + }); + + // Get all Amplify auth token cookie names + const authKeys = lastAuthUserCookie.value + ? getAmplifyAuthKeys(lastAuthUserCookie.value) + : []; + + // Create a key-value map of cookie name => cookie ref + // + // Using the composable `useCookie` here in the plugin setup prevents + // cross-request pollution. + const amplifyCookies = authKeys + .map((name) => ({ + name, + cookieRef: useCookie(name, { sameSite: 'lax', expires, secure: true }) + })) + .reduce>>( + (result, current) => ({ + ...result, + [current.name]: current.cookieRef + }), + {} + ); + + // Create a key value storage based on the cookies + // + // This key value storage is responsible for providing Amplify Auth tokens to + // the APIs that you are calling. + // + // If you implement the `set` method, when Amplify needed to refresh the Auth + // tokens on the server side, the new tokens would be sent back to the client + // side via `SetCookie` header in the response. Otherwise the refresh tokens + // would not be propagate to the client side, and Amplify would refresh + // the tokens when needed on the client side. + // + // In addition, if you decide not to implement the `set` method, you don't + // need to pass any `CookieOptions` to the `useCookie` composable. + const keyValueStorage = createKeyValueStorageFromCookieStorageAdapter({ + get(name) { + const cookieRef = amplifyCookies[name]; + + if (cookieRef && cookieRef.value) { + return { name, value: cookieRef.value }; + } + + return undefined; + }, + getAll() { + return Object.entries(amplifyCookies).map(([name, cookieRef]) => { + return { name, value: cookieRef.value ?? undefined }; + }); + }, + set(name, value) { + const cookieRef = amplifyCookies[name]; + if (cookieRef) { + cookieRef.value = value; + } + }, + delete(name) { + const cookieRef = amplifyCookies[name]; + + if (cookieRef) { + cookieRef.value = null; + } + } + }); + + // Create a token provider + const tokenProvider = createUserPoolsTokenProvider( + amplifyConfig.Auth!, + keyValueStorage + ); + + // Create a credentials provider + const credentialsProvider = createAWSCredentialsAndIdentityIdProvider( + amplifyConfig.Auth!, + keyValueStorage + ); + + // Create the libraryOptions object + const libraryOptions: LibraryOptions = { + Auth: { + tokenProvider, + credentialsProvider + } + }; + + return { + provide: { + // You can add the Amplify APIs that you will use on the server side of + // your Nuxt app here. You must only use the APIs exported from the + // `aws-amplify//server` subpaths. + // + // You can call the API by via the composable `useNuxtApp()`. For example: + // `useNuxtApp().$Amplify.Auth.fetchAuthSession()` + // + // Recall that Amplify server APIs are required to be called in a isolated + // server context that is created by the `runWithAmplifyServerContext` + // function. + Amplify: { + Auth: { + fetchAuthSession: (options: FetchAuthSessionOptions) => + runWithAmplifyServerContext( + amplifyConfig, + libraryOptions, + (contextSpec) => fetchAuthSession(contextSpec, options) + ), + fetchUserAttributes: () => + runWithAmplifyServerContext( + amplifyConfig, + libraryOptions, + (contextSpec) => fetchUserAttributes(contextSpec) + ), + getCurrentUser: () => + runWithAmplifyServerContext( + amplifyConfig, + libraryOptions, + (contextSpec) => getCurrentUser(contextSpec) + ) + }, + Storage: { + list: (input: ListPaginateWithPathInput) => + runWithAmplifyServerContext( + amplifyConfig, + libraryOptions, + (contextSpec) => list(contextSpec, input) + ) + }, + GraphQL: { + client: { + models: { + Todo: { + list(...input: TodoListInput) { + return runWithAmplifyServerContext( + amplifyConfig, + libraryOptions, + (contextSpec) => gqlServerClient.models.Todo.list(contextSpec, ...input) + ) + }, + create(...input: TodoCreateInput) { + return runWithAmplifyServerContext( + amplifyConfig, + libraryOptions, + (contextSpec) => gqlServerClient.models.Todo.create(contextSpec, ...input) + ) + }, + update(...input: TodoUpdateInput) { + return runWithAmplifyServerContext( + amplifyConfig, + libraryOptions, + (contextSpec) => gqlServerClient.models.Todo.update(contextSpec, ...input) + ) + } + } + } + } + } + } + } + }; + } +}); +``` + +#### Usage Example + +Using the Storage `list` API in `pages/storage-list.vue`: + +```ts title="pages/storage-list.vue" + +// `useAsyncData` and `useNuxtApp` are Nuxt composables +// `$Amplify` is generated by Nuxt according to the `provide` key in the plugins +// we've added above + + + + +``` + +Using the GraphQL API in `pages/todos-list.vue`: + +```ts title="pages/todos-list.vue" + + + +``` + +The above two pages can be rendered on both the client and server sides by default. `useNuxtApp().$Amplify` will pick up the correct implementation of `01.amplify-apis.client.ts` and `01.amplify-apis.server.ts` to use, depending on the runtime. + + + +Only a subset of Amplify APIs are usable on the server side, and as the libraries evolve, `amplify-apis.client` and `amplify-apis.server` may diverge further. You can guard your API calls to ensure an API is available in the context where you use it. E.g., you can use `if (process.client)` to ensure that a client-only API isn't executed on the server. + + + +## Set Up Auth Middleware to Protect Your Routes + +The auth middleware will use the plugin set up in the previous step as a dependency; therefore you can add the auth middleware via another plugin that will be loaded after the previous one. + +1. Create a `02.auth-redirect.ts` file under plugins directory + + + +**NOTE:** This file will run on both client and server, details see: https://nuxt.com/docs/guide/directory-structure/middleware#when-middleware-runs. The `02` name prefix ensures this plugin loads after the previous so `useNuxtApp().$Amplify` becomes available. + + + +### Implement `02.auth-redirect.ts` + +Example implementation: + +```ts title="plugins/02.auth-redirect.ts" +import { Amplify } from 'aws-amplify'; +import config from '~/amplify_outputs.json'; + +// Amplify.configure() only needs to be called on the client side +if (process.client) { + Amplify.configure(config, { ssr: true }); +} + +export default defineNuxtPlugin({ + name: 'AmplifyAuthRedirect', + enforce: 'pre', + setup() { + addRouteMiddleware( + 'AmplifyAuthMiddleware', + defineNuxtRouteMiddleware(async (to) => { + try { + const session = await useNuxtApp().$Amplify.Auth.fetchAuthSession(); + + // If the request is not associated with a valid user session + // redirect to the `/sign-in` route. + // You can also add route match rules against `to.path` + if (session.tokens === undefined && to.path !== '/sign-in') { + return navigateTo('/sign-in'); + } + + if (session.tokens !== undefined && to.path === '/sign-in') { + return navigateTo('/'); + } + } catch (e) { + if (to.path !== '/sign-in') { + return navigateTo('/sign-in'); + } + } + }), + { global: true } + ); + } +}); +``` + + + +Make sure you call `Amplify.configure` as early as possible in your application’s life-cycle. A missing configuration or `NoCredentials` error is thrown if `Amplify.configure` has not been called before other Amplify JavaScript APIs. Review the [Library Not Configured Troubleshooting guide](/gen1/[platform]/build-a-backend/troubleshooting/library-not-configured/) for possible causes of this issue. + + + +## Set Up Amplify for API Route Use Cases + +Following the specification of Nuxt, your API route handlers will live under `~/server`, which is a separate environment from other parts of your Nuxt app; hence, the plugins created in the previous sections are not usable here, and extra work is required. + +### Set Up Amplify Server Context Utility + +1. If you haven’t already done so, create a `utils` directory under the server directory of your Nuxt project +2. Create an `amplifyUtils.ts` file under the `utils` directory + +In this file, you will create a helper function to call Amplify APIs that are capable of running on the server side with context isolation. + +Example implementation: + + +```ts title="utils/amplifyUtils.ts" +import type { H3Event, EventHandlerRequest } from 'h3'; +import { + createKeyValueStorageFromCookieStorageAdapter, + createUserPoolsTokenProvider, + createAWSCredentialsAndIdentityIdProvider, + runWithAmplifyServerContext, + AmplifyServer, + CookieStorage +} from 'aws-amplify/adapter-core'; +import { parseAmplifyConfig } from 'aws-amplify/utils'; + +import type { LibraryOptions } from '@aws-amplify/core'; +import config from '~/amplify_outputs.json'; + +const amplifyConfig = parseAmplifyConfig(config); + +const createCookieStorageAdapter = ( + event: H3Event +): CookieStorage.Adapter => { + // `parseCookies`, `setCookie` and `deleteCookie` are Nuxt provided functions + const readOnlyCookies = parseCookies(event); + + return { + get(name) { + if (readOnlyCookies[name]) { + return { name, value: readOnlyCookies[name] }; + } + }, + set(name, value, options) { + setCookie(event, name, value, options); + }, + delete(name) { + deleteCookie(event, name); + }, + getAll() { + return Object.entries(readOnlyCookies).map(([name, value]) => { + return { name, value }; + }); + } + }; +}; + +const createLibraryOptions = ( + event: H3Event +): LibraryOptions => { + const cookieStorage = createCookieStorageAdapter(event); + const keyValueStorage = + createKeyValueStorageFromCookieStorageAdapter(cookieStorage); + const tokenProvider = createUserPoolsTokenProvider( + amplifyConfig.Auth!, + keyValueStorage + ); + const credentialsProvider = createAWSCredentialsAndIdentityIdProvider( + amplifyConfig.Auth!, + keyValueStorage + ); + + return { + Auth: { + tokenProvider, + credentialsProvider + } + }; +}; + +export const runAmplifyApi = ( + // we need the event object to create a context accordingly + event: H3Event, + operation: ( + contextSpec: AmplifyServer.ContextSpec + ) => Result | Promise +) => { + return runWithAmplifyServerContext( + amplifyConfig, + createLibraryOptions(event), + operation + ); +}; +``` + +You can then use `runAmplifyApi` function to call Amplify APIs in an isolated server context. + +#### Usage Example + +Take implementing an API route `GET /api/current-user` , in `server/api/current-user.ts`: + +```ts title="server/api/current-user.ts" +import { getCurrentUser } from 'aws-amplify/auth/server'; +import { runAmplifyApi } from '~/server/utils/amplifyUtils'; + +export default defineEventHandler(async (event) => { + const user = await runAmplifyApi(event, (contextSpec) => + getCurrentUser(contextSpec) + ); + + return user; +}); +``` + +Then you can fetch data from this route, for example, `fetch('http://localhost:3000/api/current-user')`. + +## Set Up Server Middleware to Protect Your API Routes + +Similar to API routes, the previously added auth middleware are not usable under `/server`, hence extra work is required to set up a auth middleware to protect your routes. + +1. If you haven’t already done so, create a `middleware` directory under the `server` directory of your Nuxt project +2. Create an `amplifyAuthMiddleware.ts` file under the `middleware` directory + +This middleware will be executed before a request reach your API route. + +Example implementation: + +```ts title="middleware/amplifyAuthMiddleware.ts" +import { fetchAuthSession } from 'aws-amplify/auth/server'; + +export default defineEventHandler(async (event) => { + if (event.path.startsWith('/api/')) { + try { + const session = await runAmplifyApi(event, (contextSpec) => + fetchAuthSession(contextSpec) + ); + + // You can add extra logic to match the requested routes to apply + // the auth protection + if (session.tokens === undefined) { + setResponseStatus(event, 403); + return { + error: 'Access denied!' + }; + } + } catch (error) { + return { + error: 'Access denied!' + }; + } + } +}); +``` + +With this middleware, when executing `fetch('http://localhost:3000/api/current-user')` without signing in a user on the client side, the `fetch` will receive a 403 error, and the request won’t reach route `/api/current-user`. From 410ee9c14b549e0083360fc68cfcfee7a58e50fd Mon Sep 17 00:00:00 2001 From: Hui Zhao Date: Mon, 13 May 2024 10:47:49 -0700 Subject: [PATCH 2/9] Better storage list example --- .../build-a-backend/server-side-rendering/nuxt/index.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/[platform]/build-a-backend/server-side-rendering/nuxt/index.mdx b/src/pages/[platform]/build-a-backend/server-side-rendering/nuxt/index.mdx index 03efd1aa5ad..ac5f1cd9b77 100644 --- a/src/pages/[platform]/build-a-backend/server-side-rendering/nuxt/index.mdx +++ b/src/pages/[platform]/build-a-backend/server-side-rendering/nuxt/index.mdx @@ -369,14 +369,14 @@ Using the Storage `list` API in `pages/storage-list.vue`: From 5251bea201bf615c32c758ccedc92996a875f051 Mon Sep 17 00:00:00 2001 From: Hui Zhao <10602282+HuiSF@users.noreply.github.com> Date: Mon, 13 May 2024 15:46:58 -0700 Subject: [PATCH 3/9] Update src/pages/[platform]/build-a-backend/server-side-rendering/nuxt/index.mdx Co-authored-by: ashika112 <155593080+ashika112@users.noreply.github.com> --- .../build-a-backend/server-side-rendering/nuxt/index.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/[platform]/build-a-backend/server-side-rendering/nuxt/index.mdx b/src/pages/[platform]/build-a-backend/server-side-rendering/nuxt/index.mdx index ac5f1cd9b77..f7ae7bbc2e1 100644 --- a/src/pages/[platform]/build-a-backend/server-side-rendering/nuxt/index.mdx +++ b/src/pages/[platform]/build-a-backend/server-side-rendering/nuxt/index.mdx @@ -376,7 +376,7 @@ const { data, error } = useAsyncData(async () => { From e7a1dbaa8d749f2e742bddfd64e8c3622720f629 Mon Sep 17 00:00:00 2001 From: Hui Zhao <10602282+HuiSF@users.noreply.github.com> Date: Mon, 13 May 2024 15:57:36 -0700 Subject: [PATCH 4/9] Apply suggestions from code review Co-authored-by: josef --- .../server-side-rendering/nuxt/index.mdx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/pages/[platform]/build-a-backend/server-side-rendering/nuxt/index.mdx b/src/pages/[platform]/build-a-backend/server-side-rendering/nuxt/index.mdx index f7ae7bbc2e1..98f8a8636c3 100644 --- a/src/pages/[platform]/build-a-backend/server-side-rendering/nuxt/index.mdx +++ b/src/pages/[platform]/build-a-backend/server-side-rendering/nuxt/index.mdx @@ -25,19 +25,19 @@ export function getStaticProps(context) { }; } -If you have not already done so, please read the introduction documentation, [Use Amplify Categories APIs in Server Side Rendering](/gen1/[platform]/build-a-backend/server-side-rendering/), to learn about how to use Amplify categories APIs in server-side rendering. +If you have not already done so, please read the introduction documentation, [Use Amplify Categories APIs in Server-Side Rendering](/gen1/[platform]/build-a-backend/server-side-rendering/), to learn about how to use Amplify categories' APIs in server-side rendering. -This documentation provides a getting-started guide to using the generic `runWithAmplifyServerContext` adapter (exported from `aws-amplify/adapter-core`) to enable Amplify Gen2 in a Nuxt 3 project. The examples in this documentation may not present best practices for your Nuxt project. You are welcome to provide suggestions and contributions to improve this documentation or to create a Nuxt adapter package for Amplify and let others use it. +This documentation provides a getting-started guide to using the generic `runWithAmplifyServerContext` adapter (exported from `aws-amplify/adapter-core`) to enable Amplify in a Nuxt 3 project. The examples in this documentation may not present best practices for your Nuxt project. You are welcome to provide suggestions and contributions to improve this documentation or to create a Nuxt adapter package for Amplify and let others use it. -**NOTE:** This guide assumes that you have a deep knowledge of Nuxt 3. +**Note:** This guide assumes that you have a deep knowledge of Nuxt 3. ## Start Using Amplify Gen2 in Your Nuxt 3 project -You can install Amplify Gen2 following this guide, [Manual installation](/[platform]/start/manual-installation/). +You can install relevant Amplify libraries by following the [manual installation](/[platform]/start/manual-installation/) guide. ## Set Up the AmplifyAPIs Plugin @@ -48,7 +48,7 @@ Nuxt 3 offers universal rendering by default, where your data fetching logic may -**NOTE:** the leading number in the files name indicate the plugin loading order, details see https://nuxt.com/docs/guide/directory-structure/plugins#registration-order. The `.client` and `.server` indicate the runtime that the logic contained in the file will run on, client or server. For details see: https://nuxt.com/docs/guide/directory-structure/plugins +**Note:** the leading number in the files name indicate the plugin loading order, details see https://nuxt.com/docs/guide/directory-structure/plugins#registration-order. The `.client` and `.server` indicate the runtime that the logic contained in the file will run on, client or server. For details see: https://nuxt.com/docs/guide/directory-structure/plugins @@ -141,10 +141,10 @@ import type { } from '@aws-amplify/core'; import type { Schema } from '~/amplify/data/resource'; -import config from '../amplify_outputs.json'; +import outputs from '../amplify_outputs.json'; // parse the content of `amplify_outputs.json` into the shape of ResourceConfig -const amplifyConfig = parseAmplifyConfig(config); +const amplifyConfig = parseAmplifyConfig(outputs); // create the Amplify used token cookies names array const userPoolClientId = amplifyConfig.Auth!.Cognito.userPoolClientId; @@ -402,7 +402,7 @@ The above two pages can be rendered on both the client and server sides by defau -Only a subset of Amplify APIs are usable on the server side, and as the libraries evolve, `amplify-apis.client` and `amplify-apis.server` may diverge further. You can guard your API calls to ensure an API is available in the context where you use it. E.g., you can use `if (process.client)` to ensure that a client-only API isn't executed on the server. +Only a subset of Amplify APIs are usable on the server side, and as the libraries evolve, `amplify-apis.client` and `amplify-apis.server` may diverge further. You can guard your API calls to ensure an API is available in the context where you use it (e.g., you can use `if (process.client)` to ensure that a client-only API isn't executed on the server). @@ -414,7 +414,7 @@ The auth middleware will use the plugin set up in the previous step as a depende -**NOTE:** This file will run on both client and server, details see: https://nuxt.com/docs/guide/directory-structure/middleware#when-middleware-runs. The `02` name prefix ensures this plugin loads after the previous so `useNuxtApp().$Amplify` becomes available. +**Note:** This file will run on both client and server, details see: https://nuxt.com/docs/guide/directory-structure/middleware#when-middleware-runs. The `02` name prefix ensures this plugin loads after the previous so `useNuxtApp().$Amplify` becomes available. From 255e1a93d565afc79e38ab63049c5b4f0f8ff2f0 Mon Sep 17 00:00:00 2001 From: Hui Zhao Date: Mon, 13 May 2024 16:28:37 -0700 Subject: [PATCH 5/9] Use camel case --- .../server-side-rendering/nuxt/index.mdx | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/pages/[platform]/build-a-backend/server-side-rendering/nuxt/index.mdx b/src/pages/[platform]/build-a-backend/server-side-rendering/nuxt/index.mdx index 98f8a8636c3..ab11eee1e9e 100644 --- a/src/pages/[platform]/build-a-backend/server-side-rendering/nuxt/index.mdx +++ b/src/pages/[platform]/build-a-backend/server-side-rendering/nuxt/index.mdx @@ -35,7 +35,7 @@ This documentation provides a getting-started guide to using the generic `runWit -## Start Using Amplify Gen2 in Your Nuxt 3 project +## Start Using Amplify in Your Nuxt 3 project You can install relevant Amplify libraries by following the [manual installation](/[platform]/start/manual-installation/) guide. @@ -44,7 +44,7 @@ You can install relevant Amplify libraries by following the [manual installation Nuxt 3 offers universal rendering by default, where your data fetching logic may be executed on both the client and server sides. Amplify offers APIs that are capable of running within a server context to support use cases such as server-side rendering (SSR) and static site generation (SSG), though Amplify's client-side APIs and server-side APIs of Amplify are slightly different. You can set up an `AmplifyAPIs` plugin to make your data fetching logic run smoothly across the client and server. 1. If you haven’t already done so, create a `plugins` directory under the root of your Nuxt project -2. Create two files `01.amplify-apis.client.ts` and `01.amplify-apis.server.ts` under the `plugins` directory +2. Create two files `01.amplifyApis.client.ts` and `01.amplifyApis.server.ts` under the `plugins` directory @@ -54,11 +54,12 @@ Nuxt 3 offers universal rendering by default, where your data fetching logic may In these files, you will register both client-specific and server-specific Amplify APIs that you will use in your Nuxt project as a plugin. You can then access these APIs via the `useNuxtApp` composable. -### Implement `01.amplify-apis.client.ts` +### Implement `01.amplifyApis.client.ts` Example implementation: -```ts title="plugins/01.amplify-apis.client.ts" +```ts title="plugins/01.amplifyApis.client.ts" +import type { Schema } from '~/amplify/data/resource'; import { Amplify } from 'aws-amplify'; import { fetchAuthSession, @@ -68,7 +69,6 @@ import { } from 'aws-amplify/auth'; import { list } from 'aws-amplify/storage'; import { generateClient } from 'aws-amplify/api'; -import type { Schema } from '~/amplify/data/resource'; import config from '../amplify_outputs.json'; const client = generateClient(); @@ -114,12 +114,18 @@ Make sure you call `Amplify.configure` as early as possible in your application -### Implement `01.amplify-apis.server.ts` +### Implement `01.amplifyApis.server.ts` Example implementation: -```ts title="plugins/01.amplify-apis.server.ts" +```ts title="plugins/01.amplifyApis.server.ts" import type { CookieRef } from 'nuxt/app'; +import type { Schema } from '~/amplify/data/resource'; +import type { ListPaginateWithPathInput } from 'aws-amplify/storage'; +import type { + LibraryOptions, + FetchAuthSessionOptions +} from '@aws-amplify/core'; import { createKeyValueStorageFromCookieStorageAdapter, createUserPoolsTokenProvider, @@ -134,12 +140,6 @@ import { } from 'aws-amplify/auth/server'; import { list } from 'aws-amplify/storage/server'; import { generateClient } from 'aws-amplify/api/server'; -import type { ListPaginateWithPathInput } from 'aws-amplify/storage'; -import type { - LibraryOptions, - FetchAuthSessionOptions -} from '@aws-amplify/core'; -import type { Schema } from '~/amplify/data/resource'; import outputs from '../amplify_outputs.json'; @@ -398,7 +398,7 @@ const { data, error } = useAsyncData(async () => { ``` -The above two pages can be rendered on both the client and server sides by default. `useNuxtApp().$Amplify` will pick up the correct implementation of `01.amplify-apis.client.ts` and `01.amplify-apis.server.ts` to use, depending on the runtime. +The above two pages can be rendered on both the client and server sides by default. `useNuxtApp().$Amplify` will pick up the correct implementation of `01.amplifyApis.client.ts` and `01.amplifyApis.server.ts` to use, depending on the runtime. @@ -410,7 +410,7 @@ Only a subset of Amplify APIs are usable on the server side, and as the librarie The auth middleware will use the plugin set up in the previous step as a dependency; therefore you can add the auth middleware via another plugin that will be loaded after the previous one. -1. Create a `02.auth-redirect.ts` file under plugins directory +1. Create a `02.authRedirect.ts` file under plugins directory @@ -418,11 +418,11 @@ The auth middleware will use the plugin set up in the previous step as a depende -### Implement `02.auth-redirect.ts` +### Implement `02.authRedirect.ts` Example implementation: -```ts title="plugins/02.auth-redirect.ts" +```ts title="plugins/02.authRedirect.ts" import { Amplify } from 'aws-amplify'; import config from '~/amplify_outputs.json'; From f9247c6ff8270f445e943b36a58a9a169ca1faa1 Mon Sep 17 00:00:00 2001 From: Hui Zhao Date: Mon, 13 May 2024 16:30:58 -0700 Subject: [PATCH 6/9] rename import config to import outputs --- .../build-a-backend/server-side-rendering/nuxt/index.mdx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pages/[platform]/build-a-backend/server-side-rendering/nuxt/index.mdx b/src/pages/[platform]/build-a-backend/server-side-rendering/nuxt/index.mdx index ab11eee1e9e..c8983947d1e 100644 --- a/src/pages/[platform]/build-a-backend/server-side-rendering/nuxt/index.mdx +++ b/src/pages/[platform]/build-a-backend/server-side-rendering/nuxt/index.mdx @@ -69,7 +69,7 @@ import { } from 'aws-amplify/auth'; import { list } from 'aws-amplify/storage'; import { generateClient } from 'aws-amplify/api'; -import config from '../amplify_outputs.json'; +import outputs from '../amplify_outputs.json'; const client = generateClient(); @@ -424,7 +424,7 @@ Example implementation: ```ts title="plugins/02.authRedirect.ts" import { Amplify } from 'aws-amplify'; -import config from '~/amplify_outputs.json'; +import outputs from '~/amplify_outputs.json'; // Amplify.configure() only needs to be called on the client side if (process.client) { @@ -496,7 +496,7 @@ import { import { parseAmplifyConfig } from 'aws-amplify/utils'; import type { LibraryOptions } from '@aws-amplify/core'; -import config from '~/amplify_outputs.json'; +import outputs from '~/amplify_outputs.json'; const amplifyConfig = parseAmplifyConfig(config); From 583a44b2e8e47856017cb380a843a2394f867bc8 Mon Sep 17 00:00:00 2001 From: Hui Zhao Date: Mon, 13 May 2024 16:33:57 -0700 Subject: [PATCH 7/9] remove extra empty line --- .../build-a-backend/server-side-rendering/nuxt/index.mdx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/[platform]/build-a-backend/server-side-rendering/nuxt/index.mdx b/src/pages/[platform]/build-a-backend/server-side-rendering/nuxt/index.mdx index c8983947d1e..084fc286565 100644 --- a/src/pages/[platform]/build-a-backend/server-side-rendering/nuxt/index.mdx +++ b/src/pages/[platform]/build-a-backend/server-side-rendering/nuxt/index.mdx @@ -379,7 +379,6 @@ const { data, error } = useAsyncData(async () => {

Files under path: album

{{ data }}
- ``` Using the GraphQL API in `pages/todos-list.vue`: From 1ec381fb29e28f86388949e0b42503232c9c946f Mon Sep 17 00:00:00 2001 From: Hui Zhao Date: Mon, 13 May 2024 17:00:48 -0700 Subject: [PATCH 8/9] title cases --- .../server-side-rendering/nuxt/index.mdx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/pages/[platform]/build-a-backend/server-side-rendering/nuxt/index.mdx b/src/pages/[platform]/build-a-backend/server-side-rendering/nuxt/index.mdx index 084fc286565..f97d4a0e771 100644 --- a/src/pages/[platform]/build-a-backend/server-side-rendering/nuxt/index.mdx +++ b/src/pages/[platform]/build-a-backend/server-side-rendering/nuxt/index.mdx @@ -35,11 +35,11 @@ This documentation provides a getting-started guide to using the generic `runWit
-## Start Using Amplify in Your Nuxt 3 project +## Start using Amplify in your Nuxt 3 project You can install relevant Amplify libraries by following the [manual installation](/[platform]/start/manual-installation/) guide. -## Set Up the AmplifyAPIs Plugin +## Set up the AmplifyAPIs plugin Nuxt 3 offers universal rendering by default, where your data fetching logic may be executed on both the client and server sides. Amplify offers APIs that are capable of running within a server context to support use cases such as server-side rendering (SSR) and static site generation (SSG), though Amplify's client-side APIs and server-side APIs of Amplify are slightly different. You can set up an `AmplifyAPIs` plugin to make your data fetching logic run smoothly across the client and server. @@ -357,7 +357,7 @@ export default defineNuxtPlugin({ }); ``` -#### Usage Example +#### Usage example Using the Storage `list` API in `pages/storage-list.vue`: @@ -405,7 +405,7 @@ Only a subset of Amplify APIs are usable on the server side, and as the librarie -## Set Up Auth Middleware to Protect Your Routes +## Set up Auth middleware to protect your routes The auth middleware will use the plugin set up in the previous step as a dependency; therefore you can add the auth middleware via another plugin that will be loaded after the previous one. @@ -472,7 +472,7 @@ Make sure you call `Amplify.configure` as early as possible in your application Following the specification of Nuxt, your API route handlers will live under `~/server`, which is a separate environment from other parts of your Nuxt app; hence, the plugins created in the previous sections are not usable here, and extra work is required. -### Set Up Amplify Server Context Utility +### Set up Amplify server context utility 1. If you haven’t already done so, create a `utils` directory under the server directory of your Nuxt project 2. Create an `amplifyUtils.ts` file under the `utils` directory @@ -565,7 +565,7 @@ export const runAmplifyApi = ( You can then use `runAmplifyApi` function to call Amplify APIs in an isolated server context. -#### Usage Example +#### Usage example Take implementing an API route `GET /api/current-user` , in `server/api/current-user.ts`: @@ -584,7 +584,7 @@ export default defineEventHandler(async (event) => { Then you can fetch data from this route, for example, `fetch('http://localhost:3000/api/current-user')`. -## Set Up Server Middleware to Protect Your API Routes +## Set up server middleware to protect your API routes Similar to API routes, the previously added auth middleware are not usable under `/server`, hence extra work is required to set up a auth middleware to protect your routes. From d7029aedcbfd114a3506f2c52ea3914fa499d181 Mon Sep 17 00:00:00 2001 From: Hui Zhao <10602282+HuiSF@users.noreply.github.com> Date: Tue, 14 May 2024 12:44:53 -0700 Subject: [PATCH 9/9] Apply suggestions from code review Co-authored-by: Ivan Artemiev <29709626+iartemiev@users.noreply.github.com> --- .../server-side-rendering/nuxt/index.mdx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/pages/[platform]/build-a-backend/server-side-rendering/nuxt/index.mdx b/src/pages/[platform]/build-a-backend/server-side-rendering/nuxt/index.mdx index f97d4a0e771..687f3d730c1 100644 --- a/src/pages/[platform]/build-a-backend/server-side-rendering/nuxt/index.mdx +++ b/src/pages/[platform]/build-a-backend/server-side-rendering/nuxt/index.mdx @@ -27,11 +27,11 @@ export function getStaticProps(context) { If you have not already done so, please read the introduction documentation, [Use Amplify Categories APIs in Server-Side Rendering](/gen1/[platform]/build-a-backend/server-side-rendering/), to learn about how to use Amplify categories' APIs in server-side rendering. -This documentation provides a getting-started guide to using the generic `runWithAmplifyServerContext` adapter (exported from `aws-amplify/adapter-core`) to enable Amplify in a Nuxt 3 project. The examples in this documentation may not present best practices for your Nuxt project. You are welcome to provide suggestions and contributions to improve this documentation or to create a Nuxt adapter package for Amplify and let others use it. +This documentation provides a getting started guide to using the generic `runWithAmplifyServerContext` adapter (exported from `aws-amplify/adapter-core`) to enable Amplify in a Nuxt 3 project. The examples in this documentation may not present best practices for your Nuxt project. You are welcome to provide suggestions and contributions to improve this documentation or to create a Nuxt adapter package for Amplify and let others use it. -**Note:** This guide assumes that you have a deep knowledge of Nuxt 3. +**Note:** This guide assumes that you have deep knowledge of Nuxt 3. @@ -41,14 +41,14 @@ You can install relevant Amplify libraries by following the [manual installation ## Set up the AmplifyAPIs plugin -Nuxt 3 offers universal rendering by default, where your data fetching logic may be executed on both the client and server sides. Amplify offers APIs that are capable of running within a server context to support use cases such as server-side rendering (SSR) and static site generation (SSG), though Amplify's client-side APIs and server-side APIs of Amplify are slightly different. You can set up an `AmplifyAPIs` plugin to make your data fetching logic run smoothly across the client and server. +Nuxt 3 offers universal rendering by default, where your data fetching logic may be executed in both client and server runtimes. Amplify offers APIs that are capable of running within a server context to support use cases such as server-side rendering (SSR) and static site generation (SSG), though Amplify's client-side APIs and server-side APIs are slightly different. You can set up an `AmplifyAPIs` plugin to make your data fetching logic run smoothly across the client and server. 1. If you haven’t already done so, create a `plugins` directory under the root of your Nuxt project 2. Create two files `01.amplifyApis.client.ts` and `01.amplifyApis.server.ts` under the `plugins` directory -**Note:** the leading number in the files name indicate the plugin loading order, details see https://nuxt.com/docs/guide/directory-structure/plugins#registration-order. The `.client` and `.server` indicate the runtime that the logic contained in the file will run on, client or server. For details see: https://nuxt.com/docs/guide/directory-structure/plugins +**Note:** the leading number in the filenames indicates the plugin loading order, details see https://nuxt.com/docs/guide/directory-structure/plugins#registration-order. The `.client` and `.server` indicate the runtime that the logic contained in the file will run on, client or server. For details see: https://nuxt.com/docs/guide/directory-structure/plugins @@ -110,7 +110,7 @@ export default defineNuxtPlugin({ -Make sure you call `Amplify.configure` as early as possible in your application’s life-cycle. A missing configuration or `NoCredentials` error is thrown if `Amplify.configure` has not been called before other Amplify JavaScript APIs. Review the [Library Not Configured Troubleshooting guide](/gen1/[platform]/build-a-backend/troubleshooting/library-not-configured/) for possible causes of this issue. +Make sure you call `Amplify.configure` as early as possible in your application’s lifecycle. A missing configuration or `NoCredentials` error is thrown if `Amplify.configure` has not been called before other Amplify JavaScript APIs. Review the [Library Not Configured Troubleshooting guide](/gen1/[platform]/build-a-backend/troubleshooting/library-not-configured/) for possible causes of this issue. @@ -153,7 +153,7 @@ const lastAuthUserCookieName = `CognitoIdentityServiceProvider.${userPoolClientI // create a GraphQL client that can be used in a server context const gqlServerClient = generateClient({ config: amplifyConfig }); -// Extra the model operation function types for creating wrapper function later +// extract the model operation function types for creating wrapper function later type RemoveFirstParam = Params extends [infer _, ...infer Rest] ? Rest : never; type TodoListInput = RemoveFirstParam>; type TodoCreateInput = RemoveFirstParam>; @@ -397,7 +397,7 @@ const { data, error } = useAsyncData(async () => { ``` -The above two pages can be rendered on both the client and server sides by default. `useNuxtApp().$Amplify` will pick up the correct implementation of `01.amplifyApis.client.ts` and `01.amplifyApis.server.ts` to use, depending on the runtime. +The above two pages can be rendered on both the client and server by default. `useNuxtApp().$Amplify` will pick up the correct implementation of `01.amplifyApis.client.ts` and `01.amplifyApis.server.ts` to use, depending on the runtime. @@ -407,7 +407,7 @@ Only a subset of Amplify APIs are usable on the server side, and as the librarie ## Set up Auth middleware to protect your routes -The auth middleware will use the plugin set up in the previous step as a dependency; therefore you can add the auth middleware via another plugin that will be loaded after the previous one. +The auth middleware will use the plugin configured in the previous step as a dependency; therefore you can add the auth middleware via another plugin that will be loaded after the previous one. 1. Create a `02.authRedirect.ts` file under plugins directory