-
Notifications
You must be signed in to change notification settings - Fork 2.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
AppSync, AWS Amplify and SSR #1613
Comments
We have a tutorial published for this with Next.js: https://medium.com/open-graphql/ssr-graphql-apps-with-next-js-aws-appsync-eaf7fbeb1bde |
Well, in this example, you re not using amplify. I will migrate all this with apollo and appsync, but for now, I just want amplify with SSR. Is this possible ? |
The answer is : no, you can't. |
@romainquellec Can you elaborate? I am considering building an AppSync react native app for a client and an admin interface to manage things is a requirement. A Next.js Amplify AppSync admin client "backend" would be ideal. Is Amplify with Next.js really not possible? Seems like people might have gotten amplify working with SSR by using cookie authentation and an isomorphic fetch: #493 (comment) Are there problems beyond that? |
i only want seo, can amplify work with sth like prerender.io? |
Any luck with amplify and next.js? @Enalmada @romainquellec |
I cant help, I'm now using apollo and appsync for the GraphQL part. |
Its a real shame that nextjs is not compatible with AWS Amplify, its a massive short fall in my eyes. |
I'm having the same issue. Maybe it would it be possible/easy to trigger prerendering of routes from a lambda function ? Perhaps using PrerenderSPAPlugin ? |
The specific GraphQL part was not SSR compatible (months ago).
is OK. |
And you still could load some components with component with React if something is broken. |
I'm using Amplify with Appsync and Nextjs and it works pretty ok. I had a setup where I use Api key on server and Cognito user pool on client. SSR is for public, anonymous user so you don't need to handle authentication etc. For all the protected pages that require user login, I render it on client. |
Closing this. There is (at least) one issue on SSR improvement for aws-amplify. |
I'm not sure why this issue was closed. This is an ongoing issue that I think a lot of people will stumble upon as nextjs and amplify grow in popularity. |
I agree this shouldn't be closed.. I, along with many others are holding our breath for amplify support for server rendered apps. |
@skdigital what are you looking for in particular? I have a nextjs workaround that I think is robust, although the workaround is just to deter Amplify from doing anything on the server |
I am looking to use nextjs and host via zeit now. Use only Amplify for Auth, Storage, and Appsync realtime DB graphql with offline functionality. Is this possible and advisable for production? (hosting somewhere else, so we can use nextjs)? |
I would highly recommend building a test app, first, that attempts the integrations you plan on using. If you'd like, I can create an open source example to show my integration strategies. I'm using react. The only problem I've had, so far, is Amplify Auth + next. I have used Storage and Api(Appsync - not realtime) without any issues. All of my Api calls are coming from the client, so I'm not sure if Api/Appsync will work on the server, especially if the calls require authentication. One of the main frustrations has been that the Amplify team does not currently fully support not-breaking-SSR** in the Amplify framework. However, the team is also extraordinarily helpful and responsive, they just have not been responsive on the SSR issue; I expect that to change as more people request SSR compatibility. Also, because amplify is an open source project provided by AWS, it seems that the official AWS support center does not actually provide support -- this policy, to me, does not make sense, but the lack of official support is worth considering for a production environment. The other frustration is that next/now/zeit team is not very responsive or supportive. The software is convenient and worth it, but when it breaks it can be hard to find a fix. Thus, I highly recommend a barebones test app before integrating your entire app (as I tried to do). **By not-breaking-ssr, I mean that the framework does not need to actual render on the server-side, but it should not break server-side rendering. Currently, simply importing certain Amplify components (eg. |
@romainquellec , I also think you should keep this ticket open. There's a lot you can do with Amplify But well supported, well documented, production ready Server Side is a must for a lot of us. Would love to hear from @undefobj if this is in the roadmap, he's usually very responsive. |
SSR support is on our roadmap, and we'd like to do it this year but for transparency it's not in the next few months. It will require a fair amount of work on the internals and potentially some breaking changes so we would like to do it as part of a larger release this summer/fall with other changes. That being said if anyone in the community wishes to fork Amplify and try to get it working, then give feedback on what was required to get things working (even if it's a PR we don't merge) then that would help us accelerate the work. I've reopened this issue for tracking. |
@mlabieniec I added the line for global.navigator in my next.config.js which seems to have fixed my running errors. However, I get that same navigator issue when I try to build my project. |
@gtinkham there is a fix for that in the latest amplify which you can get with Seeing if there is anything we can do to figure out a work-around for this in the meantime, next also has an RFC related to better support for this here: vercel/next.js#8626 |
@gtinkham You can fix the build by using the following const withCSS = require('@zeit/next-css')
const resolve = require('resolve')
global.navigator = () => null
module.exports = withCSS({
webpack (config, options) {
const { dir, isServer } = options
config.externals = []
if (isServer) {
config.externals.push((context, request, callback) => {
resolve(request, { basedir: dir, preserveSymlinks: true }, (err, res) => {
if (err) {
return callback()
}
if (
res.match(/node_modules[/\\].*\.css/)
&& !res.match(/node_modules[/\\]webpack/)
&& !res.match(/node_modules[/\\]@aws-amplify/)
) {
return callback(null, `css ${request}`)
}
callback()
})
})
}
return config
}
}); |
@undefobj are there any updates on this with regards to official support? |
We want SSR! ✊ |
Basically, Web application should be optimized SEO. |
Would be good to get opinions on this... May seem slightly hacky but I've managed to at least get the Auth category storage running on SSR... From what I can tell, a great step forward for SSR in this library would be allowing us to universally configure the data storage across all categories. for example, in @aws-amplify/core package, storage is configured to use either window.localstorage and if no window.localstorage is available (i.e. on the server) it defaults to just using a js object. Would be great to be able to override this to use cookie storage instead? as we could then use universal cookies on the browser and server in nextJS. Currently, I'm doing something like this (very rough and extremely hacky until https://github.com/aws-amplify/amplify-js/blob/master/packages/core/src/StorageHelper/index.ts is allowed to pass storage object in: Basically I'm overwriting window.localstorage to a custom storage object, configuring amplify to use that storage object then removing it such that it doesn't mess other dependency configs, etc. This is done on both the server and browser whilst bootstrapping Amplify. I also need to add config.storage to my config, as the packages/amazon-cognito-identity-js which seems to do a lot of the underlying heavy lifting for the auth category takes a storage parameter (see https://github.com/aws-amplify/amplify-js/blob/master/packages/amazon-cognito-identity-js/src/CognitoUserPool.js ) My storage class then uses a universal client and server-side cookie library called nookies in nextJS. configureAmplify.js import config from '@/amplify/aws-exports'
import Amplify from '@aws-amplify/core'
import Auth from '@aws-amplify/auth'
import API from '@aws-amplify/api'
import Storage from '@aws-amplify/storage'
import { UniversalCookieStorage } from '@/lib/cookieStorage'
import { NextPageContext } from 'next'
export default (ctx?: NextPageContext)=> {
if(ctx) UniversalCookieStorage.setContext(ctx)
let isServer = false
let localStorage = null
//@ts-ignore
config.storage = UniversalCookieStorage
if(typeof window === 'undefined') {
//@ts-ignore
global.window = {
localStorage: UniversalCookieStorage
}
isServer = true
}
else {
localStorage = window.localStorage
Object.defineProperty(window, 'localStorage', {
value: UniversalCookieStorage,
configurable: true,
enumerable: true,
writable: false
})
}
Amplify.configure(config)
//Amplify.Logger.LOG_LEVEL = 'ERROR'
Auth.configure(config)
Storage.configure(config)
API.configure(config)
if(isServer) {
//@ts-ignore
global.window = undefined
}
else {
Object.defineProperty(window, 'localStorage', {
value: localStorage,
configurable: true,
enumerable: true,
writable: false
})
}
} cookieStorage.ts import { NextPageContext } from 'next'
import { setCookie, parseCookies, destroyCookie } from 'nookies'
export class UniversalCookieStorage {
public static ctx: NextPageContext | null = null
public static maxAge = 30 * 24 * 60 * 60
public static setContext(ctx: NextPageContext) {
UniversalCookieStorage.ctx = ctx
}
/**
* This is used to set a specific item in storage
* @param {string} key - the key for the item
* @param {object} value - the value
* @returns {string} value that was set
*/
public static setItem(key: string, value: any) {
if(typeof value != 'string' && typeof value == 'boolean' && typeof value == 'number') {
value = JSON.stringify(value)
}
setCookie(UniversalCookieStorage.ctx, key, value, {
maxAge: UniversalCookieStorage.maxAge,
path: '/',
})
}
/**
* This is used to get a specific key from storage
* @param {string} key - the key for the item
* This is used to clear the storage
* @returns {string} the data item
*/
public static getItem(key: string) {
const cookies = parseCookies(UniversalCookieStorage.ctx)
// if(cookies[key] == null) throw new Error(`${key} does not exist in cookies`)
return cookies[key]
}
/**
* This is used to remove an item from storage
* @param {string} key - the key being set
* @returns {string} value - value that was deleted
*/
public static removeItem(key: string) {
const cookies = parseCookies(UniversalCookieStorage.ctx)
// if(cookies[key] == null) throw new Error(`${key} does not exist in cookies`)
destroyCookie(UniversalCookieStorage.ctx, key, {
path: '/'
})
return cookies[key]
}
/**
* This is used to clear the storage
* @returns {string} nothing
*/
public static clear() {
const cookies = parseCookies(UniversalCookieStorage.ctx)
const keys = Object.keys(cookies)
keys.forEach((key)=> {
destroyCookie(UniversalCookieStorage.ctx, key, {
path: '/'
})
})
return {}
}
}
example component with SSR rendering for a user (i.e. show authenticated page otherwise redirect user) import React, { useState, useEffect } from 'react'
import Auth from '@aws-amplify/auth'
import configureAmplify from '@/lib/configureAmplify'
interface Props {
isLoggedIn: boolean
}
const Example: React.FC<Props> = (props)=> {
if(props.isLoggedIn) {
return <div>Secret content</div>
}
else {
return <div>need to log in!</div>
}
}
//@ts-ignore
Example.getInitialProps = async (ctx: NextPageContext)=> {
configureAmplify(ctx)
let user = null
let isLoggedIn = false
try {
user = await Auth.currentAuthenticatedUser()
if(user != null) isLoggedIn = true
}
catch(e) {
// console.error(e)
}
return {
isLoggedIn
}
}
export default Example and lastly, _app.js import React from 'react'
import App, { AppInitialProps, AppProps, AppContext } from 'next/app'
import configureAmplify from '@/lib/configureAmplify'
interface MyAppProps extends AppProps, InitialPageLoadAppProps {
}
interface InitialPageLoadAppProps extends AppInitialProps {
}
interface MyAppState {
}
class MyApp extends App<MyAppProps, {}, MyAppState> {
// Only uncomment this method if you have blocking data requirements for
// every single page in your application. This disables the ability to
// perform automatic static optimization, causing every page in your app to
// be server-side rendered.
// public static async getInitialProps(appContext: AppContext) {
// //server-side auth checks and redirects here
// // calls page's `getInitialProps` and fills `appProps.pageProps`
// const appProps = await App.getInitialProps(appContext)
// return { ...appProps }
// }
public constructor(props: MyAppProps) {
super(props)
this.state = {
}
configureAmplify()
}
public async componentDidMount() {
try {
this.setState({
...this.state,
})
}
catch(e) {
console.error(e)
}
}
public render() {
const { Component, pageProps } = this.props
return <Component {...pageProps} />
}
}
export default MyApp package.json dependencies: {
"@aws-amplify/api": "3.1.3",
"@aws-amplify/auth": "3.2.0",
"@aws-amplify/core": "3.2.0",
"@aws-amplify/storage": "3.1.3",
} I haven't played around with the API library and the above as yet but if we can get it to play nice, at least that will let one use SSR for the auth and API categories.... which is my mind will be most of the use cases for SSR anyway. Interested to hear feedback. |
@undefobj, do you think SSR could become available next few months? thank you |
We don't have any specific timelines but we are actively looking into this. |
Hi, I'm looking for adding Next.js to my apps with Amplify. I don't know if this example works only with create-react-app: https://github.com/aws-samples/aws-amplify-auth-starters/tree/react because it does not use localstorage manually, it just uses the methods that Amplify auth offers, I don't know if these methods are the enough smart to detect if the app is using ssr or not, even with ssg. Algo what @danielblignaut is showing in his example would work great, it's very similar to this: https://spectrum.chat/next-js/general/amplify-auth-and-next-js~b7ef99fe-d6bf-459d-8dd3-d5a04fcbaf89 I don't know if you used it as a reference. Also, Next.js 9.3 has new methods that are more powerful than
Maybe these methods can help to bring this functionality as soon as possible :) |
Just deploy to now. It works perfectly with serverless functions & SSR as it's built by zeit (nextjs). It uses AWS Lambda behind the scenes. |
why it's not compatible? |
can you provide and example of app or index file? |
For reference, this is in |
@undefobj Hopefully, the SSR implementation will be framework agnostic so it can also be used with nuxtjs |
This has been addressed as part of #6146! Please see my synopsis on changes here: #5435 (comment) |
This issue has been automatically locked since there hasn't been any recent activity after it was closed. Please open a new issue for related bugs. Looking for a help forum? We recommend joining the Amplify Community Discord server |
AppSync, AWS Amplify and SSR :
Hi ! Is Amplify (GraphQL Client) is SSR compatible ? Can't find any information on docs right now. Thanks !
The text was updated successfully, but these errors were encountered: