Closed michaelbromley closed 4 months ago
I can only agree to the things said above. One thing I would like to add is some kind of "Nest.js in the context of Vendure". It took me a long time to understand these decorators and the architecture with resolvers, modules, providers and services. Such a video or written guide would help immensely in the beginning when one is writing the first plugins.
There have been a few times where I have searched docs and had been pointed to somewhere else in docs. I feel like sometimes it can be tricky to find all the locations, even just a "look here for more" link where docs might overlap?
For example: I followed https://docs.vendure.io/typescript-api/core-plugins/payments-plugin/braintree-plugin/ and although it mentioned database update/sync this was something I thought happened automatically and not something I needed to configure. Pointing to here https://docs.vendure.io/developer-guide/migrations/#synchronize-vs-migrate would have cleared it up? Just food for thought.
Ideas for guides layout:
Other guides that need writing:
In the NestJS guide add a recipe for defining global providers like
import { VendurePlugin } from '@vendure/core';
import { APP_FILTER } from '@nestjs/core';
@VendurePlugin({
providers: [
{
provide: APP_FILTER,
useClass: MyExceptionFilter,
},
],
})
export class MyPlugin {}
What I struggled with was the migration (I generated duplicates, without running the migration and restarting the app), this of course is covered in other guides. But I think some best practices on migrations in a dedicated guide might help some newly developers.
Preview of ongoing work: https://vendure-docs-beta.netlify.app/guides/getting-started/installation
beautyfull UI interface!!!!
If you can add in the marketplace docs, fundamentalmente parts needed to build a marketplace. I think it would would be the only one in the world open source fulling documented with cutting edge tech.
Running list of some findings:
VendureConfig
object is in src/vendure-config.ts
DefaultJobQueuePlugin
should be turned off if BullMQJobQueuePlugin
is enabled.I have recently started working with Vendure. As impressed as I am with the toolbox, there are a few areas where I really went down rabbit holes as I started building a custom plugin that required fields from other custom plugins. I would suggest adding documentation on how to ensure you can access customFields and extensions on e.g. OrderLine or Customer inside a custom plugin resolver you are building, when those extensions were made by other plugins you are loading.
What I run into is that sometimes properties are loaded by services offered by those third party plugins or you are using the @vendure/core services. But in both cases, I'm struggling to get access to customFields created by other plugins that I know are there, but are not being loaded. Since you you are already "behind the resolver in the service layer", you can no longer rely on any kind of resolver based resolution to get those. Anyways, would love to get a howto on that.
@mschipperheyn thanks, this is very good feedback and yes, not knowing which relations are joined is one of the main shortcomings of the TypeORM type system. Are you able to provide any small examples of things that are causing problems? E.g. what you tried that you expected to work but it didn't?
Hi @michaelbromley let me try to give you that example in a summary way
So, I'm building a plugin that uses two existing plugins, one is @pinelab customer-managed-groups. This plugin allows you to assign users that you consider as part of your group, e.g. family members. Another plugin we use allows you to assign that group member to an orderline, so you can buy products for your child.
import {
Customer,
LanguageCode,
PluginCommonModule,
VendurePlugin,
Allow,
Transaction,
} from "@vendure/core"
import gql from "graphql-tag"
import { Args, Mutation, Resolver } from "@nestjs/graphql"
import { Ctx, RequestContext, OrderService } from "@vendure/core"
import { Permission, OrderLine } from "@vendure/common/lib/generated-types"
import { isFunctionTypeNode } from "typescript"
const assignCustomerSchemaExtension = gql`
extend type Mutation {
assignCustomerToOrderLine(orderLineId: ID!, customerId: ID!): OrderLine!
}
`
@Resolver("OrderLine")
export class OrderLineAssignmentResolver {
constructor(private orderService: OrderService) {
console.log("OrderLineAssignmentResolver constructor called")
}
@Transaction()
@Mutation()
@Allow(Permission.UpdateCatalog)
async assignCustomerToOrderLine(
@Ctx() ctx: RequestContext,
@Args() args: any
) {
const order = await this.orderService.findOneByOrderLineId(
ctx,
args.orderLineId
)
const orderId = order!.id
console.log("assignCustomerToOrderLine called with args: ", args)
// error checking
return this.orderService.adjustOrderLine(
ctx,
orderId,
args.orderLineId,
1,
{ assignedCustomer: { id: args.customerId } }
)
}
}
@VendurePlugin({
imports: [PluginCommonModule],
shopApiExtensions: {
schema: assignCustomerSchemaExtension,
resolvers: [OrderLineAssignmentResolver],
},
configuration: (config) => {
config.customFields.OrderLine.push({
name: "assignedCustomer",
type: "relation",
entity: Customer,
eager: false,
defaultValue: false,
nullable: true,
label: [{ languageCode: LanguageCode.en, value: "Assigned Member" }],
})
return config
},
compatibility: "^2.0.0",
})
export class OrderLineAssignmentPlugin {}
One feature is that the plugin adds a customField to an OrderLine.
So, on my side, I'm building a plugin that ensures that you can't select any products that were already bought for user X or family member Y. So, I use pinelabs order retrieval for family members and try to verify that assignedCustomer. It doesn't exist on that (I guess retrieval is not using *) and also when I use the @vendure/core order manager, I cannot access the assignedCustomer on the retrieved orderLine.
So, I'm looking for how to get that assignedCustomer loaded up and have it recognized as a valid typescript property. That last part I think I'll be able to get through. The first part is trickier.
HTH
Thanks for this detailed explanation, it is helpful!
So regarding the TS typings for custom fields, this is covered here: https://beta-docs.vendure.io/guides/developer-guide/custom-fields/#typescript-typings - let me know if that is helpful on that point.
Regarding the broader issues brought up, I think we need 2 sections added to the docs:
This section could be a sub-heading of this section which explains how relations work, i.e.
relations
argument which allows you to specify which relations to load, e.g.const order = await this.orderService.findOne(ctx, id, ['lines.customFields.assignedCustomer']);
A new section in the custom fields guide which demonstrates patterns for accessing custom fields, e.g.
const orderLine = await this.connection.getRepository(ctx, OrderLine).findOne({
where: { id },
relations: {
customFields: {
assignedCustomer: true,
}
}
})
or with EntityHydrator:
const order = await this.orderService.findOne(ctx, id);
await this.entityHydrator.hydrate(ctx, order, { relations: ['lines.customFields.assignedCustomer'] });
One to add the the Admin UI customization docs:
Add IDE plugin config example:
name: Vendure GraphQL Schema
schema: schema.graphql
extensions:
endpoints:
admin:
url: http://localhost:3000/admin-api
shop:
url: http://localhost:3000/shop-api
Add section on mocking services in e2e tests: https://discord.com/channels/1100672177260478564/1147481709206568981/1148135234349584496
Add section on all aspects of email handling, including how to toggle dev/prod mode in the email config:
EmailPlugin.init({
handlers: [
// ...
],
templatePath: path.join(rootDir, 'static/email/templates'),
globalTemplateVars: {
// ...
},
...(DEV_MODE
? {
route: 'mailbox',
devMode: true,
outputPath: path.join(rootDir, 'static/email/output'),
}
: {
transport: {
type: 'smtp',
host: process.env.SMTP_HOST,
port: +process.env.SMTP_PORT,
//...
},
}),
Another suggestion I have is to add a section to Error Handling that explains how to create your own Errors, in particular how to ensure that say a validation error gets converted to an ErrorResult if you use an ErrorResult union. My first impression is that it's prob not desirable to extend on Vendure's error result. I'm not even sure if it's possible to extend that ErrorCode
enum. So, it's prob more of a nestjs style question but it's a scenario that's likely common if you're building your own plugins.
yes, it should be expose on docs :)
// extends graphql `ErrorCode`
extend enum ErrorCode {
CUSTOM_ERROR_CODE
}
type CustomPluginError implements ErrorResult {
errorCode: ErrorCode!
message: String!
}
// Create custom error class?
export class CustomPluginError {
// eslint-disable-next-line @typescript-eslint/naming-convention
readonly __typename = 'CustomPluginError';
constructor(
private readonly message: string,
private readonly errorCode: string
) {
console.log('runhere');
}
}
// Throw the error?
return new CustomPluginError(
`Some error ocurr here?`,
ErrorCode.CUSTOM_ERROR_CODE
);
From what I can tell from the issues (https://github.com/vendure-ecommerce/vendure/issues/437) and the repo there is actually a plugin for this
However, on my side, I do see the enum ErrorCode
values being auto generated but not the error class being auto generated. I'm not so familiar with codegen and I get how it's supposed to work through graphql-errors-plugin
but all of this is a bit auto-magical and hard to debug.
I finally got it. I have been dumb, but it is an easy one to trip over if you don't use you brain. You have use the isGraphQlErrorResult
(or some other method) to determine if the error that is occurring in your resolver method is a an ErrorResult. In that case, you have to return it in stead of throwing it.
Another suggestion is documentation on how to handle T_id style ids in testing. Bc they are leading to not found results for me. Ids are stored as numbers 1,2,3
in our test database, but returned through the graphql database as T_1, T_2, T-3
through the graphql api. I'm guessing there must be a standard way of dealing with them on the service side. I mean I can write a utility method, but my guess is I'm doing something wrong. Reviewing the entity id strategy, that must be it
Documentation improvements: Add fields to existing types
Some aspects on adding custom fields to existing domain objects are not clear in the documentation: Context: I want to add an avatar to the Administrator.
customFields
object. The ProductVariant example makes clear that it's possible but not how to configure it at the entity level, since the documented way adds fields at the customFields level.I added the avatar field as a custom field. Then to avoid ad 3, I tried to add something like
extend type AdministratorCustomFields {
avatar: Asset
}
extend type Administrator {
customFields: AdministratorCustomFields
}
But since AdministratorCustomFields didn't exist yet, I ran into ad 1.
extend type Administrator {
customFields: {
avatar: Asset
}
}
is illegal
Background
Vendure is dedicated to providing an outstanding developer experience. A major part of that is our documentation at https://docs.vendure.io. The docs have a solid foundation of correctness (all API docs are generated from source in a readable, linked way) and coverage in the form of many pages of hand-written guides.
However, in the spirit of continual improvement, I am launching an effort to further improve the documentation with the following goals (in order of priority):
Improving information architecture
Right now we have a single navbar on the left with a tree structure. There is a visual break between the hand-written guides and the generated API docs:
I think we can improve the information architecture to something along the lines of the new React docs: https://react.dev/learn
They make the top-level split between "learn" (guides) and "reference" (API docs).
Within "learn" they have a "get started" and "learn react" sub-sections, and then these break down into the major conceptual areas: "describing the ui", "adding interactivity" etc. Each topic is a very comprehensive, tutorial-like article:
In our case, the "learn vendure" topic areas could be something like:
The "administrator guide" which contains instructions for using the Admin UI could be moved to a different section, e.g. "operator's manual".
Task-focused
We should move towards a more task-focused approach. There are common tasks that devs want to complete with Vendure - we need to anticipate these and provide a complete, step-by-step guide to doing it, including copy-pastable code snippets.
More complex tasks that require many related parts in different files can be complemented with an actual working plugin in the dev-server/documentation-examples directory.
Improved search
Feedback from a user:
We can improve search using keywords, and perhaps a full-on search engine rather than the client-side index search we currently have.