-
-
Notifications
You must be signed in to change notification settings - Fork 1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
40 changed files
with
7,067 additions
and
5,732 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
105 changes: 105 additions & 0 deletions
105
docs/content/plugins/plugin-examples/defining-db-subscribers.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
--- | ||
title: "Defining database subscribers" | ||
showtoc: true | ||
--- | ||
|
||
# Defining database subscribers | ||
|
||
TypeORM allows us to define [subscribers](https://typeorm.io/listeners-and-subscribers#what-is-a-subscriber). With a subscriber, we can listen to specific entity events and take actions based on inserts, updates, deletions and more. | ||
|
||
If you need lower-level access to database changes that you get with the [Vendure EventBus system]({{< relref "event-bus" >}}), TypeORM subscribers can be useful. | ||
|
||
## Simple subscribers | ||
|
||
The simplest way to register a subscriber is to pass it to the `dbConnectionOptions.subscribers` array: | ||
|
||
```TypeScript | ||
import { Product, VendureConfig } from '@vendure/core'; | ||
import { EntitySubscriberInterface, EventSubscriber, UpdateEvent } from 'typeorm'; | ||
|
||
@EventSubscriber() | ||
export class ProductSubscriber implements EntitySubscriberInterface<Product> { | ||
listenTo() { | ||
return Product; | ||
} | ||
|
||
beforeUpdate(event: UpdateEvent<Product>) { | ||
console.log(`BEFORE PRODUCT UPDATED: `, event.entity); | ||
} | ||
} | ||
|
||
// ... | ||
export const config: VendureConfig = { | ||
dbConnectionOptions: { | ||
// ... | ||
subscribers: [ProductSubscriber], | ||
} | ||
} | ||
``` | ||
The limitation of this method is that the `ProductSubscriber` class cannot make use of dependency injection, since it is not known to the underlying NestJS application and is instead instantiated by TypeORM directly. | ||
|
||
If you need to make use of providers in your subscriber class, you'll need to use the following pattern. | ||
|
||
## Injectable subscribers | ||
|
||
By defining the subscriber as an injectable provider, and passing it to a Vendure plugin, you can take advantage of Nest's dependency injection inside the subscriber methods. | ||
|
||
```TypeScript | ||
import { | ||
PluginCommonModule, | ||
Product, | ||
TransactionalConnection, | ||
VendureConfig, | ||
VendurePlugin, | ||
} from '@vendure/core'; | ||
import { Injectable } from '@nestjs/common'; | ||
import { EntitySubscriberInterface, EventSubscriber, UpdateEvent } from 'typeorm'; | ||
import { MyService } from './services/my.service'; | ||
|
||
@Injectable() | ||
@EventSubscriber() | ||
export class ProductSubscriber implements EntitySubscriberInterface<Product> { | ||
constructor(private connection: TransactionalConnection, | ||
private myService: MyService) { | ||
// This is how we can dynamically register the subscriber | ||
// with TypeORM | ||
connection.rawConnection.subscribers.push(this); | ||
} | ||
|
||
listenTo() { | ||
return Product; | ||
} | ||
|
||
async beforeUpdate(event: UpdateEvent<Product>) { | ||
console.log(`BEFORE PRODUCT UPDATED: `, event.entity); | ||
// Now we can make use of our injected provider | ||
await this.myService.handleProductUpdate(event); | ||
} | ||
} | ||
|
||
@VendurePlugin({ | ||
imports: [PluginCommonModule], | ||
providers: [ProductSubscriber, MyService], | ||
}) | ||
class MyPlugin {} | ||
|
||
// ... | ||
export const config: VendureConfig = { | ||
dbConnectionOptions: { | ||
// We no longer need to pass the subscriber here | ||
// subscribers: [ProductSubscriber], | ||
}, | ||
plugins: [ | ||
MyPlugin, | ||
], | ||
} | ||
``` | ||
|
||
## Troubleshooting subscribers | ||
|
||
An important factor when working with TypeORM subscribers is that they are very low-level and require some understanding of the Vendure schema. | ||
|
||
For example consider the `ProductSubscriber` above. If an admin changes a product's name in the Admin UI, this subscriber **will not fire**. The reason is that the `name` property is actually stored on the `ProductTranslation` entity, rather than on the `Product` entity. | ||
|
||
So if your subscribers do not seem to work as expected, check your database schema and make sure you are really targeting the correct entity which has the property that you are interested in. | ||
|
119 changes: 119 additions & 0 deletions
119
docs/content/storefront/configuring-a-graphql-client.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
--- | ||
title: "Configuring a GraphQL Client" | ||
weight: 2 | ||
showtoc: true | ||
--- | ||
|
||
# Configuring a GraphQL Client | ||
|
||
This guide provides examples of how to set up popular GraphQL clients to work with the Vendure Shop API. These examples are designed to work with both the `bearer` and `cookie` methods of [managing sessions]({{< relref "managing-sessions" >}}). | ||
|
||
## Apollo Client | ||
|
||
Here's an example configuration for [Apollo Client](https://www.apollographql.com/docs/react/) with a React app. | ||
|
||
```TypeScript | ||
import { | ||
ApolloClient, | ||
ApolloLink, | ||
HttpLink, | ||
InMemoryCache, | ||
} from '@apollo/client' | ||
import { setContext } from '@apollo/client/link/context' | ||
|
||
const AUTH_TOKEN_KEY = 'auth_token'; | ||
|
||
const httpLink = new HttpLink({ | ||
uri: `${process.env.NEXT_PUBLIC_URL_SHOP_API}/shop-api`, | ||
withCredentials: true, | ||
}); | ||
|
||
const afterwareLink = new ApolloLink((operation, forward) => { | ||
return forward(operation).map((response) => { | ||
const context = operation.getContext(); | ||
const authHeader = context.response.headers.get('vendure-auth-token'); | ||
if (authHeader) { | ||
// If the auth token has been returned by the Vendure | ||
// server, we store it in localStorage | ||
localStorage.setItem(AUTH_TOKEN_KEY, authHeader); | ||
} | ||
return response; | ||
}); | ||
}); | ||
|
||
const client = new ApolloClient({ | ||
link: ApolloLink.from([ | ||
setContext(() => { | ||
const authToken = localStorage.getItem(AUTH_TOKEN_KEY) | ||
if (authToken) { | ||
// If we have stored the authToken from a previous | ||
// response, we attach it to all subsequent requests. | ||
return { | ||
headers: { | ||
authorization: `Bearer ${authToken}`, | ||
}, | ||
} | ||
} | ||
}), | ||
afterwareLink, | ||
httpLink, | ||
]), | ||
cache: new InMemoryCache(), | ||
}) | ||
|
||
export default client; | ||
``` | ||
|
||
## Urql | ||
|
||
Here's an example using the [urql](https://formidable.com/open-source/urql/) client: | ||
|
||
```tsx | ||
import * as React from "react" | ||
import { createClient, dedupExchange, fetchExchange, Provider } from "urql" | ||
import { cacheExchange} from "@urql/exchange-graphcache" | ||
import { makeOperation} from "@urql/core" | ||
|
||
const AUTH_TOKEN_KEY = "auth_token" | ||
|
||
const client = createClient({ | ||
fetch: (input, init) => { | ||
const token = localStorage.getItem(AUTH_TOKEN_KEY) | ||
if (token) { | ||
const headers = input instanceof Request ? input.headers : init.headers; | ||
headers['Authorization'] = `Bearer ${token}`; | ||
} | ||
return fetch(input, init).then(response => { | ||
const token = response.headers.get("vendure-auth-token") | ||
if (token) { | ||
localStorage.setItem(AUTH_TOKEN_KEY, token) | ||
} | ||
return response | ||
}) | ||
}, | ||
url: process.env.NEXT_PUBLIC_URL_SHOP_API, | ||
exchanges: [ | ||
dedupExchange, | ||
cacheExchange({ | ||
updates: { | ||
Mutation: { | ||
addItemToOrder: (parent, args, cache) => { | ||
const activeOrder = cache.resolve('Query', 'activeOrder'); | ||
if (activeOrder == null) { | ||
// The first time that the `addItemToOrder` mutation is called in a session, | ||
// the `activeOrder` query needs to be manually updated to point to the newly-created | ||
// Order type. From then on, the graphcache will handle keeping it up-to-date. | ||
cache.link('Query', 'activeOrder', parent.addItemToOrder); | ||
} | ||
}, | ||
}, | ||
}, | ||
}), | ||
fetchExchange, | ||
], | ||
}) | ||
|
||
export const App = () => ( | ||
<Provider value={client}><YourRoutes /></Provider> | ||
) | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.