Conditional url based on NODE_ENV on Next.js - javascript

Is there a way for me to set an url based on whether I'm in development or production?
Currently I have a component with the following code:
export default class Search extends Component {
static async getInitialProps({ query: { location } }) {
const res = await fetch(
`http://localhost:3000/api/search?location=${location}`
)
const businesses = await res.json()
return businesses
}
...
}
I would like something that allows me to do the following:
export default class Search extends Component {
static async getInitialProps({ query: { location } }) {
let res
if (environment is in developement) {
res = await fetch(
`http://localhost:3000/api/search?location=${location}`
)
} else if (environment is in production) {
res = await fetch (
`https://productionurl.now.sh/api/search?location=${location}`
)
}
const businesses = await res.json()
return businesses
}
...
}

You can do that using the NODE_ENV environment variable. For a nice developer experience, set up a config file like this:
/config/index.js
const dev = process.env.NODE_ENV !== 'production';
export const server = dev ? 'http://localhost:3000/api' : 'https://productionurl.now.sh/api';
Then you can use that inside your getInitialProps methods throughout your application.
/components/Search.js
import { server } from '../config';
// ...
static async getInitialProps({ query: { location } }) {
const res = await fetch(`${server}/search?location=${location}`);
const businesses = await res.json();
return businesses;
}
Make sure that the NODE_ENV variable is set inside package.json build scripts, which should look something like this.
package.json
"scripts": {
"build": "NODE_ENV=production next build",
},

Here's an example on how to setup development and production
const prodConfig = {
publicRuntimeConfig: {
API_ENDPOINT: 'http://google.com/api'
}
}
const devConfig = {
publicRuntimeConfig: {
API_ENDPOINT: 'http://localhost:3000/api'
}
}
module.exports = process.env.NODE_ENV === 'production ? prodConfig : devConfig

Yes, like what alex bennett has commented, using dotenv should work for your case!
To set it up,
Install dotenv as a dependency on your Node.js project npm install dotenv --save then require it in your application require('dotenv').config()
Create a file called .env in the root directory of your project with the environment variables that you need in this <NAME>/<VALUE> format here: MY_ENVIRONMENT=production.
Change <VALUE> to production if you're deploying from your hosted server, or to development if you're deploying from your localhost.
When that's all set up, you can very easily check the loaded environment variables in your code like this (from your example):
export default class Search extends Component {
static async getInitialProps({ query: { location } }) {
let res
if (process.env.MY_ENVIRONMENT === 'development') {
res = await fetch(
`http://localhost:3000/api/search?location=${location}`
)
} else if (process.env.MY_ENVIRONMENT === 'production') {
res = await fetch (
`https://productionurl.now.sh/api/search?location=${location}`
)
}
const businesses = await res.json()
return businesses
}
...
}

Related

How to use plugin's data in a nuxt.config.js file?

My plugin, env.js:
export default async (_ctx, inject) => {
const resp = await fetch('/config.json')
const result = await resp.json()
inject('env', result)
// eslint-disable-next-line no-console
console.log('env injected', result)
return result
}
Then an idea was to use it's data inside nuxt.config.js to inject into publicRuntimeConfig:
import env from './plugins/env.js'
publicRuntimeConfig: {
test: env,
},
Then in a browser console i'm checking it:
this.$nuxt.$config
It shows me:
instead of a value, though this.$nuxt.$env shows the correct values:
What's wrong?
UPDATE 1
Tried Tony's suggestion:
// nuxt.config.js
import axios from 'axios'
export default async () => {
const resp = await axios.get('/config.json')
const config = resp.data
return {
publicRuntimeConfig: {
config
}
}
}
It cannot fetch config.json, but if i point it to an external resource: "https://api.openbrewerydb.org/breweries" it does work.
Intention of this question, is to have config.json where a user could simply change variable values there (from a compiled code) and change endpoints without a re-build process.
In nuxt.config.js, your env variable is a JavaScript module, where the default export is the function intended to be automatically run by Nuxt in a plugin's context. Importing the plugin script does not automatically execute that function. Even if you manually ran that function, it wouldn't make sense to use an injected prop as a runtime config because the data is already available as an injected prop.
If you just want to expose config.json as a runtime config instead of an injected prop, move the code from the plugin into an async configuration:
// nuxt.config.js
export default async () => {
const resp = await fetch('/config.json')
const config = await resp.json()
return {
publicRuntimeConfig: {
keycloak: config
}
}
}

Import.meta.env undefined on production build vitejs

I am using vitejs to compile my react app statically, however after build .env imports become undefined which is not the case on development stage.
reading the docs I've found out that these variables are replace by their corresponding values, but upon looking at the source/compiled code in the dev tools after serving it shows an empty object with the env name/key
i might have a wrong configuration in vite.config.ts so here it is.
//vite.config.ts
import { defineConfig, loadEnv } from 'vite';
import reactRefresh from '#vitejs/plugin-react-refresh';
import { getAliases } from 'vite-aliases';
const aliases = getAliases({
path: 'src',
prefix: '#',
});
export default ({ mode }) => {
process.env = { ...process.env, ...loadEnv(mode, process.cwd()) };
// import.meta.env.VITE_NAME available here with: process.env.VITE_NAME
// import.meta.env.VITE_PORT available here with: process.env.VITE_PORT
const plugins = mode === 'development' ? [reactRefresh()] : [];
return defineConfig({
plugins,
publicDir: 'src/assets',
resolve: {
alias: aliases,
},
build: {
chunkSizeWarningLimit: 1500,
},
});
};
And also the code where I'm referencing these env var
//config.ts
export const config = () => {
const url = import.meta.env.VITE_SERVER_URL;
const api = import.meta.env.VITE_API_ENDPOINT;
const auth = import.meta.env.VITE_AUTH_ENDPOINT;
const isProd = import.meta.env.MODE === 'production';
const isDev = import.meta.env.MODE === 'development';
console.log(url, api, auth);
return {
api: (endpoint: string) => `${url}${api}${endpoint}`,
auth: (endpoint: string) => `${url}${auth}${endpoint}`,
test: (endpoint: string) => `${url}test${endpoint}`,
isProd,
isDev,
};
};
I just realized what the ViteJS documentation says and I'll leave it in case someone also suffers from this.
You don't have to use VITE_. You can use any prefix you like as long as you define it in the envPrefix option on the vite config.

Using Handlebars with Deno v1.6.x

I'm struggling to get the Handlebars templating working with Deno v1.6.x, any thoughts?
$ deno --version
deno 1.6.2 (release, x86_64-unknown-linux-gnu)
v8 8.8.278.2
typescript 4.1.3
$ tree
.
├── template.js
└── views
├── layouts
│ └── main.hbs
└── partials
/* template.js */
import { Application, Router, Status } from 'https://deno.land/x/oak/mod.ts'
import { Handlebars } from 'https://deno.land/x/handlebars/mod.ts'
const port = 8080
const app = new Application()
const router = new Router()
const handle = new Handlebars()
// error handler
app.use(async (context, next) => {
try {
await next()
} catch (err) {
console.log(err)
}
})
// the routes defined here
router.get('/', async context => {
const data = {
title: 'Fools Day',
date: '01/04/20'
}
context.response.body = await handle.renderView('main', data)
})
app.use(router.routes())
app.use(router.allowedMethods())
// static content
app.use(async (context, next) => {
const root = `${Deno.cwd()}/static`
try {
await context.send({ root })
} catch {
next()
}
})
// page not found
app.use( async context => {
context.response.status = Status.NotFound
context.response.body = `"${context.request.url}" not found`
})
app.addEventListener("listen", ({ port }) => console.log(`listening on port: ${port}`) )
await app.listen({ port })
I get the following error when I try to view the page:
$ deno run --allow-net --unstable template.js
Check file:///home/xxx/template.js
listening on port: 8080
NotFound: No such file or directory (os error 2)
at processResponse (deno:core/core.js:223:11)
at Object.jsonOpAsync (deno:core/core.js:240:12)
at async open (deno:runtime/js/30_files.js:44:17)
at async readFile (deno:runtime/js/40_read_file.js:15:18)
at async Handlebars.render (mod.ts:98:53)
at async Handlebars.renderView (mod.ts:76:26)
at async file:///home/xxx/template.js:28:26
at async dispatch (middleware.ts:41:7)
at async dispatch (middleware.ts:41:7)
at async dispatch (middleware.ts:41:7)
I've looked at the documentation at https://deno.land/x/handlebars#v0.6.0 and think I have used all the default settings but the error message doesn't tell me what file is not found.
Has anyone had experience using Handlebars with Deno?
Cheers.
EDIT: Make sure you have a layout file according to your default layout!!
If you do not want a default layout file then you can do this:
const handle = new Handlebars({defaultLayout:''});
Here's the code for renderView
public async renderView(
view: string,
context?: Record<string, unknown>,
layout?: string,
): Promise<string> {
if (!view) {
console.warn("View is null");
return "";
}
const config: HandlebarsConfig = this.config as HandlebarsConfig;
const partialsPathes = await this.getTemplatesPath(
join(config.baseDir, config.partialsDir),
);
partialsPathes && (await this.registerPartials(partialsPathes));
const path = join(config.baseDir, view + config.extname);
const body: string = await this.render(path, context);
layout = (layout as string) || config.defaultLayout;
if (layout) {
const layoutPath: string = join(
config.baseDir,
config.layoutsDir,
layout + config.extname,
);
return this.render(layoutPath, { ...context, body });
}
return body;
}
The first parameter, in your case main tells the code to look for a file called main.hbs in your views directory (provided you are using the default config).
You can additionally pass a layout parameter to render it within a layout file that is housed in your layouts directory.

How to serve static file if exists, and a file with default values if not exists with koa.js

I want to modify a framework. At the moment, it creates a robots.txt file with default values. It should check first, if robots.txt exists, and if not, create it as before.
The code looks like this at the moment:
import Koa from "koa";
import { get } from "koa-route";
import serve from "koa-static";
import mount from "koa-mount";
import React from "react";
import { Context } from "#frontity/types";
export default ({ packages }): ReturnType<Koa["callback"]> => {
const app = new Koa();
// Serve static files.
app.use(mount("/static", serve("./build/static")));
// Default robots.txt.
app.use(
get("/robots.txt", (ctx) => {
ctx.type = "text/plain";
ctx.body = "User-agent: *\nDisallow:";
})
);
// Ignore HMR if not in dev mode or old browser open.
const return404 = (ctx: Context) => {
ctx.status = 404;
};
app.use(get("/__webpack_hmr", return404));
app.use(get("/static/([a-z0-9]+\\.hot-update\\.json)", return404));
// Return Frontity favicon for favicon.ico.
app.use(get("/favicon.ico", serve("./")));
// Frontity server rendering.
app.use(async (ctx, next) => {
...
});
return app.callback();
};
I could serve it like favicon.ico is served: app.use(get("/robots.txt", serve("./")));, but I have no idea, how to check it first, if the file exists, and if not return the default value:
(ctx) => {
ctx.type = "text/plain";
ctx.body = "User-agent: *\nDisallow:";
})
I check the file existence with fs.existsSync, like:
import fs from "fs";
let hasRobotTxt = false;
if (fs.existsSync("./robots.txt")) {
hasRobotTxt = true;
}
Then serve it conditionally like:
app.use(
get(
"/robots.txt",
hasRobotTxt
? serve("./")
: (ctx) => {
ctx.type = "text/plain";
ctx.body = "User-agent: *\nDisallow:";
}
)
);

How does react-create-app/react-scripts find the entry point?

In this tutorial, how does npm start find the src/index/js to star the render? I can't find the configuration of this anywhere.
you can run npm run eject so that the project turn back to a normal webpack project. then you can find the configs.
If you look at package.json, the start script is defined there:
"scripts": {
"start": "react-scripts start",
It runs react-scripts start. And the package.json file for react-scripts looks like this:
"bin": {
"react-scripts": "./bin/react-scripts.js"
},
So we should see what does ./bin/react-scripts.js start do, which runs this code:
// #remove-on-eject-begin
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
// #remove-on-eject-end
'use strict';
// Do this as the first thing so that any code reading it knows the right env.
process.env.BABEL_ENV = 'development';
process.env.NODE_ENV = 'development';
// Makes the script crash on unhandled rejections instead of silently
// ignoring them. In the future, promise rejections that are not handled will
// terminate the Node.js process with a non-zero exit code.
process.on('unhandledRejection', err => {
throw err;
});
// Ensure environment variables are read.
require('../config/env');
const fs = require('fs');
const chalk = require('react-dev-utils/chalk');
const webpack = require('webpack');
const WebpackDevServer = require('webpack-dev-server');
const clearConsole = require('react-dev-utils/clearConsole');
const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
const {
choosePort,
createCompiler,
prepareProxy,
prepareUrls,
} = require('react-dev-utils/WebpackDevServerUtils');
const openBrowser = require('react-dev-utils/openBrowser');
const semver = require('semver');
const paths = require('../config/paths');
const configFactory = require('../config/webpack.config');
const createDevServerConfig = require('../config/webpackDevServer.config');
const getClientEnvironment = require('../config/env');
const react = require(require.resolve('react', { paths: [paths.appPath] }));
const env = getClientEnvironment(paths.publicUrlOrPath.slice(0, -1));
const useYarn = fs.existsSync(paths.yarnLockFile);
const isInteractive = process.stdout.isTTY;
// Warn and crash if required files are missing
if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
process.exit(1);
}
// Tools like Cloud9 rely on this.
const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 3000;
const HOST = process.env.HOST || '0.0.0.0';
if (process.env.HOST) {
console.log(
chalk.cyan(
`Attempting to bind to HOST environment variable: ${chalk.yellow(
chalk.bold(process.env.HOST)
)}`
)
);
console.log(
`If this was unintentional, check that you haven't mistakenly set it in your shell.`
);
console.log(
`Learn more here: ${chalk.yellow('https://cra.link/advanced-config')}`
);
console.log();
}
// We require that you explicitly set browsers and do not fall back to
// browserslist defaults.
const { checkBrowsers } = require('react-dev-utils/browsersHelper');
checkBrowsers(paths.appPath, isInteractive)
.then(() => {
// We attempt to use the default port but if it is busy, we offer the user to
// run on a different port. `choosePort()` Promise resolves to the next free port.
return choosePort(HOST, DEFAULT_PORT);
})
.then(port => {
if (port == null) {
// We have not found a port.
return;
}
const config = configFactory('development');
const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
const appName = require(paths.appPackageJson).name;
const useTypeScript = fs.existsSync(paths.appTsConfig);
const urls = prepareUrls(
protocol,
HOST,
port,
paths.publicUrlOrPath.slice(0, -1)
);
// Create a webpack compiler that is configured with custom messages.
const compiler = createCompiler({
appName,
config,
urls,
useYarn,
useTypeScript,
webpack,
});
// Load proxy config
const proxySetting = require(paths.appPackageJson).proxy;
const proxyConfig = prepareProxy(
proxySetting,
paths.appPublic,
paths.publicUrlOrPath
);
// Serve webpack assets generated by the compiler over a web server.
const serverConfig = {
...createDevServerConfig(proxyConfig, urls.lanUrlForConfig),
host: HOST,
port,
};
const devServer = new WebpackDevServer(serverConfig, compiler);
// Launch WebpackDevServer.
devServer.startCallback(() => {
if (isInteractive) {
clearConsole();
}
if (env.raw.FAST_REFRESH && semver.lt(react.version, '16.10.0')) {
console.log(
chalk.yellow(
`Fast Refresh requires React 16.10 or higher. You are using React ${react.version}.`
)
);
}
console.log(chalk.cyan('Starting the development server...\n'));
openBrowser(urls.localUrlForBrowser);
});
['SIGINT', 'SIGTERM'].forEach(function (sig) {
process.on(sig, function () {
devServer.close();
process.exit();
});
});
if (process.env.CI !== 'true') {
// Gracefully exit when stdin ends
process.stdin.on('end', function () {
devServer.close();
process.exit();
});
}
})
.catch(err => {
if (err && err.message) {
console.log(err.message);
}
process.exit(1);
});
Which starts a Webpack server:
const devServer = new WebpackDevServer(serverConfig, compiler);
// Launch WebpackDevServer.
devServer.startCallback(() => {
and then opens the browser:
openBrowser(urls.localUrlForBrowser);
while running npm run eject, you can find a config folder with all configuration files. Inside path.js file you can find the default path and if want you can change the default path there. Be careful while doing this since it is an irreversible process
Ref :http://blog.teamtreehouse.com/getting-started-create-react-app-tool

Categories