How to use TypeGraphQL with tsyringe - typegraphql

I have a project using tsyringe and a RESTFul API. Now i should to add a GraphQL API using the type-graphql but in our documentation no have some example using it.
Is possiblem to use tsyringe to make DI with TypeGraphQL?

Yes, it's possible!
Most IOC containers have the get function to retrieve an instance of the given class type. tsyringe instead have the resolve function.
Therefore, you have to change the container option when building the schema to instruct type-graphql how to retrieve (get) a class instance from a class type:
import { container } from 'tsyringe';
const schema = await buildSchema({
// ...
container: { get: (cls) => container.resolve(cls) }
});

Related

Nuxt class-based services architecture (register globally; vs manual import)

In my Nuxt app I'm registering app services in a plugin file (e.g. /plugins/services.js) like this...
import FeatureOneService from '#/services/feature-one-service.js'
import FeatureTwoService from '#/services/feature-two-service.js'
import FeatureThreeService from '#/services/feature-three-service.js'
import FeatureFourService from '#/services/feature-four-service.js'
import FeatureFiveService from '#/services/feature-five-service.js'
export default (ctx, inject) => {
inject('feature1', new FeatureOneService(ctx))
inject('feature2', new FeatureTwoService(ctx))
inject('feature3', new FeatureThreeService(ctx))
inject('feature4', new FeatureFourService(ctx))
inject('feature5', new FeatureFiveService(ctx))
}
After doing this I can access any of my service on vue instance like this.$feature1.someMethod()
It works but I've once concern, that is, this approach loads all services globally. So whatever page the user visits all these services must be loaded.
Now I've 20+ such services in my app and this does not seem optimal approach to me.
The other approach I was wondering is to export a singleton instance within each service class and import this class instance in any component which needs that service.
So basically in my service class (e.g. feature-one-service.js) I would do like to do it like this..
export default new FeatureOneService() <---- I'm not sure how to pass nuxt instance in a .js file?
and import it my component where it is required like so...
import FeatureOneService from '#/services/feature-one-service.js'
What approach do you think is most feasible? if its the second one, then how to pass nuxt instance to my singleton class?
Yep, loading everything globally is not optimal in terms of performance.
You will need to either try to use JS files and pass down the Vue instance there.
Or use mixins, this is not optimal but it is pretty much the only solution in terms of reusability with Vue2.
Vue3 (composition API) brings composables, which is a far better approach regarding reusability (thing React hooks).
I've been struggling a lot with it and the only solution is probably to inject services to the global Vue instance at the component/page level during the initialisation (in created hook), another option is to do that in the middleware (or anywhere else where you have access to the nuxt context. Otherwise you won't be able to pass nuxt context to the service.
I usually set up services as classes, call them where necessary, and pass in the properties of the context which the class depends on as constructor arguments.
So for example, a basic MeiliSearchService class might look like:
export class MeilisearchService {
#client: MeiliSearch
constructor($config: NuxtRuntimeConfig) {
super()
this.#client = new MeiliSearch({
host: $config.services.meilisearch.host,
apiKey: $config.services.meilisearch.key
})
}
...
someMethod() {
let doSomething = this.#client.method()
...
}
...
}
Then wherever you need to use the service, just new up an instance (passing in whatever it needs) and make it available to the component.
data() {
const meiliSearchService = new MeiliSearchService(this.$config)
return {
meiliSearchService,
results,
...
}
},
methods: {
search(query) {
...
this.results = this.meiliSearchService.search(query)
...
}
}
As I'm sure you know, some context properties are only available in certain Nuxt life-cycle hooks. I find most of what I need is available everywhere, including $config, store, $route, $router etc.
Don't forget about Vue's reactivity when using this approach. For example, using a getter method on your service class will return the most recent value only when explicitly called. You can't, for example, stick the getter method in a computed() property and expect reactivty.
<div v-for='result in latestSearchResults'>
...
</div>
...
computed: {
latestSearchResults() {
return this.#client.getLatestResults()
}
}
Instead, call the method like:
methods: {
getLatestResults() {
return this.#client.getLatestResults()
}
}

Access data model in VueJS with Cypress (application actions)

I recently came across this blog post: Stop using Page Objects and Start using App Actions. It describes an approach where the application exposes its model so that Cypress can access it in order to setup certain states for testing.
Example code from the link:
// app.jsx code
var model = new app.TodoModel('react-todos');
if (window.Cypress) {
window.model = model
}
I'd like to try this approach in my VueJS application but I'm struggling with how to expose "the model".
I'm aware that it's possible to expose the Vuex store as described here: Exposing vuex store to Cypress but I'd need access to the component's data().
So, how could I expose e.g. HelloWorld.data.message for being accessible from Cypress?
Demo application on codesandbox.io
Would it be possible via Options/Data API?
Vue is pretty good at providing it's internals for plugins, etc. Just console.log() to discover where the data sits at runtime.
For example, to read internal Vue data,
either from the app level (main.js)
const Vue = new Vue({...
if (window.Cypress) {
window.Vue = Vue;
}
then in the test
cy.window().then(win => {
const message = win.Vue.$children[0].$children[0].message;
}
or from the component level
mounted() {
if (window.Cypress) {
window.HelloWorld = this;
}
}
then in the test
cy.window().then(win => {
const message = win.HelloWorld.message;
}
But actions in the referenced article implies setting data, and in Vue that means you should use Vue.set() to maintain observability.
Since Vue is exposed on this.$root,
cy.window().then(win => {
const component = win.HelloWorld;
const Vue = component.$root;
Vue.$set(component, 'message', newValue);
}
P.S. The need to use Vue.set() may go away in v3, since they are implementing observability via proxies - you may just be able to assign the value.
Experimental App Action for Vue HelloWorld component.
You could expose a setter within the Vue component in the mounted hook
mounted() {
this.$root.setHelloWorldMessage = this.setMessage;
},
methods: {
setMessage: function (newValue) {
this.message = newValue;
}
}
But now we are looking at a situation where the Cypress test is looking like another component of the app that needs access to state of the HelloWorld.
In this case the Vuex approach you referenced seems the cleaner way to handle things.

State is always undefined when using the #connectTo decorator in a config pipeline step in Aurelia Store

I have an Aurelia app using Aurelia Store. I'm having some trouble when using the #connectTo decorator in an Aurelia pipeline step.
I have added the following step to my config pipeline:
config.addPipelineStep('authorize', AuthorizeStep);
And this step looks like:
#connectTo()
export class AuthorizeStep {
state: State;
run(navigationInstruction, next) {
if (navigationInstruction.getAllInstructions().find(x => x.config.isAdmin))
{
if (!this.state.user.isAdmin) {
return next.cancel();
}
}
return next();
}
}
However, my state is always undefined. Looking at other parts of my project, I can see the state and user are being populated, but it seems like in this AuthorizeStep it doesn't seem to work.
I think this issue may be due to the fact that my AuthorizeStep doesn't have a bind lifecycle method, but if so, what can I do about this?
The maintainers of Aurelia responded (only after I raised an issue on their GitHub) here.
Basically, as the bind lifecycle does not exist within this class, the #connectTo decorator won't work. Instead, I will need to manually inject the Store and subscribing to the state.

Hot to add POST interceptor to aurelia http client

Basically, the question says it all :). The scenario is as follows: I have some python models that are being passed between the browser, and the server. Python convention for naming things is to use underscores, and js convention is to camelCase everything. So I figured, I'd just create an http request interceptor to convert between python_models and jsModels. Is there a simple way to accomplish that?
I'm also looking for a way to do the inverse, so camelCase to that-case :)
If you're using aurelia-http-client, you can use a reviver.
import {HttpClient} from 'aurelia-http-client';
import {Person} from './models';
export class PersonService {
constructor(){
this.http = new HttpClient().configure(x=> {
x.withReviver((k,v) => {
return typeof v === 'object' ? new Person(v) : v;
});
});
}
getPeople(){
return this.http.get('/people');
}
}
This only works for aurelia-http-client and not aurelia-fetch-client. It has been talked about in the fetch spec, but I don't believe it is currently implemented.
Check the following for more information:
(my) Best Practices in Aurelia: The Model
https://github.com/whatwg/fetch/issues/104

Session Data with Durandal

I am just getting started with Durandal.js so excuse me for the silly quesstion...
When a user makes it's first request to the app it is asked to choose a 'profile kind', and I need it to be accessible to every other view model in the web site, I first though of creating this property in the shell viewmodel, but don't how to do it.
How is the best way to store data in a Session like mode in a Durandal SPA?
Thanks!
Create an amd module for what data you need to store.
Then just require that module as a dependency for whatever other modules that need it.
Sort of like this:
session module
define(function () {
return {
someVariable: 'value1',
someVariable2: 'value2'
}
})
some other module
define(['session'], function(session) {
return {
getValue1: function () {
return session.someVariable;
},
obs1: ko.observable(session.someVariable2)
}
})
EDIT**
AMD modules are there to not pollute the global namespace of the window object. But if you would rather not require your session as a dependency and just access it through a global variable then that is perfectly fine.
you can declare it in your shell.js if you would like and do something like:
define(function () {
window.session = { someVariable: 'value1', someVariable2: 'value2' };
})
then inside some other module you can access the session object like so:
define(function() {
return {
getValue1: function () {
return session.someVariable;
},
obs1: ko.observable(session.someVariable2)
}
})
This information will not be persisted between page refreshes.. its only in-memory.
If your looking to persist the session data I would not look into persisting any information on the client unless you planned on making your app an off-line application.
An offline application is an app that works even w/out internet access. But if your app requires that the user is always connected to the internet then I would just store the session data on the server. So, just use web services to persist the session data and retrieve it.
You can tie the session on the server to the client by using a cookie.
As an alternative to Evan's answer, which is definitively the correct AMD approach... have you considered using a global object for that purpose?
in main.js
window.myApp = {
prop1: 'value',
subOne: {
prop1: 'value'
}
...
}
That will allow you to access the global myApp object everywhere. I know that some people will consider that as global namespace pollution and in general a bad practice, but in a SPA project (where you control window) I'd consider this still a viable approach.