docs: add details on updating a cart's customer (#10226)
Should be merged after releasing v2.0.5 Closes DX-1096
This commit is contained in:
@@ -233,7 +233,7 @@ export const GET = async (
|
|||||||
const b = req.validatedQuery.b as number
|
const b = req.validatedQuery.b as number
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
sum: a + b
|
sum: a + b,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
+6
-6
@@ -149,7 +149,7 @@ class HelloModuleService {
|
|||||||
data: {
|
data: {
|
||||||
id: "123",
|
id: "123",
|
||||||
// other data payload
|
// other data payload
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -167,7 +167,7 @@ export const depsHighlight = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
```ts title="medusa-config.ts" highlights={depsHighlight}
|
```ts title="medusa-config.ts" highlights={depsHighlight}
|
||||||
import { Modules } from '@medusajs/framework/utils'
|
import { Modules } from "@medusajs/framework/utils"
|
||||||
|
|
||||||
module.exports = defineConfig({
|
module.exports = defineConfig({
|
||||||
// ...
|
// ...
|
||||||
@@ -175,10 +175,10 @@ module.exports = defineConfig({
|
|||||||
{
|
{
|
||||||
resolve: "./src/modules/hello",
|
resolve: "./src/modules/hello",
|
||||||
dependencies: [
|
dependencies: [
|
||||||
Modules.EVENT_BUS
|
Modules.EVENT_BUS,
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -150,7 +150,7 @@ const { data: productCustoms } = await query.graph({
|
|||||||
take: 5,
|
take: 5,
|
||||||
skip: 0,
|
skip: 0,
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
In the object passed to the `graph` method:
|
In the object passed to the `graph` method:
|
||||||
@@ -318,9 +318,9 @@ export default defineMiddlewares({
|
|||||||
defaults: [
|
defaults: [
|
||||||
"id",
|
"id",
|
||||||
"name",
|
"name",
|
||||||
"products.*"
|
"products.*",
|
||||||
],
|
],
|
||||||
isList: true
|
isList: true,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -371,7 +371,7 @@ export const GET = async (
|
|||||||
|
|
||||||
const { data: myCustoms } = await query.graph({
|
const { data: myCustoms } = await query.graph({
|
||||||
entity: "my_custom",
|
entity: "my_custom",
|
||||||
...req.remoteQueryConfig
|
...req.remoteQueryConfig,
|
||||||
})
|
})
|
||||||
|
|
||||||
res.json({ my_customs: myCustoms })
|
res.json({ my_customs: myCustoms })
|
||||||
|
|||||||
+2
-2
@@ -106,7 +106,7 @@ const myWorkflow = createWorkflow(
|
|||||||
const today = new Date()
|
const today = new Date()
|
||||||
|
|
||||||
return new WorkflowResponse({
|
return new WorkflowResponse({
|
||||||
today
|
today,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -117,7 +117,7 @@ const myWorkflow = createWorkflow(
|
|||||||
const today = transform({}, () => new Date())
|
const today = transform({}, () => new Date())
|
||||||
|
|
||||||
return new WorkflowResponse({
|
return new WorkflowResponse({
|
||||||
today
|
today,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ Each workflow step must have a unique ID, which is the ID passed as a first para
|
|||||||
|
|
||||||
```ts
|
```ts
|
||||||
const useQueryGraphStep = createStep(
|
const useQueryGraphStep = createStep(
|
||||||
"use-query-graph",
|
"use-query-graph"
|
||||||
// ...
|
// ...
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
@@ -29,13 +29,13 @@ const helloWorkflow = createWorkflow(
|
|||||||
() => {
|
() => {
|
||||||
const { data: products } = useQueryGraphStep({
|
const { data: products } = useQueryGraphStep({
|
||||||
entity: "product",
|
entity: "product",
|
||||||
fields: ["id"]
|
fields: ["id"],
|
||||||
})
|
})
|
||||||
|
|
||||||
// ERROR OCCURS HERE: A STEP HAS THE SAME ID AS ANOTHER IN THE WORKFLOW
|
// ERROR OCCURS HERE: A STEP HAS THE SAME ID AS ANOTHER IN THE WORKFLOW
|
||||||
const { data: customers } = useQueryGraphStep({
|
const { data: customers } = useQueryGraphStep({
|
||||||
entity: "customer",
|
entity: "customer",
|
||||||
fields: ["id"]
|
fields: ["id"],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -63,13 +63,13 @@ const helloWorkflow = createWorkflow(
|
|||||||
() => {
|
() => {
|
||||||
const { data: products } = useQueryGraphStep({
|
const { data: products } = useQueryGraphStep({
|
||||||
entity: "product",
|
entity: "product",
|
||||||
fields: ["id"]
|
fields: ["id"],
|
||||||
})
|
})
|
||||||
|
|
||||||
// ✓ No error occurs, the step has a different ID.
|
// ✓ No error occurs, the step has a different ID.
|
||||||
const { data: customers } = useQueryGraphStep({
|
const { data: customers } = useQueryGraphStep({
|
||||||
entity: "customer",
|
entity: "customer",
|
||||||
fields: ["id"]
|
fields: ["id"],
|
||||||
}).config({ name: "fetch-customers" })
|
}).config({ name: "fetch-customers" })
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -54,8 +54,8 @@ export default async function orderPlacedHandler({
|
|||||||
await sendOrderConfirmationWorkflow(container)
|
await sendOrderConfirmationWorkflow(container)
|
||||||
.run({
|
.run({
|
||||||
input: {
|
input: {
|
||||||
id: data.id
|
id: data.id,
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -133,8 +133,8 @@ export const loaderHighlights = [
|
|||||||
|
|
||||||
```ts title="src/modules/mongo/loaders/connection.ts" highlights={loaderHighlights}
|
```ts title="src/modules/mongo/loaders/connection.ts" highlights={loaderHighlights}
|
||||||
import { LoaderOptions } from "@medusajs/framework/types"
|
import { LoaderOptions } from "@medusajs/framework/types"
|
||||||
import { ContainerRegistrationKeys } from "@medusajs/framework/utils";
|
import { ContainerRegistrationKeys } from "@medusajs/framework/utils"
|
||||||
import { asValue } from "awilix";
|
import { asValue } from "awilix"
|
||||||
import { MongoClient } from "mongodb"
|
import { MongoClient } from "mongodb"
|
||||||
|
|
||||||
type ModuleOptions = {
|
type ModuleOptions = {
|
||||||
@@ -144,7 +144,7 @@ type ModuleOptions = {
|
|||||||
|
|
||||||
export default async function mongoConnectionLoader({
|
export default async function mongoConnectionLoader({
|
||||||
container,
|
container,
|
||||||
options
|
options,
|
||||||
}: LoaderOptions<ModuleOptions>) {
|
}: LoaderOptions<ModuleOptions>) {
|
||||||
if (!options.connection_url) {
|
if (!options.connection_url) {
|
||||||
throw new Error(`[MONGO MDOULE]: connection_url option is required.`)
|
throw new Error(`[MONGO MDOULE]: connection_url option is required.`)
|
||||||
@@ -187,10 +187,10 @@ module.exports = defineConfig({
|
|||||||
resolve: "./src/modules/mongo",
|
resolve: "./src/modules/mongo",
|
||||||
options: {
|
options: {
|
||||||
connection_url: process.env.MONGO_CONNECTION_URL,
|
connection_url: process.env.MONGO_CONNECTION_URL,
|
||||||
db_name: process.env.MONGO_DB_NAME
|
db_name: process.env.MONGO_DB_NAME,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -239,11 +239,11 @@ export default class MongoModuleService {
|
|||||||
const moviesCol = this.mongoClient_.collection("movie")
|
const moviesCol = this.mongoClient_.collection("movie")
|
||||||
|
|
||||||
const insertedMovie = await moviesCol.insertOne({
|
const insertedMovie = await moviesCol.insertOne({
|
||||||
title
|
title,
|
||||||
})
|
})
|
||||||
|
|
||||||
const movie = await moviesCol.findOne({
|
const movie = await moviesCol.findOne({
|
||||||
_id: insertedMovie.insertedId
|
_id: insertedMovie.insertedId,
|
||||||
})
|
})
|
||||||
|
|
||||||
return movie
|
return movie
|
||||||
@@ -254,8 +254,8 @@ export default class MongoModuleService {
|
|||||||
|
|
||||||
await moviesCol.deleteOne({
|
await moviesCol.deleteOne({
|
||||||
_id: {
|
_id: {
|
||||||
equals: id
|
equals: id,
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -274,7 +274,7 @@ export const MONGO_MODULE = "mongo"
|
|||||||
|
|
||||||
export default Module(MONGO_MODULE, {
|
export default Module(MONGO_MODULE, {
|
||||||
service: MongoModuleService,
|
service: MongoModuleService,
|
||||||
loaders: [mongoConnectionLoader]
|
loaders: [mongoConnectionLoader],
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -26,8 +26,8 @@ export const highlights = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
```ts highlights={highlights}
|
```ts highlights={highlights}
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework";
|
import { MedusaRequest, MedusaResponse } from "@medusajs/framework"
|
||||||
import { ContainerRegistrationKeys } from "@medusajs/framework/utils";
|
import { ContainerRegistrationKeys } from "@medusajs/framework/utils"
|
||||||
|
|
||||||
export async function GET(
|
export async function GET(
|
||||||
req: MedusaRequest,
|
req: MedusaRequest,
|
||||||
@@ -41,12 +41,12 @@ export async function GET(
|
|||||||
entity: "product",
|
entity: "product",
|
||||||
fields: ["*"],
|
fields: ["*"],
|
||||||
filters: {
|
filters: {
|
||||||
id: "prod_123"
|
id: "prod_123",
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
products
|
products,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -84,7 +84,7 @@ export const subscriberHighlights = [
|
|||||||
|
|
||||||
```ts highlights={subscriberHighlights}
|
```ts highlights={subscriberHighlights}
|
||||||
import { SubscriberArgs, type SubscriberConfig } from "@medusajs/framework"
|
import { SubscriberArgs, type SubscriberConfig } from "@medusajs/framework"
|
||||||
import { ContainerRegistrationKeys } from "@medusajs/framework/utils";
|
import { ContainerRegistrationKeys } from "@medusajs/framework/utils"
|
||||||
|
|
||||||
export default async function productCreateHandler({
|
export default async function productCreateHandler({
|
||||||
event: { data },
|
event: { data },
|
||||||
@@ -96,8 +96,8 @@ export default async function productCreateHandler({
|
|||||||
entity: "product",
|
entity: "product",
|
||||||
fields: ["*"],
|
fields: ["*"],
|
||||||
filters: {
|
filters: {
|
||||||
id: data.id
|
id: data.id,
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
console.log(`You created a product with the title ${products[0].title}`)
|
console.log(`You created a product with the title ${products[0].title}`)
|
||||||
@@ -119,7 +119,7 @@ export const scheduledJobHighlights = [
|
|||||||
|
|
||||||
```ts highlights={scheduledJobHighlights}
|
```ts highlights={scheduledJobHighlights}
|
||||||
import { MedusaContainer } from "@medusajs/framework/types"
|
import { MedusaContainer } from "@medusajs/framework/types"
|
||||||
import { ContainerRegistrationKeys } from "@medusajs/framework/utils";
|
import { ContainerRegistrationKeys } from "@medusajs/framework/utils"
|
||||||
|
|
||||||
export default async function myCustomJob(
|
export default async function myCustomJob(
|
||||||
container: MedusaContainer
|
container: MedusaContainer
|
||||||
@@ -130,8 +130,8 @@ export default async function myCustomJob(
|
|||||||
entity: "product",
|
entity: "product",
|
||||||
fields: ["*"],
|
fields: ["*"],
|
||||||
filters: {
|
filters: {
|
||||||
id: "prod_123"
|
id: "prod_123",
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
@@ -160,7 +160,7 @@ import {
|
|||||||
createStep,
|
createStep,
|
||||||
StepResponse,
|
StepResponse,
|
||||||
} from "@medusajs/framework/workflows-sdk"
|
} from "@medusajs/framework/workflows-sdk"
|
||||||
import { ContainerRegistrationKeys } from "@medusajs/framework/utils";
|
import { ContainerRegistrationKeys } from "@medusajs/framework/utils"
|
||||||
|
|
||||||
const step1 = createStep("step-1", async (_, { container }) => {
|
const step1 = createStep("step-1", async (_, { container }) => {
|
||||||
const query = container.resolve(ContainerRegistrationKeys.QUERY)
|
const query = container.resolve(ContainerRegistrationKeys.QUERY)
|
||||||
@@ -169,8 +169,8 @@ const step1 = createStep("step-1", async (_, { container }) => {
|
|||||||
entity: "product",
|
entity: "product",
|
||||||
fields: ["*"],
|
fields: ["*"],
|
||||||
filters: {
|
filters: {
|
||||||
id: "prod_123"
|
id: "prod_123",
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
return new StepResponse(products)
|
return new StepResponse(products)
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ import { model } from "@medusajs/framework/utils"
|
|||||||
|
|
||||||
const Post = model.define("post", {
|
const Post = model.define("post", {
|
||||||
id: model.id().primaryKey(),
|
id: model.id().primaryKey(),
|
||||||
title: model.text()
|
title: model.text(),
|
||||||
})
|
})
|
||||||
|
|
||||||
export default Post
|
export default Post
|
||||||
@@ -143,8 +143,8 @@ module.exports = defineConfig({
|
|||||||
},
|
},
|
||||||
modules: [
|
modules: [
|
||||||
{
|
{
|
||||||
resolve: "./src/modules/blog"
|
resolve: "./src/modules/blog",
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
@@ -168,16 +168,16 @@ npx medusa db:generate blog
|
|||||||
The `db:generate` command of the Medusa CLI accepts one or more module names to generate the migration for. It will create a migration file for the Blog Module in the directory `src/modules/blog/migrations` similar to the following:
|
The `db:generate` command of the Medusa CLI accepts one or more module names to generate the migration for. It will create a migration file for the Blog Module in the directory `src/modules/blog/migrations` similar to the following:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { Migration } from '@mikro-orm/migrations';
|
import { Migration } from "@mikro-orm/migrations"
|
||||||
|
|
||||||
export class Migration20241121103722 extends Migration {
|
export class Migration20241121103722 extends Migration {
|
||||||
|
|
||||||
async up(): Promise<void> {
|
async up(): Promise<void> {
|
||||||
this.addSql('create table if not exists "post" ("id" text not null, "title" text not null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "post_pkey" primary key ("id"));');
|
this.addSql("create table if not exists \"post\" (\"id\" text not null, \"title\" text not null, \"created_at\" timestamptz not null default now(), \"updated_at\" timestamptz not null default now(), \"deleted_at\" timestamptz null, constraint \"post_pkey\" primary key (\"id\"));")
|
||||||
}
|
}
|
||||||
|
|
||||||
async down(): Promise<void> {
|
async down(): Promise<void> {
|
||||||
this.addSql('drop table if exists "post" cascade;');
|
this.addSql("drop table if exists \"post\" cascade;")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -226,10 +226,10 @@ import {
|
|||||||
createStep,
|
createStep,
|
||||||
createWorkflow,
|
createWorkflow,
|
||||||
StepResponse,
|
StepResponse,
|
||||||
WorkflowResponse
|
WorkflowResponse,
|
||||||
} from "@medusajs/framework/workflows-sdk";
|
} from "@medusajs/framework/workflows-sdk"
|
||||||
import { BLOG_MODULE } from "../modules/blog";
|
import { BLOG_MODULE } from "../modules/blog"
|
||||||
import BlogModuleService from "../modules/blog/service";
|
import BlogModuleService from "../modules/blog/service"
|
||||||
|
|
||||||
type CreatePostWorkflowInput = {
|
type CreatePostWorkflowInput = {
|
||||||
title: string
|
title: string
|
||||||
@@ -241,7 +241,7 @@ const createPostStep = createStep(
|
|||||||
const blogModuleService: BlogModuleService = container.resolve(BLOG_MODULE)
|
const blogModuleService: BlogModuleService = container.resolve(BLOG_MODULE)
|
||||||
|
|
||||||
const post = await blogModuleService.createPosts({
|
const post = await blogModuleService.createPosts({
|
||||||
title
|
title,
|
||||||
})
|
})
|
||||||
|
|
||||||
return new StepResponse(post, post)
|
return new StepResponse(post, post)
|
||||||
@@ -272,11 +272,11 @@ You'll now execute that workflow in an API route to expose the feature of creati
|
|||||||
```ts
|
```ts
|
||||||
import type {
|
import type {
|
||||||
MedusaRequest,
|
MedusaRequest,
|
||||||
MedusaResponse
|
MedusaResponse,
|
||||||
} from "@medusajs/framework/http";
|
} from "@medusajs/framework/http"
|
||||||
import {
|
import {
|
||||||
createPostWorkflow
|
createPostWorkflow,
|
||||||
} from "../../../workflows/create-post";
|
} from "../../../workflows/create-post"
|
||||||
|
|
||||||
export async function POST(
|
export async function POST(
|
||||||
req: MedusaRequest,
|
req: MedusaRequest,
|
||||||
@@ -285,12 +285,12 @@ export async function POST(
|
|||||||
const { result: post } = await createPostWorkflow(req.scope)
|
const { result: post } = await createPostWorkflow(req.scope)
|
||||||
.run({
|
.run({
|
||||||
input: {
|
input: {
|
||||||
title: "My Post"
|
title: "My Post",
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
post
|
post,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -159,7 +159,7 @@ module.exports = defineConfig({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -118,3 +118,5 @@ This is necessary, as only products matching the cart's sales channel(s) can be
|
|||||||
## Associate Customer with Cart
|
## Associate Customer with Cart
|
||||||
|
|
||||||
When the cart is created for a logged-in customer, it's automatically associated with that customer.
|
When the cart is created for a logged-in customer, it's automatically associated with that customer.
|
||||||
|
|
||||||
|
However, if the cart is created for a guest customer, then the customer logs in, then you have to set the cart's customer as explained in [this guide](../update/page.mdx#set-carts-customer).
|
||||||
|
|||||||
@@ -46,3 +46,46 @@ fetch(`http://localhost:9000/store/carts/${cartId}`, {
|
|||||||
```
|
```
|
||||||
|
|
||||||
The Update Cart API route accepts a `region_id` request body parameter, whose value is the new region to associate with the cart.
|
The Update Cart API route accepts a `region_id` request body parameter, whose value is the new region to associate with the cart.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Set Cart's Customer
|
||||||
|
|
||||||
|
You might need to change the cart's customer in two cases:
|
||||||
|
|
||||||
|
- You created the cart for the customer as a guest, then they logged-in, so you want to associate the cart with them as a registered customer.
|
||||||
|
- You're transfering the cart from one customer to another, which is useful in company setups, such as when implementing B2B commerce applicatons.
|
||||||
|
|
||||||
|
To set or change the cart's customer, send an authenticated `POST` request to the Set Cart's Customer API route:
|
||||||
|
|
||||||
|
<Note>
|
||||||
|
|
||||||
|
This API route is only available after [Medusa v2.0.5](https://github.com/medusajs/medusa/releases/tag/v2.0.5).
|
||||||
|
|
||||||
|
</Note>
|
||||||
|
|
||||||
|
```ts
|
||||||
|
fetch(`http://localhost:9000/store/carts/${cartId}/customer`, {
|
||||||
|
credentials: "include",
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then(({ cart }) => {
|
||||||
|
// use cart...
|
||||||
|
console.log(cart)
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
To send an authenticated request, either use `credentials: include` if the customer is already authenticated with a cookie session, or pass the Authorization Bearer token in the request's header.
|
||||||
|
|
||||||
|
<Note title="Tip">
|
||||||
|
|
||||||
|
Learn more about authenticating customers in [this guide](../../customers/login/page.mdx).
|
||||||
|
|
||||||
|
</Note>
|
||||||
|
|
||||||
|
The cart is now associated with the logged-in customer.
|
||||||
|
|||||||
Reference in New Issue
Block a user