Migrating apollo-server-express to #apollo/server or graphql-yoga - firebase

apollo-server-express and apollo-server-cloud-functions are deprecated, which most tutorials online showing how to set up firebase CF with graphql rely on. I'm hoping someone can help me (and surely others), migrate things so they work with #apollo/server v4, or alternatively using another open source library, such as graphql-yoga.
To begin, here is how I had my server set up until recently:
import { ApolloServer } from "apollo-server-express";
import admin from "firebase-admin";
import express from "express";
import schema from "./schema";
import resolvers from "./resolvers";
import UsersAPI from "./datasources/users";
const serviceAccount = require("../../private/serviceAccount.json");
const databaseURL = "https://my-app.firebaseio.com";
const globalServerApp = admin.initializeApp(
{ credential: admin.credential.cert(serviceAccount), databaseURL },
"server"
);
const globalDataSources = {
usersAPI: new UsersAPI(globalServerApp)
};
const initServer = () => {
const app = express();
const server = new ApolloServer({
typeDefs: schema,
resolvers,
dataSources: () => {
return globalDataSources;
},
uploads: false,
introspection: true,
playground: true,
context: async ({ req }) => {
return {
request: req,
};
},
});
server.applyMiddleware({ app, path: "/", cors: true });
return app;
};
export default initServer;
In addition, I would have this for the users schema:
import { gql } from "apollo-server-express";
const UserSchema = gql`
scalar JSONObject
type Something {
customer: String
}
extend type Query {
doSomething(someIDs: [String]): [Something]
}
extend type Mutation {
completeRegistration(
uid: ID!
first_name: String
last_name: String
invited_by: JSONObject
): Boolean
}
`;
export default UserSchema;
How would you write these using #apollo/server v4, graphql-yoga or maybe another simple way?

The primary differences between the version of apollo-server-express you're using and #apollo/server v4 are:
Package name changes, obviously.
Server implementations have been removed. This was done so that implementations could be community-owned, rather than them having to learn each new thing. Lucky for you express middleware is still in the main package, but now you can use the expressMiddleware wrapper function instead of applyMiddleware.
Playground was deprecated and removed in favor of Apollo's own "Apollo Sandbox". If you prefer GraphQL Playground, there's a plugin that needs to be installed called #apollo/server-plugin-landing-page-graphql-playground. The code below assumes you've installed that.
context became an implementation-specific function, so is no longer on the ApolloServer config. It's now on the expressMiddleware function.
DataSources are no longer in the root config. The return of the context function should now include dataSources if you want them. This should generally be a bit more straightforward.
With all of that put together (though, admittedly, without me having your code, so I can't actually test this thing running), this is what I would expect to work for you:
// These are the new packages
import { ApolloServer } from '#apollo/server';
import { expressMiddleware } from '#apollo/server/express4';
import { ApolloServerPluginLandingPageGraphQLPlayground } from '#apollo/server-plugin-landing-page-graphql-playground';
import admin from 'firebase-admin';
import express from 'express';
import schema from './schema';
import resolvers from './resolvers';
import UsersAPI from './datasources/users';
const serviceAccount = require('../../private/serviceAccount.json');
const databaseURL = 'https://my-app.firebaseio.com';
const globalServerApp = admin.initializeApp({ credential: admin.credential.cert(serviceAccount), databaseURL }, 'server');
const globalDataSources = {
usersAPI: new UsersAPI(globalServerApp),
};
const initServer = () => {
const app = express();
const server = new ApolloServer({
typeDefs: schema,
resolvers,
introspection: true,
plugins: [ApolloServerPluginLandingPageGraphQLPlayground()],
});
app.use(
'/',
expressMiddleware(server, {
context: async ({ req }) => {
return {
request: req,
dataSources: globalDataSources,
};
},
}),
);
return app;
};
export default initServer;

Related

Caching does not seem to work with apollo-server-micro in ApolloGraphql server

I'm following a simple example with NextJS running an Apollo GraphQL server with the package apollo-server-micro. I basically have this:
import { ApolloServer, makeExecutableSchema, } from "apollo-server-micro";
import responseCachePlugin from 'apollo-server-plugin-response-cache';
const typeDefs = require("../../apolloserver/schema");
const { Query } = require("../../apolloserver/resolvers");
const resolvers = {
Query
};
const schema = makeExecutableSchema({ typeDefs, resolvers });
const apolloServer = new ApolloServer({
schema,
context(ctx) {
return {...ctx,prisma};
},
resolvers,
plugins: [responseCachePlugin()]
});
export const config = {
api: {
bodyParser: false,
},
};
export default apolloServer.createHandler({ path: "/api/graphql" });
And when I add the Cache Control to my resolver as follows, I get an error saying
Cannot read property 'cacheControl' of undefined
async function codeCampYears(parent: any, args: any, context: any, info: any) {
console.log(`[${new Date().toLocaleTimeString()}] Query.MyTable`);
info.cacheControl.setCacheHint({ maxAge: 10 });
const data = await context.prisma.MyTable.findMany();

ngx-translate-router Angular Universal SSR : NetworkError at XMLHttpRequest.send

I am implementing ngx-translate-router in an Angular Universal app that is already working in SSR but once I added this module SSR does not run anymore.
But works correctly with ng serve so it means that the integration of this module without SSR is working fine.
But when running in SSR mode : npm run serve:ssr i get this error :
NetworkError
at XMLHttpRequest.send (F:\GitaLab\vyv-angular\dist\server\main.js:1:819512)
at Observable_Observable._subscribe (F:\GitaLab\vyv-angular\dist\server\main.js:1:3285565)
at Observable_Observable._trySubscribe (F:\GitaLab\vyv-angular\dist\server\main.js:1:576303)
at Observable_Observable.subscribe (F:\GitaLab\vyv-angular\dist\server\main.js:1:576085)
at CatchOperator.call (F:\GitaLab\vyv-angular\dist\server\main.js:1:3994238)
at Observable_Observable.subscribe (F:\GitaLab\vyv-angular\dist\server\main.js:1:575939)
at DoOperator.call (F:\GitaLab\vyv-angular\dist\server\main.js:1:3343772)
at Observable_Observable.subscribe (F:\GitaLab\vyv-angular\dist\server\main.js:1:575939)
at F:\GitaLab\vyv-angular\dist\server\main.js:1:3315893
at Observable_Observable._subscribe (F:\GitaLab\vyv-angular\dist\server\main.js:1:3316238)
I implemented the SSR part based on the instructions of ngx-translate-router so what i did additional for the SSR part are the following :
1 - implemented an interceptor in the app.server.module.ts to be able to access the translations within the server part. Here is the interceptor :
import { REQUEST } from '#nguniversal/express-engine/tokens';
import * as express from 'express';
import {Inject, Injectable} from '#angular/core';
import {HttpHandler, HttpInterceptor, HttpRequest} from '#angular/common/http';
#Injectable()
export class TranslateInterceptor implements HttpInterceptor {
private readonly DEFAULT_PORT = 4200;
private readonly PORT = process.env.PORT || this.DEFAULT_PORT;
constructor(#Inject(REQUEST) private request: express.Request) {}
getBaseUrl(req: express.Request) {
const { protocol, hostname } = req;
return this.PORT ?
`${protocol}://${hostname}:${this.PORT}` :
`${protocol}://${hostname}`;
}
intercept(request: HttpRequest<any>, next: HttpHandler) {
if (request.url.startsWith('./assets')) {
const baseUrl = this.getBaseUrl(this.request);
request = request.clone({
url: `${baseUrl}/${request.url.replace('./assets', 'assets')}`
});
}
return next.handle(request);
}
}
2 - I modified the server.ts to access the different locales and added the routes for them, but I think the issues lies there. I think I incorrectly added the routes listening in the server.ts but i did not find help about this anywhere...
here is the server.ts
// The Express app is exported so that it can be used by serverless Functions.
export function app(): express.Express {
const server = express();
const distFolder = join(process.cwd(), 'dist/browser');
const fs = require('fs');
const data: any = JSON.parse(fs.readFileSync(`src/assets/locales.json`, 'utf8'));
const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index';
server.use(cookieParser());
// Our Universal express-engine (found # https://github.com/angular/universal/tree/master/modules/express-engine)
server.engine('html', ngExpressEngine({
bootstrap: AppServerModule,
}));
server.set('view engine', 'html');
server.set('views', distFolder);
server.get('/', (req, res) => {
const defaultLang = 'en';
const lang = req.acceptsLanguages('en', 'de', 'fr', 'es', 'pt');
let cookieLang = req.cookies.lang;
if (!cookieLang) {
cookieLang = req.cookies.LOCALIZE_DEFAULT_LANGUAGE; // This is the default name of cookie
}
const definedLang = cookieLang || lang || defaultLang;
console.log('domain requested without language');
res.redirect(301, `/${definedLang}/`);
});
// Example Express Rest API endpoints
// server.get('/api/**', (req, res) => { });
// Serve static files from /browser
server.get('*.*', express.static(distFolder, {
maxAge: '1y'
}));
console.log('routes for the locales:');
console.log(data);
data.locales.forEach(route => {
server.get(`/${route}`, (req: express.Request, res: express.Response) => {
console.log('domain requested with language' + req.originalUrl);
res.render(indexHtml, {
req, providers: [
{ provide: REQUEST, useValue: req }
]
});
});
server.get(`/${route}/*`, (req: express.Request, res: express.Response) => {
console.log('page requested with language ' + req.originalUrl);
res.render(indexHtml, {
req, providers: [
{ provide: REQUEST, useValue: req }
]
});
});
});
return server;
}
function run(): void {
const port = process.env.PORT || 4000;
// Start up the Node server
const server = app();
server.use(compression());
server.listen(port, () => {
console.log(`Node Express server listening on http://localhost:${port}`);
});
}
// Webpack will replace 'require' with '__webpack_require__'
// '__non_webpack_require__' is a proxy to Node 'require'
// The below code is to ensure that the server is run only when not requiring the bundle.
declare const __non_webpack_require__: NodeRequire;
const mainModule = __non_webpack_require__.main;
const moduleFilename = mainModule && mainModule.filename || '';
if (moduleFilename === __filename || moduleFilename.includes('iisnode')) {
run();
}
export * from './src/main.server';
when I start the SSR server and then request the page http://localhost
I can see the redirect working to the default language and the console logs the "domain requested with language /en/" before the error reported above.
I think the issue is that the server.ts does not manage to map the requested url to something within the routes declared in the app-routing.module.ts but i don't know how to do that.
In the GitHub repository of ngx-translate-router they say :
// let node server knows about the new routes:
let fs = require('fs');
let data: any = JSON.parse(fs.readFileSync(`src/assets/locales.json`, 'utf8'));
app.get('/', ngApp);
data.locales.forEach(route => {
app.get(`/${route}`, ngApp);
app.get(`/${route}/*`, ngApp);
});
but they don't describe what "ngApp" is so i just extrapolated it base on how the server.ts was before integrating this plugin:
// All regular routes use the Universal engine
server.get('*', (req: express.Request, res: express.Response) => {
res.render(indexHtml, {
req, providers: [
{ provide: REQUEST, useValue: req }
]
});
});
So my question is double. Do you think I am right to keep searching on the direction that I dentified ? (server.ts implementation is wrong).
If yes do you have an idea how to correct it ?
If no, any other direction to look for ?
The error was not in the server.ts, but the interceptor. I originally designed this interceptor and I thought it could be shared just like that to retrieve the data from ./assets/locales.json as it was working fine in SSR for retrieving the ./assets/i18n/en.json, but no luck. ngx-translate and ngx-translate-router cannot share the same interceptor, I don't know why but that's it. So I had to create a second interceptor (code below) and this solved my issue.
import { REQUEST } from '#nguniversal/express-engine/tokens';
import * as express from 'express';
import {Inject, Injectable, PLATFORM_ID} from '#angular/core';
import {HttpHandler, HttpInterceptor, HttpRequest} from '#angular/common/http';
import {isPlatformServer} from '#angular/common';
#Injectable()
export class LocalizeInterceptor implements HttpInterceptor {
constructor(#Inject(REQUEST) private request: express.Request, #Inject(PLATFORM_ID) private platformId: any) {}
intercept(request: HttpRequest<any>, next: HttpHandler) {
if (request.url.startsWith('assets') && isPlatformServer(this.platformId)) {
const req = this.request;
const url = req.protocol + '://' + req.get('host') + '/' + request.url;
request = request.clone({
url
});
}
return next.handle(request);
}
}

Next JS api routes with type graphql error (resolver is not a function)

I wanted to set-up a new Next JS project with graphql using TypeGraphQL. I thought it is a good idea to skip stand alone server and use API routes provided by Next JS. Here is an example I was inspired by. Unfortunately I can't get graphql up and running.
Next JS starts without errors but it throws one right after sending request to api/graphql
TypeError: resolver is not a function
at apiResolver (/home/vavra/Projects/project-united/node_modules/next/dist/next-server/server/api-utils.js:6:7)
at DevServer.handleApiRequest (/home/vavra/Projects/project-united/node_modules/next/dist/next-server/server/next-server.js:43:427)
at async Object.fn (/home/vavra/Projects/project-united/node_modules/next/dist/next-server/server/next-server.js:35:764)
at async Router.execute (/home/vavra/Projects/project-united/node_modules/next/dist/next-server/server/router.js:28:28)
at async DevServer.run (/home/vavra/Projects/project-united/node_modules/next/dist/next-server/server/next-server.js:44:494)
at async DevServer.handleRequest (/home/vavra/Projects/project-united/node_modules/next/dist/next-server/server/next-server.js:13:133)
Here is my file src/pages/api/graphql
import { Resolver, Query } from "type-graphql";
import { ApolloServer } from "apollo-server-micro";
import { buildSchema } from "type-graphql";
import "reflect-metadata";
#Resolver()
class HelloResolver {
#Query(() => String)
hello() {
return "hello";
}
}
export const config = {
api: {
bodyParser: false,
},
};
export default (async () => {
const schema = await buildSchema({
resolvers: [HelloResolver],
});
const apolloServer = new ApolloServer({ schema });
return apolloServer.createHandler({ path: "/api/graphql" });
})();
Any help with this?
here they were talking about a similar problem, and with this form I work nextjs with type-graphql.
pages/api/graphql
import 'reflect-metadata';
import { ApolloServer } from 'apollo-server-micro';
import { buildSchema } from 'type-graphql';
import youResolver from '../resolvers/youResolver';
import { NextApiRequest, NextApiResponse } from 'next';
let apolloServerHandler: (req: any, res: any) => Promise<void>;
const getApolloServerHandler = async () => {
if (!apolloServerHandler) {
const schema = await buildSchema({
resolvers: [youResolver],
});
apolloServerHandler = new ApolloServer({ schema }).createHandler({
path: '/api/graphql',
});
}
return apolloServerHandler;
};
export default async (req: NextApiRequest, res: NextApiResponse) => {
const apolloServerHandler = await getApolloServerHandler();
return apolloServerHandler(req, res);
};
export const config = { api: { bodyParser: false } };

React Redux Firebase: Error on firebaseConnect - Cannot read property 'ordered' of undefined

I followed the example in the documentation under v2.0.0 > Read Me > Load Data (listeners automatically managed on mount/unmount) (direct link is not possible).
And replaced the connect call with the firestore specific one shown here](http://react-redux-firebase.com/docs/firestore.html#examples) in Example 1.
I copied the Todo example exactly in a new component created for testing purposes.
Todo Component:
import React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { compose } from 'redux'
import { firebaseConnect,firestoreConnect, isLoaded, isEmpty } from 'react-redux-firebase'
const Todos = ({ todos, firebase }) => {
// Build Todos list if todos exist and are loaded
const todosList = !isLoaded(todos)
? 'Loading'
: isEmpty(todos)
? 'Todo list is empty'
: Object.keys(todos).map(
(key, id) => (
<TodoItem key={key} id={id} todo={todos[key]}/>
)
)
return (
<div>
<h1>Todos</h1>
<ul>
{todosList}
</ul>
<input type="text" ref="newTodo" />
<button onClick={this.handleAdd}>
Add
</button>
</div>
)
}
// export default compose(
// firestoreConnect([
// 'todos' // { path: '/todos' } // object notation
// ]),
// connect((state) => ({
// todos: state.firestore.data.todos,
// profile: state.firestore.profile // load profile
// }))
// )(Todos)
export default compose(
firestoreConnect(['todos']), // or { collection: 'todos' }
connect((state, props) => ({
todos: state.firestore.ordered.todos
}))
)(Todos)
The store configuration was configured as shown here in the docs. The store configuration was adapted to slot into the framework created by react-boilerplate.
/**
* Create the store with dynamic reducers
*/
import { createStore, applyMiddleware, compose } from 'redux'
import { fromJS } from 'immutable'
import { routerMiddleware } from 'connected-react-router/immutable'
import createSagaMiddleware from 'redux-saga'
import { reactReduxFirebase, firebaseReducer } from 'react-redux-firebase'
import { reduxFirestore, firestoreReducer } from 'redux-firestore'
import firebase from 'firebase/app'
import 'firebase/auth'
import 'firebase/database'
import 'firebase/firestore'
import createReducer from './reducers'
const sagaMiddleware = createSagaMiddleware()
const firebaseConfig = {
apiKey: process.env.FIREBASE_API_KEY,
authDomain: process.env.AUTH_DOMAIN,
databaseURL: process.env.DATABASE_URL,
projectId: process.env.PROJECT_ID,
storageBucket: process.env.STORAGE_BUCKET,
messagingSenderId: process.env.MESSAGING_SENDER_ID,
}
const rrfConfig = {
userProfile: 'users',
// useFirestoreForProfile: true, // Firestore for Profile instead of Realtime DB
// attachAuthIsReady: true
}
// Initialize Cloud Firestore through Firebase
export default function configureStore(initialState = {}, history) {
firebase.initializeApp(firebaseConfig)
// Initialize Firestore with timeshot settings
firebase.firestore()
// firebase.firestore().settings({ timestampsInSnapshots: true })
// Create the store with two middlewares
// 1. sagaMiddleware: Makes redux-sagas work
// 2. routerMiddleware: Syncs the location/URL path to the state
const middlewares = [sagaMiddleware, routerMiddleware(history)]
const enhancers = [
applyMiddleware(...middlewares),
// reactReduxFirebase(config), // enhancing our store with these packages
// reduxFirestore(config)
]
// If Redux DevTools Extension is installed use it, otherwise use Redux compose
/* eslint-disable no-underscore-dangle, indent */
const composeEnhancers =
process.env.NODE_ENV !== 'production' &&
typeof window === 'object' &&
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({})
: compose
/* eslint-enable */
const createStoreWithFirebase = compose(
reactReduxFirebase(firebase, rrfConfig), // firebase instance as first argument
reduxFirestore(firebase),
)(createStore)
const store = createStoreWithFirebase(
createReducer(),
fromJS(initialState),
composeEnhancers(...enhancers),
)
// Extensions
store.runSaga = sagaMiddleware.run
store.injectedReducers = {} // Reducer registry
store.injectedSagas = {} // Saga registry
// Make reducers hot reloadable, see http://mxs.is/googmo
/* istanbul ignore next */
if (module.hot) {
module.hot.accept('./reducers', () => {
store.replaceReducer(createReducer(store.injectedReducers))
})
}
return store
}
I traced and verified my store configuration exactly to make sure all steps present in the documentation are configured correctly in my configuration.
My createReducer funciton is in a seperate file and you can see that I added the firebaseReducer and firebaseReducer correctly.
import { combineReducers } from 'redux-immutable'
import { connectRouter } from 'connected-react-router/immutable'
import { firebaseReducer } from 'react-redux-firebase'
import { firestoreReducer } from 'redux-firestore'
import history from 'utils/history'
import languageProviderReducer from 'containers/LanguageProvider/reducer'
export default function createReducer(injectedReducers = {}) {
const rootReducer = combineReducers({
firebase: firebaseReducer,
firestore: firestoreReducer,
language: languageProviderReducer,
...injectedReducers,
})
// Wrap the root reducer and return a new root reducer with router state
const mergeWithRouterState = connectRouter(history)
return mergeWithRouterState(rootReducer)
}
My redux store contains the firestore and firebase and it is injected into the component props.
What does not work is the use of connectFirestore HoC to automatically retrieve and inject a list of documents in to the component.
This is the error message:
react-dom.development.js?61bb:20266 Uncaught TypeError: Cannot read property 'ordered' of undefined
at Function.eval [as mapToProps] (index.js?d834:49)
at mapToPropsProxy (wrapMapToProps.js?1817:54)
at Function.detectFactoryAndVerify (wrapMapToProps.js?1817:63)
at mapToPropsProxy (wrapMapToProps.js?1817:54)
at handleFirstCall (selectorFactory.js?805c:37)
at pureFinalPropsSelector (selectorFactory.js?805c:85)
at Object.runComponentSelector [as run] (connectAdvanced.js?48b8:43)
at Connect.initSelector (connectAdvanced.js?48b8:195)
at new Connect (connectAdvanced.js?48b8:136)
at constructClassInstance (react-dom.development.js?61bb:11315)
(Snipped from my code which is the example 1 in documentation):
export default compose(
firestoreConnect(['todos']), // or { collection: 'todos' }
connect((state, props) => ({
todos: state.firestore.ordered.todos
}))
)(Todos)
I inspected the state variable and it does contain the firestore attribute. This attribute contains a number of functions, as expected, but it is missing the query results under "ordered", which is undefined.
I have tried all different ways to use firestoreconnect e.g. using a Class-based component, using a query with parameters, etc. and all give the same error.
My Firebase project is configured correct as I am able to create documents inside collections. A todos collection for testing purposes is present as well containing 2 documents.
I have come across this post, which mentions the following:
If you just upgraded to React-Redux v6, it's because react-redux-firebase is not compatible with v6.
See https://github.com/prescottprue/react-redux-firebase/issues/581 for details.
This does not apply to me because I am using react-redux version 5. Here are the versions I am using:
"firebase": "^5.10.1",
"react-redux": "^5.0.7",
"react-redux-firebase": "^2.2.6",
"redux": "^4.0.1",
"redux-firestore": "^0.7.3",
I have spent a significant amount of time on this. Like I said, using firestore to add new data to collections works fine. It is just this HoC business that is failing no matter how i approach the solution.
any help would be appreciated.
Never solved this. I guess it is related to incompatible versions. What I ended up doing is download v4 of react-boilerplate and set up v3 react-redux-firebase which uses the Context API as opposed to store enhancers. Now works very well.

vuex not loading module decorated with vuex-module-decorators

I get this error when trying to load a store module with the vuex-module-decorators into the initialiser:
vuex.esm.js?2f62:261 Uncaught TypeError: Cannot read property
'getters' of undefined at eval (vuex.esm.js?2f62:261) at Array.forEach
() at assertRawModule (vuex.esm.js?2f62:260) at
ModuleCollection.register (vuex.esm.js?2f62:186) at eval
(vuex.esm.js?2f62:200) at eval (vuex.esm.js?2f62:75) at Array.forEach
() at forEachValue (vuex.esm.js?2f62:75) at ModuleCollection.register
(vuex.esm.js?2f62:199) at new ModuleCollection (vuex.esm.js?2f62:160)
The index.ts file is quite simple and all works until i introduce the modules to the initialiser:
import Vue from 'vue';
import Vuex from 'vuex';
import { AuthenticationModule, IAuthenticationState } from './modules/authentication';
import VuexPersistence from 'vuex-persist';
Vue.use(Vuex);
export interface IRootState {
authentication: IAuthenticationState;
}
const vuexLocal = new VuexPersistence({
storage: window.localStorage,
});
const store = new Vuex.Store<IRootState>({
modules: {
authentication: AuthenticationModule, // if this is not here it works but this will mean the vuex-persist will not work
},
plugins: [vuexLocal.plugin],
});
export default store;
Here is the authentication module i believe is throwing the error:
import { Action, getModule, Module, Mutation, VuexModule } from 'vuex-module-decorators';
import { Generic422, LoginEmailPost, RegisterPost, TokenRenewPost, User, UserEmailPut, UserPasswordPut, UserPut } from '#/api/ms-authentication/model/models';
import { Api } from '#/services/ApiHelper';
import Auth from '#/services/Auth';
import store from '#/store';
export interface IAuthenticationState {
user: User;
authenticated: boolean;
prompt: {
login: boolean,
};
errorRegister: Generic422;
errorLogin: Generic422;
}
const moduleName = 'authentication';
#Module({dynamic: true, store, name: moduleName})
class Authentication extends VuexModule implements IAuthenticationState
{
public authenticated: boolean = false;
public errorRegister: Generic422 = {};
public errorLogin: Generic422 = {};
public prompt = {
login: false,
};
public user: User = {
email: '',
firstName: '',
lastName: '',
birthday: '',
verified: false,
};
#Action({commit: 'SET_USER'})
public async login(data: LoginEmailPost) {
try {
const resp = await Api.authenticationApi.v1LoginEmailPost(data);
Auth.injectAccessJWT(resp.data.tokenAccess.value);
Auth.injectRenewalJWT(resp.data.tokenRenewal.value);
return resp.data.user;
} catch (e) {
return e.statusCode;
}
}
#Mutation
public SET_USER(user: User) {
this.authenticated = true;
this.user = {...this.user, ...user};
}
}
export const AuthenticationModule = getModule(Authentication);
I took this setup from: https://github.com/calvin008/vue3-admin
I don't know if this is a bug or if this is a setup issue but completely stuck here as I intend to use the vuex-persist here to "rehydrate" the store after page reload.
Another completely different way of declaring the stores with this lib was here: https://github.com/eladcandroid/typescript-vuex-example/blob/master/src/components/Profile.vue but the syntax seems it would get wildly verbose when in the vue3-admin it is alll neatly in the store opposed to the component.
Currently I have all the state nicely persisted to local storage but I have no idea due to this error or and lack of exmaple how rehydrate the store with this stored data :/
It seems there are two ways to use the decorators but both are quite different. I like the method i have found from the vie admin as the components are nice and clean, but i cannot inject the modules as https://www.npmjs.com/package/vuex-persist#detailed states should be done :/
I found the answer to be the example vue admin app was not structured quite correctly.
Instead to export the class from the module:
#Module({ name: 'authentication' })
export default class Authentication extends VuexModule implements IAuthenticationState {
Then inject the class as a module into the index and export the module via the decorator but also inject the store into the said decorator:
import Vue from 'vue';
import Vuex from 'vuex';
import Authentication from './modules/authentication';
import VuexPersistence from 'vuex-persist';
import { getModule } from 'vuex-module-decorators';
Vue.use(Vuex);
const vuexLocal = new VuexPersistence({
storage: window.localStorage,
});
const store = new Vuex.Store({
modules: {
authentication: Authentication,
},
plugins: [vuexLocal.plugin],
});
export default store;
export const AuthenticationModule = getModule(Authentication, store);
The result is it all works.

Resources