Svelte framework: pass environment variables to client-side bundle at runtime - javascript

Posted this to the Svelte repo as well:
I just made my first Svelte app over the weekend, and really liked the experience. One thing I'm curious about, that I wasn't able to figure out with a decent amount of research, is if/how one could pass a runtime env var or similar to a client-side script so it's available in the bundle/browser. This probably isn't considered a "best practice", so maybe I'm just on my own here, but in Pug for example you can do something like the following (from a Hapi.js route handler for example):
const context = {
foo: bar,
baz: ''
}
return h.view('index', context)
These vars are then available in the Pug context.
In my toy app I wanted to make it possible to pass an api key at server start time (either from a .env or CLI), and inject that from the Express server like so:
app.use(express.static(`${__dirname}/public`))
and have that var be available in the client script. Again, it's probably not a best practice to be injecting api keys into client-side scripts and making calls from there, but is this kind of variable passing possible in Svelte?
It seems like this should be possible using either rollup-plugin-inject or rollup-plugin-replace, but I wasn't able to figure out how to make that work. This is definitely not a criticism of the framework, but perhaps a section on working with env vars would be a useful addition to the Svelte docs. Thanks!

Typically this is the sort of thing you would do in your build config. From the rollupjs tag I'll assume you're using that — you could use rollup-plugin-replace to inject the content you need:
// rollup.config.js
import replace from 'rollup-plugin-replace';
import svelte from 'rollup-plugin-svelte';
export default {
input: 'src/main.js',
output: {
file: 'public/bundle.js',
format: 'iife'
},
plugins: [
svelte(),
replace({
// you're right, you shouldn't be injecting this
// into a client script :)
'API_KEY': process.env.API_KEY
})
]
};

Related

Dependancy injection using Tsyringe for multiple implementation of interface in Typescript

Context
I am currently working on a Typescript Lambda project where we are planning to refactor our code to make use of dependency injection using the Tsyringe library. We have a typical MVC structure for projects except instead of the Repo/Database layer we have a proxy layer which calls a third-party service over the rest API to fetch the required data.
The catch is that the proxy layer will have a single interface defined and it will have multiple implementations among which one needs to be injected depending upon the business decision. For example AuthProxy is an interface which contains a login method, and it has two different implementation classes KeycloakAuthProxyImpl and AuthZeroAuthProxyImpl. These two implementations will be in 2 separate folders say AuthZero and KeyCloak and while building we pass an argument like --folderName so only one implementation will be available in runtime for dependency injection.
The problem
The problem we are facing with Tsyringe (I have evaluated some other libraries too) is that interface-based dependency injection needs explicit token-based registration with ioc-container in the main.ts page(In my case, the handler function file). So as per theory, I should be registering it as follows.
.
But in our case, this is not possible. Because say we are building it as --keycloak as an argument, then AuthZearoAuthProxyimpl will be ignored while compiling and hence the code will break in line 14 at runtime.
We tried to move that dependency registration logic to the corresponding implementation class so that each implementation class will be self-contained and isolated so that there won't be any runtime issues. But then these are not even being registered for dependency injection and we get an error saying Attempted to resolve unregistered dependency token: "AuthProxy". This is expected as per the file loading of javascript.
KeycloakImpl class.
.
We even tried using #registry decorator which can be found commented in the images, but it also didn't make any difference.
Even though I haven't tried any other dependency injection libraries of Typescript, from my little research, most of them follow more or less the same pattern for interface-based dependency injection and I am anticipating the same issue in those also. Is there any other workaround through which I can resolve this issue, or is it even possible with typescript?
PS: I don't have much expertise in js and typescript, the above keywords are based on my experience with spring and java. Please ignore if I have misused any js specific terminologies while explaining the issue.
Code and project structure
I had similar problems with tsyringe and I found a much better approach.
Here is how I would solve this with a different DI lib iti and also remove many lines of code:
import { createContainer } from "iti"
import express from "express"
// import other deps
const container = createContainer()
.add({
authProxy: () =>
Math.random() > 0.5
? new KeycloakAuthProxyImpl()
: new AuthZearoAuthProxyImpl(),
})
.add((ctx) => ({
authService: () => new AuthServiceV3(ctx.authProxy),
}))
.add((ctx) => ({
server: () => {
const app = express()
app.all("/login", async (req, res) => handler(req, ctx.authService))
return app
},
}))
// start your server / lambda
const server = container.get("server")
server.listen(3000)
I've also refactor other parts of the app and got rid of singletons and made code IMO a bit simpler to read
I’ve created an interactive playground with a mock your app:
https://stackblitz.com/edit/json-server-ntssfj?file=index.ts
Here are some links to the lib:
https://github.com/molszanski/iti
https://itijs.org/

Get language from nuxt.js middleware

Is there a way in nuxt.js middleware to know the language used for the request (of the page)?
By taking the example proposed by nuxt.js (https://nuxtjs.org/api/pages-middleware/) and by supposing that for example a page declined in two languages (French and English) with the following urls:
/fr/ma-page
/en/my-page
When visiting the first or second url, can we have an indicator on the language used directly in the middleware? Or should you parse the page url to find out (route)?
Because I want to redirect the user according to the language requested...
If you're using nuxt-i18n module, is really simple, and you have two ways of doing it. I'll suggest the first, but I think it would be cool to give a perspective on how the module works.
Both of the solutions have the same starting point, getting the i18n from the middleware context. i18n should be accessed from the app parameters from the context, so we start from here:
export default async function({ app: { i18n } }) {}
1. The best way: letting the i18n module do the hard lifting
The same way you can access the translations inside Vue components using the $t method, we could use it from the i18n object directly.
So if you would write in a Vue Component
$t('components.login.register')
Then in the middleware you would do
i18n.t('components.login.register')
2. Other option: getting to know the i18n module better
If you inspect the i18n object that the module exposes, you can find the following parameters is contains:
locale - That represents the current language
messages - An object that keeps the translations for each active language
so you could easily get the previous example translation like this:
const { messages, locale } = i18n
const currentTranslations = messages[locale]
const wantedTranslation = currentTranslations.components.login.register
I'd suggest for you to use the i18n.t module, as it works some of the abstractions you'd need to rewrite yourself, but it's a good example to understand the module better.

Integrating Instana error tracking into an Angular 2 application

I'm trying to integrate Instana into an application. More specifically I'm trying to send errors from my Angular app to Instana. I have my code 'working' so this is kind of a question about best practice.
Instana's Backend Correlation documentation defines functions in 'window' from what I understand. I've set something similar to this in my index.html.
<script>
(function(i,s,o,g,r,a,m){i['InstanaEumObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//eum.instana.io/eum.min.js','ineum');
ineum('apiKey', 'someKey');
ineum('traceId', 'backendTraceId'); //replace 'backendTraceId with the appropriate expression for retrieval
</script>
The issue I have is when I try to follow Instana's guide for Angular 2+ Integration regarding error tracking, where they call one of the methods that I can access from window. The guide just straight up calls the function ineum(...) by itself. When I try to do this, my project wouldn't compile.
class CustomErrorHandler implements ErrorHandler {
handleError(error) {
ineum('reportError', error);
// Continue to log caught errors to the console
console.error(error);
}
}
My current fix is: (<any>window).ineum('reportError', errorContext); But I was looking at another stack overflow question where they accessed window differently in their javascript.
Is typecasting window to 'any' a bad practice?
Is it better to try window['ineum'](...) or is that just a syntax preference?
Is it better to try to define functions in another file, say an Instana service of sorts, and then use that service in the index.html script and CustomErrorHandler or keep it in the window? (Though this one may be a little tricker for me to figure out)
Sorry for my confusion as this may not be an actual issue because my code is 'working' but I just want clarification on this. I'm not sure if I just didn't follow Instana's guides correctly but this was the best of what I could find. I tried reaching out via their contact page but I haven't received a response quite yet.
Instana has a demo application that shows to do this.
To summarize the parts that you would need:
Ensure that you have a local directory configured within your TypeScript config that allows definition of custom typings:
// common file name: tsconfig.app.json
{
// other configuration…
"typeRoots": [
// other configuration…
"./custom-typings"
]
}
Declare the global ineum function in a file within this directory:
// common file name globals.d.ts
declare function ineum(s: string, ...parameters: any[]): any;
The combination of these two steps will make TypeScript aware of the function. Now you can use ineum just like any other global.

Vue load a JS file with a javascript object in a runtime

I am building a no-code application using Vue. I have an idea that I have some JSON file and import it on runtime. However, JSON file don't allow to add a function. I change to use a javascript or typescript file to store the JSON object like below.
export {
"is": "button",
"props": {
"type": "button"
},
"events": {
"click": () => {
// do something
}
}
}
Currently, I want to trigger the import when Vue created the component.
{
created () {
// import here
}
}
I have two methods that I have tried:
Add a script tag but I need something to store it first, like window
use import but it only supports local file.
I store these files in backend and load each of them when frontend need it. Is there any better solution? Please help and thank you.
It sounds like you essentially want to do something like deferred module loading, if you use Webpack you may want to have a look at this: https://alexjover.com/blog/lazy-load-in-vue-using-webpack-s-code-splitting/
I'm not sure I can recommend the approach that you're taking but if you do want to stick with it, your solution would probably look something like:
Make an HTTP get request to the URL of your file. This should be a plain text response, not JSON. The file itself can be a .js file containing an object literal ({ a: ..., b: ..., c: ... }) with function definitions.
Once you get the text, evaluate it with eval(...the response) and save it to a local variable which you can then use. Note that if the code in your file has any dependencies, you will have to come up with some way to bring those over as well.
Clearly though, this is a lot of trouble and probably not worth it. Code splitting in general is only worth the effort if it's a fairly big app; for smaller apps the cost of the round trips outweighs the cost of loading the initial bundle.
In any case, you'll likely have more luck using Vue's baked-in dynamic import system than a hand-rolled solution, but it really just depends on what you're trying to achieve here.

A clean way to allow React shared component to get configuraiton property without having to pass it all the way down

I have about 8 simple UI's for different applications, all of them make use of a shared components library I also produce. I recently added a feature to modify how querying is done in the low levels of a some utility functions used in all the saga of all my UIs.
I want each of my UIs to be able to include an optional configuration value to tweak this new querying logic in their configuration files. However, the method that needs the property is very low level, I'd prefer not to have to add an extra property to 5-6 methods to pass one value down to the method that needs it.
These shared components are used only for my UI, which all have a standard format. As such I don't need them to be fully generic, I have no complaint with hard coding their checking the standard location of my configuration file in my UIs. However, since the shared components are a separate NPM package I don't know how to reference the location of a configuration file in the applications calling the UI.
Is there any clean way to get the value from the configuration file other then just passing it through all the relevant methods?
It's simple. You can create configuration file where you'll export your logic. And when you need that config, import that and use. Here's an example:
configuration.js
export const test = (myvar) => {
return 'test: ' + myvar
}
component.js
import { test } from 'configuration.js'
test('pass') // test: passed

Categories

Resources