Contentful with Nuxt.JS "Expected parameter accessToken" - javascript

I made a page which pulls data from Contentful. The data is pulling correctly, but buttons which use functions from methods don't work. Live updating of variables (for example, using v-model) doesn't work either.
I see this error in the console:
I think this error is the problem. Does anyone know what's wrong? I have no clue how to solve it :(
My contentful.js:
const contentful = require('contentful')
const client = contentful.createClient({
space: process.env.CONTENTFUL_ENV_SPACE_ID,
accessToken: process.env.CONTENTFUL_ENV_ACCESS_TOKEN
})
module.exports = client
Code which pulls data:
export default {
layout: "landing_page",
asyncData() {
return client
.getEntries({
content_type: "landingPage"
})
.then(entries => {
return { contentfulData: entries.items[0].fields };
});
},
computed: {
styles() {
return landingPageCss;
}
},
components: {
priceBox,
contact,
home,
aboutUs,
footerDiv
}
};

The best approach is used dotenv package to that. Set your env keys in .env file.
nuxt.config.js file should contain:
const env = require('dotenv').config()
export default {
mode: 'universal',
...
env: env.parsed,
...
}
Look at this video: https://codecourse.com/watch/using-env-files-with-nuxt

If you use dotenv you need to do following steps:
npm install --save-dev #nuxtjs/dotenv
Then you install it as an module. Note here if you using Nuxt.js older then v2.9 then you ahve to go to nuxt.config.js and put your code into the module section:
...
module: [
'#nuxtjs/dotenv'
]
...
If there is no module section then create one.
If you using newer then v2.9 then you put it into the buildModules
...
buildModules: [
'#nuxtjs/dotenv'
]
...
Your variables that are saved in the .env file are now accessable through context.env or process.env

Related

How to dynamically import markdown files

I'm building a blog using Vue, where I'd like my posts to be written as markdown files.
Currently I have the following structure
src/
posts/
blogpost1.md
blogpost2.md
view/
myComponent.vue
I want myComponent.vue to dynamically load a markdown post based on the route params.
For instance if we visited .../blog/blogpost2, then we would dynamically load in blogpost2.md
My current implementation is as follows:
<template>
<div
v-html="md"
></div>
</template>
<script>
import marked from "marked";
export default {
data () {
return {
md: undefined,
};
},
created () {
const importRoute = `posts/blog/${this.$route.params.postName}.md`;
const md = require(importRoute);
this.md = marked(md);
},
};
</script>
To load the markdown file I'm using markdown-loader in webpack.
I'm also using copy-webpack-plugin to try to copy my entire /posts dir into my build folder.
configureWebpack: {
module: {
rules: [{
test: /\.md$/,
use: [
{ loader: "html-loader", options: {} },
{ loader: "markdown-loader", options: {} },
],
},
],
},
plugins: [
new CopyPlugin({
patterns: [
{ from: "src/posts", to: "posts" },
],
}),
],
},
When I run npm run serve however, I receive
[Vue warn]: Error in created hook: "Error: Cannot find module './posts/blogpost2.md'"
And when I look at the pages in my browser I don't see the posts directory (see image below)
However, when running npm run build:staging I do see the posts directory in /dist
Why am I unable to import import blogpost2.mddynamically?
My attempt at using a dynamic import that changes based on the route will not work because webpack is unable to properly bundle the imported files. This is because webpack needs to know what it will be importing in order to bundle them correctly.
Instead we can use a different approach of copying our markdown directory as it is into /dist and sending a request to fetch each markdown file.
We end up with this in our webpack config (or in my case vue.config.js):
const CopyPlugin = require("copy-webpack-plugin");
configureWebpack: {
plugins: [
new CopyPlugin({
patterns: [
{ from: "src/posts", to: "posts" },
],
}),
],
},
Note that we don't need the webpack loaders shown in my initial question because webpack itself isn't handling the loading of these files.
This ensures that posts/ gets copied as it is when we build this project.
Then in our component we can dynamically retrieve Markdown files like so:
<template>
<div
v-html="md"
></div>
</template>
<script>
import marked from "marked";
export default {
data () {
return {
md: undefined,
};
},
async created () {
const res = await fetch(`http://localhost:8081/posts/${this.$route.params.postName}.md`);
const md = await res.text();
this.md = marked(md);
},
};
</script>
// I once need to do a similar thing and faced similar challenges but it was built with Create React App, so please take my answer with a pinch of salt.
Try placing your import path inside require(…) (or import(…), if you ever use it) instead of declaring a variable then passing it in. I remember WebPack has trouble finding files when the paths are not static strings (hard-coded in this way). Whatever dynamic path it may be, your path needs to start with a static string, but you can concatenate dynamic values behind it.
Also, notice the ./ I added to the front of the path. Ignore it if you're using absolute imports, otherwise, this might also be a reason why the code failed?
created () {
// Instead of:
// const importRoute = `posts/blog/${this.$route.params.postName}.md`;
// const md = require(importRoute);
// Try:
const md = require(
`./posts/blog/${this.$route.params.postName}.md`
);
this.md = marked(md);
},
Extra: As much as I know, that the created hook in Vue is supposed only be synchronous, but after stumbling upon this answer I decided to include this snippet too. This one uses dynamic import. If all else fails perhaps you can give this a try?
async created () {
const { default: url } = await import(
`posts/blog/${this.$route.params.postName}.md`
);
const res = await fetch(url);
const md = res.text();
this.md = marked(md);
},

Svelte framework: environment variables not appearing in svelte app

I'm trying to use environment variables in my svelte app. I've installed #Rollup/plugin-replace and dotenv. I created a .env file to hold my API_KEY and added the following to plugins in rollup.config.js from this Medium article:
plugins: [
replace({
__myapp: JSON.stringify({
env: {
isProd: production,
API_URL : process.env.API_URL
}
}),
}),
]
and in the svelte components of my app I would access my API key via
const apiUrl = __myapp.env.API_URL
which worked. However, a few days later I was having authentication issues and after some debugging I found that __myapp.env.API_URL was returning undefined by trying to print it to the console.
I then tried changing the replace call to just replace({'API_KEY': process.env.API_KEY}) and console.log(API_KEY) was still displaying undefined. I tested replace by trying to use it replace some variable with some string and it worked so that confirms that rollup is working fine. So, I suspect the problem is in process.env.API_KEY but I'm not sure. What might I be doing wrong with my attempts to access my environment variables?
(Some background: I am using sveltejs/template as a template to build my app)
dotenv won't register your .env vars until you call config, that's why process.env.API_KEY is undefined when you try to access it.
In your rollup.config.js you can do:
import { config as configDotenv } from 'dotenv';
import replace from '#rollup/plugin-replace';
configDotenv();
export default {
input: 'src/main.js',
...
plugins: [
replace({
__myapp: JSON.stringify({
env: {
isProd: production,
API_URL: process.env.API_URL,
},
}),
}),
svelte({ ... })
]
}

Environment variables not working (Next.JS 9.4.4)

So i'm using the Contentful API to get some content from my account and display it in my Next.Js app (i'm using next 9.4.4). Very basic here. Now to protect my credentials, i'd like to use environment variables (i've never used it before and i'm new to all of this so i'm a little bit losted).
I'm using the following to create the Contentful Client in my index.js file :
const client = require('contentful').createClient({
space: 'MYSPACEID',
accessToken: 'MYACCESSTOKEN',
});
MYSPACEID and MYACCESSTOKEN are hardcoded, so i'd like to put them in an .env file to protect it and don't make it public when deploying on Vercel.
I've created a .env file and filled it like this :
CONTENTFUL_SPACE_ID=MYSPACEID
CONTENTFUL_ACCESS_TOKEN=MYACCESSTOKEN
Of course, MYACCESSTOKEN and MYSPACEID contains the right keys.
Then in my index.js file, i do the following :
const client = require('contentful').createClient({
space: `${process.env.CONTENTFUL_SPACE_ID}`,
accessToken: `${process.env.CONTENTFUL_ACCESS_TOKEN}`,
});
But it doesn't work when i use yarn dev, i get the following console error :
{
sys: { type: 'Error', id: 'NotFound' },
message: 'The resource could not be found.',
requestId: 'c7340a45-a1ef-4171-93de-c606672b65c3'
}
Here is my Homepage and how i retrieve the content from Contentful and pass them as props to my components :
const client = require('contentful').createClient({
space: 'MYSPACEID',
accessToken: 'MYACCESSTOKEN',
});
function Home(props) {
return (
<div>
<Head>
<title>My Page</title>
<link rel="icon" href="/favicon.ico" />
</Head>
<main id="page-home">
<Modal />
<NavTwo />
<Hero item={props.myEntries[0]} />
<Footer />
</main>
</div>
);
}
Home.getInitialProps = async () => {
const myEntries = await client.getEntries({
content_type: 'mycontenttype',
});
return {
myEntries: myEntries.items
};
};
export default Home;
Where do you think my error comes from?
Researching about my issue, i've also tried to understand how api works in next.js as i've read it could be better to create api requests in pages/api/ but i don't understand how to get the content and then pass the response into my pages components like i did here..
Any help would be much appreciated!
EDIT :
So i've fixed this by adding my env variables to my next.config.js like so :
const withSass = require('#zeit/next-sass');
module.exports = withSass({
webpack(config, options) {
const rules = [
{
test: /\.scss$/,
use: [{ loader: 'sass-loader' }],
},
];
return {
...config,
module: { ...config.module, rules: [...config.module.rules, ...rules] },
};
},
env: {
CONTENTFUL_SPACE_ID: process.env.CONTENTFUL_SPACE_ID,
CONTENTFUL_ACCESS_TOKEN: process.env.CONTENTFUL_ACCESS_TOKEN,
},
});
if you are using latest version of nextJs ( above 9 )
then follow these steps :
Create a .env.local file in the root of the project.
Add the prefix NEXT_PUBLIC_ to all of your environment variables.
eg: NEXT_PUBLIC_SOMETHING=12345
use them in any JS file like with prefix process.env
eg: process.env.NEXT_PUBLIC_SOMETHING
You can't make this kind of request from the client-side without exposing your API credentials. You have to have a backend.
You can use Next.js /pages/api to make a request to Contentful and then pass it to your front-end.
Just create a .env file, add variables and reference it in your API route as following:
process.env.CONTENTFUL_SPACE_ID
Since Next.js 9.4 you don't need next.config.js for that.
By adding the variables to next.config.js you've exposed the secrets to client-side. Anyone can see these secrets.
New Environment Variables Support
Create a Next.js App with Contentful and Deploy It with Vercel
Blog example using Next.js and Contentful
I recomended to update at nextjs 9.4 and up, use this example:
.env.local
NEXT_PUBLIC_SECRET_KEY=i7z7GeS38r10orTRr1i
and in any part of your code you could use:
.js
const SECRET_KEY = process.env.NEXT_PUBLIC_SECRET_KEY
note that it must be the same name of the key "NEXT_PUBLIC_ SECRET_KEY" and not only "SECRET_KEY"
and when you run it make sure that in the log says
$ next dev
Loaded env from E:\awesome-project\client\.env.local
ready - started server on http://localhost:3000
...
To read more about environment variables see this link
Don't put sensitive things in next.config.js however in my case I have some env variables that aren't sensitive at all and I need them Server Side as well as Client side and then you can do:
// .env file:
VARIABLE_X=XYZ
// next.config.js
module.exports = {
env: {
VARIABLE_X: process.env.VARIABLE_X,
},
}
You have to make a simple change in next.config.js
const nextConfig = {
reactStrictMode: true,
env:{
MYACCESSTOKEN : process.env.MYACCESSTOKEN,
MYSPACEID: process.env.MYSPACEID,
}
}
module.exports = nextConfig
change it like this
Refer docs
You need to add a next.config.js file in your project. Define env variables in that file and those will be available inside your app.
npm i --save dotenv-webpack#2.0.0 // version 3.0.0 has a bug
create .env.development.local file in the root. and add your environment variables here:
AUTH0_COOKIE_SECRET=eirhg32urrroeroro9344u9832789327432894###
NODE_ENV=development
AUTH0_NAMESPACE=https:ilmrerino.auth0.com
create next.config.js in the root of your app.
const Dotenv = require("dotenv-webpack");
module.exports = {
webpack: (config) => {
config.resolve.alias["#"] = path.resolve(__dirname);
config.plugins.push(new Dotenv({ silent: true }));
return config;
},
};
However those env variables are gonna be accessed by the server. if you want to use any of the env variables you have to add one more configuration.
module.exports = {
webpack: (config) => {
config.resolve.alias["#"] = path.resolve(__dirname);
config.plugins.push(new Dotenv({ silent: true }));
return config;
},
env: {
AUTH0_NAMESPACE: process.env.AUTH0_NAMESPACE,
},
};
For me, the solution was simply restarting the local server :)
Gave me a headache and then fixed it on accident.
It did not occur to me that env variables are loaded when the server is starting.

How to get React Native variant in Metro?

I am developing React Native application that includes different configurations for different possible clients, in a file such as src/config/config.js. These configurations are quite complex. The file is structured based on the client name as key, and the values as the object entries, e.g.:
export default {
fooClient: {
apiUrl: "https://foo.example.com/",
barClient: {
apiUrl: "https://bar.example.com/"
}
}
Of course, there are many other option keys.
When building the app, I know for which client I want to do this, by specifying an Android build variant, e.g.:
ENVFILE=.env npx react-native run-android --variant fooDebug --appIdSuffix foo
For security reasons, I don't want keys of other clients to be included in the config file though. What are my options to remove all other client configs from this file before I build the app and ship it to a client?
I thought about the following: I modify the packager so that it strips out the keys that do not correspond to the current build variant.
I now have a transformer plugin for Metro that does the following:
const upstreamTransformer = require('metro-react-native-babel-transformer');
module.exports.transform = function(src, filename, options) {
if (typeof src === 'object') {
// handle RN >= 0.46
({ src, filename, options } = src);
}
if (filename.endsWith('config.js')) {
console.log('Transforming ' + filename);
let srcStripped = src.replace(';', '').replace('export default ', '');
let configObj = JSON.parse(srcStripped);
// TODO: get the build variant and strip all keys that we do not need from configObj
return upstreamTransformer.transform({
src: 'export default ' + JSON.stringify(configObj) + ';',
filename: filename,
options
});
} else {
return upstreamTransformer.transform({ src, filename, options });
}
};
But how do I know which build variant is being used?
If this seems like an XY problem, I am happy to explore alternatives to building the configuration dynamically. I cannot, however, use environment variables, since the configuration will be too complex for it to be just a list of .env keys.
You shouldn't use Metro transform this way. It's not clean and it may lead to missing configuration and/or damaged syntax sooner or later.
What I have done and suggest you, is to create 3 different configuration files under src/config/; one file for fooClient.js, one file for barClient.js and one last file with common configuration client.js. All files will export default configuration objects, but inside each fooClient and barClient, you'll use deepmerge module to merge client.js config:
client.js:
export default {
commonSettingA: "...",
commonSettings: {
...
}
...
}
fooClient.js:
import merge from 'deepmerge';
import config from './config';
export default merge.all([
config,
{
apiUrl: "https://foo.example.com/",
}
]);
barClient.js:
import merge from 'deepmerge';
import config from './config';
export default merge.all([
config,
{
apiUrl: "https://bar.example.com/",
}
]);
Then you can use an environment variable to pass the needed configuration and create a propriate metro resolve; #react-native-community/cli does not pass command line arguments to metro config script. You can use process.argv to parse it by yourself, but it's not worth it.
Here is how you can create a resolve inside metro.config.js using environment variable:
const path = require("path");
module.exports = {
projectRoot: path.resolve(__dirname),
resolver: {
sourceExts: ['js', 'jsx', 'ts', 'tsx'],
extraNodeModules: {
// Local aliases
"#config": path.resolve(__dirname, "src/config", `${process.env.METRO_VARIANT}Client.js`)
}
}
};
Using this resolve, you'll have to import the configuration like this:
import config from '#config';
Then you add 2 package.json scripts, one for fooClient and one for barClient:
{
...
"scripts": {
"run:android:foo": "METRO_VARIANT=foo ENVFILE=.env npx react-native run-android --variant fooDebug --appIdSuffix foo",
"run:android:bar": "METRO_VARIANT=bar ENVFILE=.env npx react-native run-android --variant barDebug --appIdSuffix bar",
...
}
...
}
Then you just run the needed script:
yarn run:android:foo # will build with fooClient.js
yarn run:android:bar # will build with barClient.js
Can't you just add a "duplicated" parameter to the command?
Like so
METRO_VARIANT=fooDebug ENVFILE=.env npx react-native run-android --variant fooDebug --appIdSuffix foo
And get it with process.env.METRO_VARIANT
You might not want to make your setup much more complex. Depending on the number of the number of different clients, you might wanna go with multiple schemas/variants per client, but that is probably way overkill.

Conditional build based on environment using Webpack

I have some things for development - e.g mocks which I would like to not bloat my distributed build file with.
In RequireJS you can pass a config in a plugin file and conditonally require things in based on that.
For webpack there doesn't seem to be a way of doing this. Firstly to create a runtime config for an environment I have used resolve.alias to repoint a require depending on the environment, e.g:
// All settings.
var all = {
fish: 'salmon'
};
// `envsettings` is an alias resolved at build time.
module.exports = Object.assign(all, require('envsettings'));
Then when creating the webpack config I can dynamically assign which file envsettings points to (i.e. webpackConfig.resolve.alias.envsettings = './' + env).
However I would like to do something like:
if (settings.mock) {
// Short-circuit ajax calls.
// Require in all the mock modules.
}
But obviously I don't want to build in those mock files if the environment isn't mock.
I could possibly manually repoint all those requires to a stub file using resolve.alias again - but is there a way that feels less hacky?
Any ideas how I can do that? Thanks.
You can use the define plugin.
I use it by doing something as simple as this in your webpack build file where env is the path to a file that exports an object of settings:
// Webpack build config
plugins: [
new webpack.DefinePlugin({
ENV: require(path.join(__dirname, './path-to-env-files/', env))
})
]
// Settings file located at `path-to-env-files/dev.js`
module.exports = { debug: true };
and then this in your code
if (ENV.debug) {
console.log('Yo!');
}
It will strip this code out of your build file if the condition is false. You can see a working Webpack build example here.
Not sure why the "webpack.DefinePlugin" answer is the top one everywhere for defining Environment based imports/requires.
The problem with that approach is that you are still delivering all those modules to the client -> check with webpack-bundle-analyezer for instance. And not reducing your bundle.js's size at all :)
So what really works well and much more logical is: NormalModuleReplacementPlugin
So rather than do a on_client conditional require -> just not include not needed files to the bundle in the first place
Hope that helps
Use ifdef-loader. In your source files you can do stuff like
/// #if ENV === 'production'
console.log('production!');
/// #endif
The relevant webpack configuration is
const preprocessor = {
ENV: process.env.NODE_ENV || 'development',
};
const ifdef_query = require('querystring').encode({ json: JSON.stringify(preprocessor) });
const config = {
// ...
module: {
rules: [
// ...
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: `ifdef-loader?${ifdef_query}`,
},
},
],
},
// ...
};
I ended up using something similar to Matt Derrick' Answer, but was worried about two points:
The complete config is injected every time I use ENV (Which is bad for large configs).
I have to define multiple entry points because require(env) points to different files.
What I came up with is a simple composer which builds a config object and injects it to a config module.
Here is the file structure, Iam using for this:
config/
└── main.js
└── dev.js
└── production.js
src/
└── app.js
└── config.js
└── ...
webpack.config.js
The main.js holds all default config stuff:
// main.js
const mainConfig = {
apiEndPoint: 'https://api.example.com',
...
}
module.exports = mainConfig;
The dev.js and production.js only hold config stuff which overrides the main config:
// dev.js
const devConfig = {
apiEndPoint: 'http://localhost:4000'
}
module.exports = devConfig;
The important part is the webpack.config.js which composes the config and uses the DefinePlugin to generate a environment variable __APP_CONFIG__ which holds the composed config object:
const argv = require('yargs').argv;
const _ = require('lodash');
const webpack = require('webpack');
// Import all app configs
const appConfig = require('./config/main');
const appConfigDev = require('./config/dev');
const appConfigProduction = require('./config/production');
const ENV = argv.env || 'dev';
function composeConfig(env) {
if (env === 'dev') {
return _.merge({}, appConfig, appConfigDev);
}
if (env === 'production') {
return _.merge({}, appConfig, appConfigProduction);
}
}
// Webpack config object
module.exports = {
entry: './src/app.js',
...
plugins: [
new webpack.DefinePlugin({
__APP_CONFIG__: JSON.stringify(composeConfig(ENV))
})
]
};
The last step is now the config.js, it looks like this (Using es6 import export syntax here because its under webpack):
const config = __APP_CONFIG__;
export default config;
In your app.js you could now use import config from './config'; to get the config object.
another way is using a JS file as a proxy, and let that file load the module of interest in commonjs, and export it as es2015 module, like this:
// file: myModule.dev.js
module.exports = "this is in dev"
// file: myModule.prod.js
module.exports = "this is in prod"
// file: myModule.js
let loadedModule
if(WEBPACK_IS_DEVELOPMENT){
loadedModule = require('./myModule.dev.js')
}else{
loadedModule = require('./myModule.prod.js')
}
export const myString = loadedModule
Then you can use ES2015 module in your app normally:
// myApp.js
import { myString } from './store/myModule.js'
myString // <- "this is in dev"
Faced with the same problem as the OP and required, because of licensing, not to include certain code in certain builds, I adopted the webpack-conditional-loader as follows:
In my build command I set an environment variable appropriately for my build. For example 'demo' in package.json:
...
"scripts": {
...
"buildDemo": "./node_modules/.bin/webpack --config webpack.config/demo.js --env.demo --progress --colors",
...
The confusing bit that is missing from the documentation I read is that I have to make this visible throughout the build processing by ensuring my env variable gets injected into the process global thus in my webpack.config/demo.js:
/* The demo includes project/reports action to access placeholder graphs.
This is achieved by using the webpack-conditional-loader process.env.demo === true
*/
const config = require('./production.js');
config.optimization = {...(config.optimization || {}), minimize: false};
module.exports = env => {
process.env = {...(process.env || {}), ...env};
return config};
With this in place, I can conditionally exclude anything, ensuring that any related code is properly shaken out of the resulting JavaScript. For example in my routes.js the demo content is kept out of other builds thus:
...
// #if process.env.demo
import Reports from 'components/model/project/reports';
// #endif
...
const routeMap = [
...
// #if process.env.demo
{path: "/project/reports/:id", component: Reports},
// #endif
...
This works with webpack 4.29.6.
I've struggled with setting env in my webpack configs. What I usually want is to set env so that it can be reached inside webpack.config.js, postcss.config.js and inside the entry point application itself (index.js usually). I hope that my findings can help someone.
The solution that I've come up with is to pass in --env production or --env development, and then set mode inside webpack.config.js.
However, that doesn't help me with making env accessible where I want it (see above), so I also need to set process.env.NODE_ENV explicitly, as recommended here.
Most relevant part that I have in webpack.config.js follow below.
...
module.exports = mode => {
process.env.NODE_ENV = mode;
if (mode === "production") {
return merge(commonConfig, productionConfig, { mode });
}
return merge(commonConfig, developmentConfig, { mode });
};
Use envirnment variables to create dev and prod deployments:
https://webpack.js.org/guides/environment-variables/
I use string-replace-loader to get rid of an unnecessary import from the production build, and it works as expected: the bundle size becomes less, and a module for development purposes (redux-logger) is completely removed from it. Here is the simplified code:
In the file webpack.config.js:
rules: [
// ... ,
!env.dev && {
test: /src\/store\/index\.js$/,
loader: 'string-replace-loader',
options: {
search: /import.+createLogger.+from.+redux-logger.+;/,
replace: 'const createLogger = null;',
}
}
].filter(Boolean)
In the file src/store/index.js:
// in prod this import declaration is substituted by `const createLogger = null`:
import { createLogger } from 'redux-logger';
// ...
export const store = configureStore({
reducer: persistedReducer,
middleware: createLogger ? [createLogger()] : [],
devTools: !!createLogger
});
While this is not the best solution, it may work for some of your needs. If you want to run different code in node and browser using this worked for me:
if (typeof window !== 'undefined')
return
}
//run node only code now

Categories

Resources