vendure icon indicating copy to clipboard operation
vendure copied to clipboard

Documentation improvements

Open michaelbromley opened this issue 1 year ago • 21 comments

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):

  • Make it easier to find the information you are looking for
  • Fill in missing docs, especially step-by-step guides for common tasks
  • Improve the overall look of the docs

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: image

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:

image

In our case, the "learn vendure" topic areas could be something like:

  • Writing a plugin
  • Extending the Admin UI
  • Deployment
  • Building a storefront

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:

I’d like to make a case for better search. I think the documentation has most of the information required to customize Vendure.

However, the documentation is laid out based on code and objects which means that I have to know Vendure and specific language to use the documentation which is counter intuitive.

We can improve search using keywords, and perhaps a full-on search engine rather than the client-side index search we currently have.

michaelbromley avatar Jul 19 '23 18:07 michaelbromley

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.

michaelbromley avatar Jul 20 '23 14:07 michaelbromley

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.

msj121 avatar Jul 23 '23 06:07 msj121

Ideas for guides layout:

  • Getting Started
    • Installation
    • Vendure key concepts (high-level overview of all the parts and how they fit together, a glossary of sorts).
    • Configuration
    • Writing a Vendure Plugin
  • How to guides
    • Custom Fields
    • Custom authentication
    • Customizing the Order Process
    • Define a custom entity
    • Using the Job Queue
    • Multi-vendor Marketplaces
    • Importing Product Data
    • Handling Images
    • Handling Emails
    • Shipping & Fulfillment
    • Payment Integrations
    • File uploads
    • Add a REST endpoint
    • Extending the GraphQL APIs
    • (more...)
  • Customizing the Admin UI
    • (similar to existing but more comprehensive with more examples)
  • Building a Storefront
    • Storefront starter kits
    • Connecting to the Vendure server
    • Storefront image handling
    • Product List
    • Product Detail
    • Checkout flow
    • Customer account management
  • Advanced topics
    • NestJS for Vendure developers
    • Testing
    • Stand-alone CLI scripts
    • Logging
    • Defining database subscribers
    • Custom permissions
  • Deployment
    • (similar to currently, but with more step-by-step guides)

michaelbromley avatar Jul 24 '23 08:07 michaelbromley

Other guides that need writing:

  • How to set up codegen
  • GraphQL for Vendure developers - including how to set up the IDE for autocompletion
  • How to sell digital goods
  • How to deploy to a VPS like Digital Ocean

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 {}

michaelbromley avatar Jul 25 '23 12:07 michaelbromley

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.

Caryntjen avatar Jul 25 '23 21:07 Caryntjen

Preview of ongoing work: https://vendure-docs-beta.netlify.app/guides/getting-started/installation

michaelbromley avatar Jul 28 '23 12:07 michaelbromley

beautyfull UI interface!!!!

tianyingchun avatar Jul 28 '23 14:07 tianyingchun

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.

NoahPerez avatar Jul 29 '23 23:07 NoahPerez

Running list of some findings:

  • [ ] 1. Admin UI > "Create new channel" > fields "Code" & "Channel token" need some explanation
  • [x] 2. Mention in https://docs.vendure.io/developer-guide/configuration/ that VendureConfig object is in src/vendure-config.ts
  • [ ] 3. "Note: The v1.x version of this plugin is designed to work with bullmq v1.x." - Unclear where to check the version of a plugin. Source: https://docs.vendure.io/typescript-api/core-plugins/job-queue-plugin/bull-mqjob-queue-plugin/. I see that on this page https://docs.vendure.io/typescript-api/core-plugins/job-queue-plugin/bull-mqplugin-options/ a version number is mentioned (v1.20)
  • [x] 4. In https://docs.vendure.io/typescript-api/core-plugins/job-queue-plugin/bull-mqjob-queue-plugin/ - will be good to mention if DefaultJobQueuePlugin should be turned off if BullMQJobQueuePlugin is enabled.
  • [ ] 5. Add some explanation on how sub collections is to be done in the Admin UI. This may not be the right place (https://docs.vendure.io/user-guide/catalog/collections/), so perhaps in the new Admin UI docs.
  • [ ] 6. Add a section on all the plugins in early release, which have not yet made their way to https://www.vendure.io/marketplace. As an example https://www.npmjs.com/package/vendure-stellate-plugin is not mentioned on the site or in the docs, hence there is no discovery for such.
  • [ ] 7. Snippets of code or "recipes" that people can use as starting points. Eg https://discord.com/channels/1100672177260478564/1135948518029271081/1136550398556131338
  • [ ] 8. Add graphql-hooks to the list of GraphQL clients in new doc - https://vendure-docs-beta.netlify.app/guides/storefront/connect-api/#select-a-graphql-client

zehawki avatar Jul 31 '23 18:07 zehawki

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 avatar Aug 29 '23 13:08 mschipperheyn

@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?

michaelbromley avatar Aug 29 '23 14:08 michaelbromley

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

mschipperheyn avatar Aug 29 '23 14:08 mschipperheyn

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:

1. Working with relations

This section could be a sub-heading of this section which explains how relations work, i.e.

  • There's no build-time guarantee of whether relations will be joined at runtime
  • Explain the use of EntityHydrator to ensure the required relations are joined.
  • Many of the core service methods include a relations argument which allows you to specify which relations to load, e.g.
const order = await this.orderService.findOne(ctx, id, ['lines.customFields.assignedCustomer']);

2. Accessing custom fields via TypeScript code

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'] });

michaelbromley avatar Aug 29 '23 15:08 michaelbromley

One to add the the Admin UI customization docs:

  • https://github.com/vendure-ecommerce/vendure/discussions/2368

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

michaelbromley avatar Aug 30 '23 06:08 michaelbromley

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,
                      //...
                  },
              }),

michaelbromley avatar Sep 27 '23 06:09 michaelbromley

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.

mschipperheyn avatar Oct 06 '23 12:10 mschipperheyn

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
);

tianyingchun avatar Oct 06 '23 13:10 tianyingchun

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.

mschipperheyn avatar Oct 07 '23 00:10 mschipperheyn

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.

mschipperheyn avatar Oct 10 '23 19:10 mschipperheyn

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

mschipperheyn avatar Oct 12 '23 13:10 mschipperheyn

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.

  1. If you add a custom field to an existing entity, that it gets added to the graphql api automatically (If you try to do it manually you run into a dead end because custom field type definitions are created in the code generation step)
  2. How to add a custom field to the root of an existing entity instead of to the 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.
  3. How to avoid the Error: "Vendure Entity you are extending" defined in resolvers, but not in schema.The custom fields get generated automatically, but I need an entity resolver to retrieve the Asset that is added as the avatar field to customFields.

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

mschipperheyn avatar Nov 20 '23 11:11 mschipperheyn