How to access Nuxt 3 runtimeConfig in service file? - vuejs3

I have service file like this .
service/http.ts
import axios from 'axios'
const config = useRuntimeConfig()
const http = axios.create({
baseURL: config.public.apiUrl,
})
export default http
and nuxt.config.js like this
export default defineNuxtConfig({
runtimeConfig: {
public: {
apiUrl: 'some value',
}
},
});
and .env like this
NUXT_API_URL=http://www.a.com/
and I want to access apiUrl here .
But it gives me an error.
also if I use process.env.NUXT_API_URL .
it gives an error again
so, how can I access env variable for my services file?

Hm, I'm not sure but you may try that one.
import axios from 'axios'
export default () => {
const config = useRuntimeConfig()
axios.create({
baseURL: config.public.apiUrl,
})
}
useRuntimeConfig() needs to be called inside of the function.

Related

how to access Vuex store from Axios config file?

I'm using Vue 3 composition and Vuex
this is axios config file
http.js
import axios from "axios";
import useStore from "#/stores/index.js";
const store = useStore()
const http = axios.create({
//from env
baseURL: "https://challenge.spexa.dev/"
})
http.interceptors.request.use(function (config) {
console.log("store" + store) // return undefined
const token = store.getters['token']
if (token) {
config.headers.Authorization = 'Bearer ' + token;
}
return config;
});
export default http
the problem is that store works fine inside component But not in axios config module and I cant access it (!!!?) .
and this is store
store/index.js
import Vuex from "vuex";
import http from "../services/http";
export default new Vuex.Store({
state: {
access_token: null,
refresh_token: null,
},
getters: {
token(state) {
return state.access_token
}
},
});
and this is main.js
import { createApp } from 'vue'
import store from './stores/index';
import App from './App.vue'
createApp(App).use(store).mount('#app')
so ,what's the problem ?
so ,what's the problem ?
well, hard to tell without seeing what useStore from "#/stores/index.js" is doing.
store works fine inside component But not in axios config module
Is not a lot to go one and you may need to define what "not" working means. Is sotre undefined or is the value not staying reactive?
Maybe you meant to do this 🤷‍♂️:
import { store } from "#/stores/index.js";
instead of"
import useStore from "#/stores/index.js";
const store = useStore()

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

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;

What is the proper way to assign static asset paths in a pinia store to use in component templates

I have a website I am making that may be used for other companies of mine and realistically I want to keep certain branding stuff in a single Pinia Store so I can use these variables later globally. My pinia store is as follows:
//using composition api in Pinia
import {ref} from 'vue'
import { defineStore } from 'pinia';
export const useCompanyInfoStore = defineStore('companyInfo', () => {
const facebookLink = ref('https://www.facebook.com/blah/')
const googleLink = ref('https://www.google.com/maps/place/blah')
const instagramLink = ref('https://www.instagram.com/blah/')
const companyLogoLink = ref('~assets/imgs/company-logo.png')
return {facebookLink, googleLink, instagramLink, companyLogoLink}
})
When trying to use the companyLogoLink it cannot find the image path however if I used that string directly in my template it would work and find the image. So I am wondering what would be the best way to do this?
You can import your assets to get its public URL
import companyLogoLink from '#/assets/imgs/company-logo.png'
export const useCompanyInfoStore = defineStore('companyInfo', () => {
return {companyLogoLink}
})
Then you can use the link anywhere like so:
const store = useCompanyInfoStore()
console.log('this is your link', store.companyLogoLink)
Note that, to use ~ or # in the file path, you need to config vite alias like so:
// vite.config.js
export default defineConfig({
resolve: {
alias: {
'#': path.resolve(__dirname, './src'),
},
}
})

how can I use 'vue3-google-oauth' with boot files in Quasar?

I am trying to implement Google OAuth2 using 'vue3-google-oauth'.
I tried the method below, but the signIn() function returned false.
Please tell me how to use 'vue3-google-oauth' with boot files in Quasar.
this is my code.
googleoauth.js
import { boot } from 'quasar/wrappers';
import GAuth from 'vue3-google-oauth2';
let app;
export default boot(({ app }) => {
const gAuthOption = {
clientId: '835170725866-nt6qibip4tb7jafav2k20j83ipso67mo.apps.googleusercontent.com',
scope: 'profile email',
prompt: 'select_account',
};
app.use(GAuth, gAuthOption);
app = app;
});
export { app };
quasar.config.js
...
boot: ['googleoauth'],
...
LoginPage.vue ----- I want to use Google OAuth2 in this file
import { getCurrentInstance } from 'vue';
const app = getCurrentInstance();
app.appContext.config.globalProperties.$gAuth.signIn(); // return false (not working)

How to import Firebase only on client in Sapper?

I'm importing Firebase into my Sapper application, I do not want the imports to be evaluated on the server. How do I make sure imports are only on the client-side?
I am using Sapper to run sapper export which generates the static files. I have tried:
Creating the firebase instance in it's own file and exported the firebase.auth() and firebase.firestore() modules.
Trying to adjust the rollup.config.js to resolve the dependencies differently, as suggested from the error message below. This brings more headaches.
Creating the Firebase instance in client.js. Unsuccessful.
Creating the instance in stores.js. Unsuccessful.
Declaring the variable and assigning it in onMount(). This causes me to have to work in different block scopes. And feels a bit hacky.
The initialization of the app, works fine:
import firebase from 'firebase/app'
const config = {...}
firebase.initializeApp(config);
I have also discovered that if I change the import to just import firebase from 'firebase' I do not get this server error:
#firebase/app:
Warning: This is a browser-targeted Firebase bundle but it appears it is being run in a Node environment. If running in a Node environment, make sure you are using the bundle specified by the "main" field in package.json.
If you are using Webpack, you can specify "main" as the first item in
"resolve.mainFields": https://webpack.js.org/configuration/resolve/#resolvemainfields
If using Rollup, use the rollup-plugin-node-resolve plugin and set "module" to false and "main" to true: https://github.com/rollup/rollup-plugin-node-resolve
I expected to just export these firebase functionalities from a file and import them into my components like:
<script>
import { auth } from "../firebase";
</script>
But as soon as that import is include, the dev server crashes. I don't want to use it on the server, since I'm just generating the static files.
Does anyone have some ideas on how to achieve importing only on client side?
So I have spent too much time on this. There isn't really a more elegant solution than onMOunt.
However, I did realize that sapper really should be used for it's SSR capabilities. And I wrote an article about how to get set up on Firebase with Sapper SSR and Cloud Functions:
https://dev.to/eckhardtd/how-to-host-a-sapper-js-ssr-app-on-firebase-hmb
Another solution to original question is to put the Firebase CDN's in the global scope via the src/template.html file.
<body>
<!-- The application will be rendered inside this element,
because `app/client.js` references it -->
<div id='sapper'>%sapper.html%</div>
<!-- Sapper creates a <script> tag containing `app/client.js`
and anything else it needs to hydrate the app and
initialise the router -->
%sapper.scripts%
<!-- Insert these scripts at the bottom of the HTML, but before you use any Firebase services -->
<!-- Firebase App (the core Firebase SDK) is always required and must be listed first -->
<script src="https://www.gstatic.com/firebasejs/6.0.4/firebase-app.js"></script>
<!-- Add Firebase products that you want to use -->
<script src="https://www.gstatic.com/firebasejs/6.0.4/firebase-auth.js"></script>
<script src="https://www.gstatic.com/firebasejs/6.0.4/firebase-firestore.js"></script>
</body>
</html>
and in the component:
<script>
import { onMount } from 'svelte';
let database, authentication;
onMount(() => {
database = firebase.firestore();
authentication = firebase.auth();
});
const authHandler = () => {
if (process.browser) {
authentication
.createUserWithEmailAndPassword()
.catch(e => console.error(e));
}
}
</script>
<button on:click={authHandler}>Sign up</button>
I was able to import firebase using ES6. If you are using rollup you need to consfigure namedExports in commonjs plugin:
//--- rollup.config.js ---
...
commonjs({
namedExports: {
// left-hand side can be an absolute path, a path
// relative to the current directory, or the name
// of a module in node_modules
'node_modules/idb/build/idb.js': ['openDb'],
'node_modules/firebase/dist/index.cjs.js': ['initializeApp', 'firestore'],
},
}),
The you can use it like this:
//--- db.js ---
import * as firebase from 'firebase';
import 'firebase/database';
import { firebaseConfig } from '../config'; //<-- Firebase initialization config json
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
export { firebase };
// Initialize db
export const db = firebase.firestore();
and maybe use it in a service like such:
// --- userService.js ----
import { db } from './common';
const usersCol = db.collection('users');
export default {
async login(username, password) {
const userDoc = await usersCol.doc(username).get();
const user = userDoc.data();
if (user && user.password === password) {
return user;
}
return null;
},
};
EDITED
Full rollup config
/* eslint-disable global-require */
import resolve from 'rollup-plugin-node-resolve';
import replace from 'rollup-plugin-replace';
import commonjs from 'rollup-plugin-commonjs';
import svelte from 'rollup-plugin-svelte';
import babel from 'rollup-plugin-babel';
import { terser } from 'rollup-plugin-terser';
import config from 'sapper/config/rollup';
import { sass } from 'svelte-preprocess-sass';
import pkg from './package.json';
const mode = process.env.NODE_ENV;
const dev = mode === 'development';
const legacy = !!process.env.SAPPER_LEGACY_BUILD;
// eslint-disable-next-line no-shadow
const onwarn = (warning, onwarn) =>
(warning.code === 'CIRCULAR_DEPENDENCY' && warning.message.includes('/#sapper/')) || onwarn(warning);
export default {
client: {
input: config.client.input(),
output: config.client.output(),
plugins: [
replace({
'process.browser': true,
'process.env.NODE_ENV': JSON.stringify(mode),
}),
svelte({
dev,
hydratable: true,
emitCss: true,
preprocess: {
style: sass(),
},
}),
resolve({
browser: true,
}),
commonjs({
namedExports: {
// left-hand side can be an absolute path, a path
// relative to the current directory, or the name
// of a module in node_modules
'node_modules/idb/build/idb.js': ['openDb'],
'node_modules/firebase/dist/index.cjs.js': ['initializeApp', 'firestore'],
},
}),
legacy &&
babel({
extensions: ['.js', '.mjs', '.html', '.svelte'],
runtimeHelpers: true,
exclude: ['node_modules/#babel/**'],
presets: [
[
'#babel/preset-env',
{
targets: '> 0.25%, not dead',
},
],
],
plugins: [
'#babel/plugin-syntax-dynamic-import',
[
'#babel/plugin-transform-runtime',
{
useESModules: true,
},
],
],
}),
!dev &&
terser({
module: true,
}),
],
onwarn,
},
server: {
input: config.server.input(),
output: config.server.output(),
plugins: [
replace({
'process.browser': false,
'process.env.NODE_ENV': JSON.stringify(mode),
}),
svelte({
generate: 'ssr',
dev,
}),
resolve(),
commonjs(),
],
external: Object.keys(pkg.dependencies).concat(require('module').builtinModules || Object.keys(process.binding('natives'))),
onwarn,
},
serviceworker: {
input: config.serviceworker.input(),
output: config.serviceworker.output(),
plugins: [
resolve(),
replace({
'process.browser': true,
'process.env.NODE_ENV': JSON.stringify(mode),
}),
commonjs(),
!dev && terser(),
],
onwarn,
},
};
The clean way is to use the Dynamic Import as the documentation said: Making a component SSR compatible
The way to get around this is to use a dynamic import for your component, from within the onMount function (which is only called on the client), so that your import code is never called on the server.
So here for example we want to import the core of firebase and the authentication package too.
<script>
let firebase;
onMount(async () => {
const module = await import("firebase/app");
await import("firebase/auth");
firebase = module.default;
firebase.initializeApp(firebaseConfig);
});
<script>
And now you can use firebase object as you can, for example we want to login with email and password:
let email;
let password;
async function login() {
try {
let result = await firebase.auth().signInWithEmailAndPassword(
email,
password
);
console.log(result.user);
} catch (error) {
console.log(error.code, error.message);
}
}
In order to use Firebase with Sapper, you have to import firebase not firebase/app. You do want firebase to be able to load correctly with SSR on the backend, not just the frontend. If you have some metatags, for example, that would be stored in the database, you want them to load on the backend (UNTESTED).
You could just use firebase, but then you get the annoying console warning. Remember also firebase loads ALL firebase dependencies while firebase/app does not, that is why you don't want to use it on the frontend. There is probably a way with admin-firebase, but we want to have less dependencies.
Do not use rxfire at all. You don't need it. It causes errors with Sapper. Just plain Firebase.
firebase.ts
import firebase from 'firebase/app';
import "firebase/auth";
import "firebase/firestore";
import * as config from "./config.json";
const fb = (process as any).browser ? firebase : require('firebase');
fb.initializeApp(config);
export const auth = fb.auth();
export const googleProvider = new fb.auth.GoogleAuthProvider();
export const db = fb.firestore();
Firebase functions require an extra step and you must enable dynamic imports. (UNTESTED)
export const functions = (process as any).browser ? async () => {
await import("firebase/functions");
return fb.functions()
} : fb.functions();
While this compiles, I have not tried to run httpsCallable or confirmed it will load from the database on the backend for seo ssr from the db. Let me know if it works.
I suspect all of this will work with the new SvelteKit now that Sapper is dead.

Resources