docs: added customer storefront guides (#7685)
* added customer guides * fixes to sidebar * remove old customer registration guide * fix build error * generate files * run linter
This commit is contained in:
1 parent
d862d03de0
commit
c1db40b564
80 files changed
+2301
-701
No files matched your search
@@ -0,0 +1,570 @@
|
||||
import { CodeTabs, CodeTab } from "docs-ui"
|
||||
|
||||
export const metadata = {
|
||||
title: `Manage Customer Addresses in Storefront`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
In this document, you'll learn how to manage a customer's addresses in a storefront.
|
||||
|
||||
## List Customer Addresses
|
||||
|
||||
To retrieve the list of customer addresses, send a request to the [List Customer Addresses API route](!api!/store#customers_getcustomersmeaddressesaddress_id):
|
||||
|
||||
<CodeTabs group="store-request">
|
||||
<CodeTab label="Fetch API" value="fetch">
|
||||
|
||||
export const fetchHighlights = [
|
||||
["2", "limit", "The number of addresses to retrieve"],
|
||||
["3", "offset", "The number of addresses to skip before those retrieved."],
|
||||
]
|
||||
|
||||
```ts highlights={fetchHighlights}
|
||||
const searchParams = new URLSearchParams({
|
||||
limit: `${limit}`,
|
||||
offset: `${offset}`,
|
||||
})
|
||||
|
||||
fetch(`http://localhost:9000/store/customers/me/addresses?${
|
||||
searchParams.toString()
|
||||
}`, {
|
||||
credentials: "include",
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then(({ addresses, count }) => {
|
||||
// use addresses...
|
||||
console.log(addresses, count)
|
||||
})
|
||||
```
|
||||
|
||||
</CodeTab>
|
||||
<CodeTab label="React" value="react">
|
||||
|
||||
export const highlights = [
|
||||
["20", "offset", "Calculate the number of addresses to skip based on the current page and limit."],
|
||||
["27", "fetch", "Send a request to retrieve the addresses."],
|
||||
["28", "searchParams.toString()", "Pass the pagination parameters in the query."],
|
||||
["33", "count", "The total number of addresses in the Medusa application."],
|
||||
["45", "setHasMorePages", "Set whether there are more pages based on the total count."],
|
||||
["59", "", "Using only two address fields for simplicity."],
|
||||
["65", "button", "Show a button to load more addresses if there are more pages."]
|
||||
]
|
||||
|
||||
```tsx highlights={highlights} collapsibleLines="50-77" expandButtonLabel="Show render"
|
||||
"use client" // include with Next.js 13+
|
||||
|
||||
import { HttpTypes } from "@medusajs/types"
|
||||
import { useEffect, useState } from "react"
|
||||
|
||||
export default function Addresses() {
|
||||
const [addresses, setAddresses] = useState<
|
||||
HttpTypes.StoreCustomerAddress[]
|
||||
>([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
const limit = 20
|
||||
const [currentPage, setCurrentPage] = useState(1)
|
||||
const [hasMorePages, setHasMorePages] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
if (!loading) {
|
||||
return
|
||||
}
|
||||
|
||||
const offset = (currentPage - 1) * limit
|
||||
|
||||
const searchParams = new URLSearchParams({
|
||||
limit: `${limit}`,
|
||||
offset: `${offset}`,
|
||||
})
|
||||
|
||||
fetch(`http://localhost:9000/store/customers/me/addresses?${
|
||||
searchParams.toString()
|
||||
}`, {
|
||||
credentials: "include",
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then(({ addresses: addressesData, count }) => {
|
||||
setAddresses((prev) => {
|
||||
if (prev.length > offset) {
|
||||
// addresses already added because
|
||||
// the same request has already been sent
|
||||
return prev
|
||||
}
|
||||
return [
|
||||
...prev,
|
||||
...addressesData,
|
||||
]
|
||||
})
|
||||
setHasMorePages(count > limit * currentPage)
|
||||
})
|
||||
.finally(() => setLoading(false))
|
||||
}, [loading])
|
||||
|
||||
return (
|
||||
<div>
|
||||
{loading && <span>Loading...</span>}
|
||||
{!loading && !addresses.length && (
|
||||
<span>You have no addresses</span>
|
||||
)}
|
||||
<ul>
|
||||
{addresses.map((address) => (
|
||||
<li key={address.id}>
|
||||
City: {address.city} -
|
||||
Country: {address.country_code}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
{!loading && hasMorePages && (
|
||||
<button
|
||||
onClick={() => {
|
||||
setCurrentPage((prev) => prev + 1)
|
||||
setLoading(true)
|
||||
}}
|
||||
disabled={loading}
|
||||
>
|
||||
Load More
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
</CodeTab>
|
||||
</CodeTabs>
|
||||
|
||||
The List Customer Addresses API route accepts pagination parameters to paginate the address.
|
||||
|
||||
{/* TODO add a link to the address object */}
|
||||
|
||||
It returns in the response the `addresses` field, which is an array of addresses.
|
||||
|
||||
---
|
||||
|
||||
## Add Customer Address
|
||||
|
||||
To add a new address for the customer, send a request to the [Add Customer Address API route](!api!/store#customers_postcustomersmeaddresses):
|
||||
|
||||
<CodeTabs group="store-request">
|
||||
<CodeTab label="Fetch API" value="fetch">
|
||||
|
||||
```ts
|
||||
fetch(`http://localhost:9000/store/customers/me/addresses`, {
|
||||
credentials: "include",
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
first_name: firstName,
|
||||
last_name: lastName,
|
||||
address_1: address1,
|
||||
company,
|
||||
postal_code: postalCode,
|
||||
city,
|
||||
country_code: countryCode,
|
||||
province,
|
||||
phone: phoneNumber,
|
||||
}),
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then(({ customer }) => {
|
||||
// use customer
|
||||
console.log(customer)
|
||||
})
|
||||
```
|
||||
|
||||
</CodeTab>
|
||||
<CodeTab label="React" value="react">
|
||||
|
||||
export const addHighlights = [
|
||||
["4", "useRegion", "Use the hook defined in the Region Context guide."],
|
||||
["5", "useCustomer", "Use the hook defined in the Customer Context guide."],
|
||||
["28"], ["29"], ["30"], ["31"], ["32"], ["33"], ["34"], ["35"], ["36"], ["37"],
|
||||
["38"], ["39"], ["40"], ["41"], ["42"], ["43"], ["44"], ["45"], ["46"], ["47"],
|
||||
["48"], ["49"]
|
||||
]
|
||||
|
||||
```tsx highlights={addHighlights} collapsibleLines="53-124" expandButtonLabel="Show form"
|
||||
"use client" // include with Next.js 13+
|
||||
|
||||
import { useState } from "react"
|
||||
import { useRegion } from "../../../../providers/region"
|
||||
import { useCustomer } from "../../../../providers/customer"
|
||||
|
||||
export default function AddAddress() {
|
||||
const { region } = useRegion()
|
||||
const { setCustomer } = useCustomer()
|
||||
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [firstName, setFirstName] = useState("")
|
||||
const [lastName, setLastName] = useState("")
|
||||
const [address1, setAddress1] = useState("")
|
||||
const [company, setCompany] = useState("")
|
||||
const [postalCode, setPostalCode] = useState("")
|
||||
const [city, setCity] = useState("")
|
||||
const [countryCode, setCountryCode] = useState("")
|
||||
const [province, setProvince] = useState("")
|
||||
const [phoneNumber, setPhoneNumber] = useState("")
|
||||
|
||||
const handleAdd = (
|
||||
e: React.MouseEvent<HTMLButtonElement, MouseEvent>
|
||||
) => {
|
||||
e.preventDefault()
|
||||
setLoading(false)
|
||||
|
||||
fetch(`http://localhost:9000/store/customers/me/addresses`, {
|
||||
credentials: "include",
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
first_name: firstName,
|
||||
last_name: lastName,
|
||||
address_1: address1,
|
||||
company,
|
||||
postal_code: postalCode,
|
||||
city,
|
||||
country_code: countryCode,
|
||||
province,
|
||||
phone: phoneNumber,
|
||||
}),
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then(({ customer }) => {
|
||||
setCustomer(customer)
|
||||
})
|
||||
.finally(() => setLoading(false))
|
||||
}
|
||||
|
||||
return (
|
||||
<form>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="First Name"
|
||||
value={firstName}
|
||||
onChange={(e) => setFirstName(e.target.value)}
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Last Name"
|
||||
value={lastName}
|
||||
onChange={(e) => setLastName(e.target.value)}
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Address Line"
|
||||
value={address1}
|
||||
onChange={(e) => setAddress1(e.target.value)}
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Company"
|
||||
value={company}
|
||||
onChange={(e) => setCompany(e.target.value)}
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Postal Code"
|
||||
value={postalCode}
|
||||
onChange={(e) => setPostalCode(e.target.value)}
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="City"
|
||||
value={city}
|
||||
onChange={(e) => setCity(e.target.value)}
|
||||
/>
|
||||
<select
|
||||
value={countryCode}
|
||||
onChange={(e) => setCountryCode(e.target.value)}
|
||||
>
|
||||
{region?.countries?.map((country) => (
|
||||
<option
|
||||
key={country.iso_2}
|
||||
value={country.iso_2}
|
||||
>
|
||||
{country.display_name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Province"
|
||||
value={province}
|
||||
onChange={(e) => setProvince(e.target.value)}
|
||||
/>
|
||||
<input
|
||||
type="tel"
|
||||
placeholder="Phone Number"
|
||||
value={phoneNumber}
|
||||
onChange={(e) => setPhoneNumber(e.target.value)}
|
||||
/>
|
||||
<button
|
||||
disabled={loading}
|
||||
onClick={handleAdd}
|
||||
>
|
||||
Add
|
||||
</button>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
</CodeTab>
|
||||
</CodeTabs>
|
||||
|
||||
The Add Address API route returns in the response a `customer` field, which is a [customer object](!api!/store#customers_customer_schema).
|
||||
|
||||
---
|
||||
|
||||
## Edit an Address
|
||||
|
||||
To edit an address, send a request to the [Update Customer Address API route](!api!/store#customers_postcustomersmeaddressesaddress_id):
|
||||
|
||||
<CodeTabs group="store-request">
|
||||
<CodeTab label="Fetch API" value="fetch">
|
||||
|
||||
```ts
|
||||
fetch(
|
||||
`http://localhost:9000/store/customers/me/addresses/${
|
||||
address.id
|
||||
}`,
|
||||
{
|
||||
credentials: "include",
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
first_name: firstName,
|
||||
last_name: lastName,
|
||||
address_1: address1,
|
||||
company,
|
||||
postal_code: postalCode,
|
||||
city,
|
||||
country_code: countryCode,
|
||||
province,
|
||||
phone: phoneNumber,
|
||||
}),
|
||||
}
|
||||
)
|
||||
.then((res) => res.json())
|
||||
.then(({ customer }) => {
|
||||
// use customer...
|
||||
console.log(customer)
|
||||
})
|
||||
```
|
||||
|
||||
</CodeTab>
|
||||
<CodeTab label="React" value="react">
|
||||
|
||||
export const editHighlights = [
|
||||
["4", "useRegion", "Use the hook defined in the Region Context guide."],
|
||||
["5", "useCustomer", "Use the hook defined in the Customer Context guide."],
|
||||
["14", "{ params: { id }}", "This is based on Next.js which passes the path parameters as a prop."],
|
||||
["19", "address", "Retrieve the address from the customer's `addresses` property."],
|
||||
["60"], ["61"], ["62"], ["63"], ["64"], ["65"], ["66"], ["67"], ["68"], ["69"],
|
||||
["70"], ["71"], ["72"], ["73"], ["74"], ["75"], ["76"], ["77"], ["78"], ["79"],
|
||||
["80"], ["81"]
|
||||
]
|
||||
|
||||
```tsx highlights={editHighlights} collapsibleLines="90-161" expandButtonLabel="Show form"
|
||||
"use client" // include with Next.js 13+
|
||||
|
||||
import { useState } from "react"
|
||||
import { useRegion } from "../../../../../providers/region"
|
||||
import { useCustomer } from "../../../../../providers/customer"
|
||||
|
||||
type Params = {
|
||||
params: {
|
||||
id: string
|
||||
}
|
||||
}
|
||||
|
||||
export default function EditAddress(
|
||||
{ params: { id } }: Params
|
||||
) {
|
||||
const { customer, setCustomer } = useCustomer()
|
||||
const { region } = useRegion()
|
||||
|
||||
const address = customer?.addresses.find(
|
||||
(address) => address.id === id
|
||||
)
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [firstName, setFirstName] = useState(
|
||||
address?.first_name || ""
|
||||
)
|
||||
const [lastName, setLastName] = useState(
|
||||
address?.last_name || ""
|
||||
)
|
||||
const [address1, setAddress1] = useState(
|
||||
address?.address_1 || ""
|
||||
)
|
||||
const [company, setCompany] = useState(
|
||||
address?.company || ""
|
||||
)
|
||||
const [postalCode, setPostalCode] = useState(
|
||||
address?.postal_code || ""
|
||||
)
|
||||
const [city, setCity] = useState(
|
||||
address?.city || ""
|
||||
)
|
||||
const [countryCode, setCountryCode] = useState(
|
||||
address?.country_code || ""
|
||||
)
|
||||
const [province, setProvince] = useState(
|
||||
address?.province || ""
|
||||
)
|
||||
const [phoneNumber, setPhoneNumber] = useState(
|
||||
address?.phone || ""
|
||||
)
|
||||
|
||||
const handleEdit = (
|
||||
e: React.MouseEvent<HTMLButtonElement, MouseEvent>
|
||||
) => {
|
||||
e.preventDefault()
|
||||
if (!customer || !address) {
|
||||
return
|
||||
}
|
||||
setLoading(true)
|
||||
|
||||
fetch(
|
||||
`http://localhost:9000/store/customers/me/addresses/${
|
||||
address.id
|
||||
}`,
|
||||
{
|
||||
credentials: "include",
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
first_name: firstName,
|
||||
last_name: lastName,
|
||||
address_1: address1,
|
||||
company,
|
||||
postal_code: postalCode,
|
||||
city,
|
||||
country_code: countryCode,
|
||||
province,
|
||||
phone: phoneNumber,
|
||||
}),
|
||||
}
|
||||
)
|
||||
.then((res) => res.json())
|
||||
.then(({ customer }) => {
|
||||
setCustomer(customer)
|
||||
})
|
||||
.finally(() => setLoading(false))
|
||||
}
|
||||
|
||||
return (
|
||||
<form>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="First Name"
|
||||
value={firstName}
|
||||
onChange={(e) => setFirstName(e.target.value)}
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Last Name"
|
||||
value={lastName}
|
||||
onChange={(e) => setLastName(e.target.value)}
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Address Line"
|
||||
value={address1}
|
||||
onChange={(e) => setAddress1(e.target.value)}
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Company"
|
||||
value={company}
|
||||
onChange={(e) => setCompany(e.target.value)}
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Postal Code"
|
||||
value={postalCode}
|
||||
onChange={(e) => setPostalCode(e.target.value)}
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="City"
|
||||
value={city}
|
||||
onChange={(e) => setCity(e.target.value)}
|
||||
/>
|
||||
<select
|
||||
value={countryCode}
|
||||
onChange={(e) => setCountryCode(e.target.value)}
|
||||
>
|
||||
{region?.countries?.map((country) => (
|
||||
<option
|
||||
key={country.iso_2}
|
||||
value={country.iso_2}
|
||||
>
|
||||
{country.display_name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Province"
|
||||
value={province}
|
||||
onChange={(e) => setProvince(e.target.value)}
|
||||
/>
|
||||
<input
|
||||
type="tel"
|
||||
placeholder="Phone Number"
|
||||
value={phoneNumber}
|
||||
onChange={(e) => setPhoneNumber(e.target.value)}
|
||||
/>
|
||||
<button
|
||||
disabled={loading}
|
||||
onClick={handleEdit}
|
||||
>
|
||||
Add
|
||||
</button>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
</CodeTab>
|
||||
</CodeTabs>
|
||||
|
||||
The Update Address API route returns in the response a `customer` field, which is a [customer object](!api!/store#customers_customer_schema).
|
||||
|
||||
---
|
||||
|
||||
## Delete Customer Address
|
||||
|
||||
To delete a customer's address, send a request to the [Delete Customer Address API route](!api!/store#customers_deletecustomersmeaddressesaddress_id):
|
||||
|
||||
export const deleteHighlights = [
|
||||
["3", "addrId", "The ID of the address to delete."]
|
||||
]
|
||||
|
||||
```ts highlights={deleteHighlights}
|
||||
fetch(
|
||||
`http://localhost:9000/store/customers/me/addresses/${
|
||||
addrId
|
||||
}`,
|
||||
{
|
||||
credentials: "include",
|
||||
method: "DELETE",
|
||||
}
|
||||
)
|
||||
.then((res) => res.json())
|
||||
.then(({ parent: customer }) => {
|
||||
// use customer...
|
||||
console.log(customer)
|
||||
})
|
||||
```
|
||||
|
||||
The Delete Customer Address API route returns a `parent` field in the response, which is a [customer object](!api!/store#customers_customer_schema).
|
||||
@@ -0,0 +1,160 @@
|
||||
export const metadata = {
|
||||
title: `Customer Context in Storefront`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
Throughout your storefront, you'll need to access the logged-in customer to perform different actions, such as associate it with a cart.
|
||||
|
||||
So, if your storefront is React-based, create a customer context and add it at the top of your components tree. Then, you can access the logged-in customer anywhere in your storefront.
|
||||
|
||||
## Create Customer Context Provider
|
||||
|
||||
For example, create the following file that exports a `CustomerProvider` component and a `useCustomer` hook:
|
||||
|
||||
export const highlights = [
|
||||
["12", "customer", "Expose customer to children of the context provider."],
|
||||
["13", "setCustomer", "Allow the context provider's\nchildren to change the logged-in customer."],
|
||||
["24", "CustomerProvider", "The provider component to use in your component tree."],
|
||||
["36", "fetch", "Try to retrieve the customer's details,\nif the customer is authentiated."],
|
||||
["37", `credentials: "include"`, "Important to include this option for cookie session authentication.\nFor token authentication, pass the authorization header instead."],
|
||||
["58", "useCustomer", "The hook that child components of the provider use to access the customer."]
|
||||
]
|
||||
|
||||
```tsx highlights={highlights}
|
||||
"use client" // include with Next.js 13+
|
||||
|
||||
import {
|
||||
createContext,
|
||||
useContext,
|
||||
useEffect,
|
||||
useState,
|
||||
} from "react"
|
||||
import { HttpTypes } from "@medusajs/types"
|
||||
|
||||
type CustomerContextType = {
|
||||
customer: HttpTypes.StoreCustomer | undefined
|
||||
setCustomer: React.Dispatch<
|
||||
React.SetStateAction<HttpTypes.StoreCustomer | undefined>
|
||||
>
|
||||
}
|
||||
|
||||
const CustomerContext = createContext<CustomerContextType | null>(null)
|
||||
|
||||
type CustomerProviderProps = {
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
export const CustomerProvider = ({
|
||||
children,
|
||||
}: CustomerProviderProps) => {
|
||||
const [customer, setCustomer] = useState<
|
||||
HttpTypes.StoreCustomer
|
||||
>()
|
||||
|
||||
useEffect(() => {
|
||||
if (customer) {
|
||||
return
|
||||
}
|
||||
|
||||
fetch(`http://localhost:9000/store/customers/me`, {
|
||||
credentials: "include",
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then(({ customer }) => {
|
||||
setCustomer(customer)
|
||||
})
|
||||
.catch((err) => {
|
||||
// customer isn't logged in
|
||||
})
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<CustomerContext.Provider value={{
|
||||
customer,
|
||||
setCustomer,
|
||||
}}>
|
||||
{children}
|
||||
</CustomerContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export const useCustomer = () => {
|
||||
const context = useContext(CustomerContext)
|
||||
|
||||
if (!context) {
|
||||
throw new Error("useCustomer must be used within a CustomerProvider")
|
||||
}
|
||||
|
||||
return context
|
||||
}
|
||||
```
|
||||
|
||||
The `CustomerProvider` handles retrieving the authenticated customer from the Medusa application.
|
||||
|
||||
The `useCustomer` hook returns the value of the `CustomerContext`. Child components of `CustomerProvider` use this hook to access `customer` or `setCustomer`.
|
||||
|
||||
---
|
||||
|
||||
## Use CustomerProvider in Component Tree
|
||||
|
||||
To use the customer context's value, add the `CustomerProvider` high in your component tree.
|
||||
|
||||
For example, if you're using Next.js, add it to the `app/layout.tsx` or `src/app/layout.tsx` file:
|
||||
|
||||
```tsx title="app/layout.tsx" collapsibleLines="1-14" highlights={[["24"]]}
|
||||
import type { Metadata } from "next"
|
||||
import { Inter } from "next/font/google"
|
||||
import "./globals.css"
|
||||
import { CartProvider } from "../providers/cart"
|
||||
import { RegionProvider } from "../providers/region"
|
||||
import { CustomerProvider } from "../providers/customer"
|
||||
|
||||
const inter = Inter({ subsets: ["latin"] })
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Create Next App",
|
||||
description: "Generated by create next app",
|
||||
}
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body className={inter.className}>
|
||||
<RegionProvider>
|
||||
<CustomerProvider>
|
||||
{/* Other providers... */}
|
||||
<CartProvider>
|
||||
{children}
|
||||
</CartProvider>
|
||||
</CustomerProvider>
|
||||
</RegionProvider>
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Use useCustomer Hook
|
||||
|
||||
Now, you can use the `useCustomer` hook in child components of `CustomerProvider`.
|
||||
|
||||
For example:
|
||||
|
||||
```tsx
|
||||
"use client" // include with Next.js 13+
|
||||
// ...
|
||||
import { useCustomer } from "../providers/customer"
|
||||
|
||||
export default function Profile() {
|
||||
const { customer } = useCustomer()
|
||||
// ...
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,89 @@
|
||||
import { CodeTabs, CodeTab } from "docs-ui"
|
||||
|
||||
export const metadata = {
|
||||
title: `Log-out Customer in Storefront`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
In this document, you'll learn how to log-out a customer in the storefront based on the authentication method.
|
||||
|
||||
## Log-Out for JWT Token
|
||||
|
||||
If you're authenticating the customer with their JWT token, remove the stored token from the browser.
|
||||
|
||||
For example, if you've stored the JWT token in the `localStorage`, remove the item from it:
|
||||
|
||||
```ts
|
||||
localStorage.removeItem(`token`)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Log-Out for Cookie Session
|
||||
|
||||
If you're authenticating the customer with their cookie session ID, send a `DELETE` request to the `/auth/session`.
|
||||
|
||||
For example:
|
||||
|
||||
<CodeTabs group="store-request">
|
||||
<CodeTab label="Fetch API" value="fetch">
|
||||
|
||||
```ts
|
||||
fetch(`http://localhost:9000/auth/session`, {
|
||||
credentials: "include",
|
||||
method: "DELETE",
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then(() => {
|
||||
// TODO redirect customer to login page
|
||||
})
|
||||
```
|
||||
|
||||
</CodeTab>
|
||||
<CodeTab label="React" value="react">
|
||||
|
||||
export const highlights = [
|
||||
["3", "useCustomer", "Use the hook defined in the Customer Context guide."],
|
||||
["9"], ["10"], ["11"], ["12"], ["13"], ["14"], ["15"], ["16"], ["17"], ["18"],
|
||||
["19"],
|
||||
]
|
||||
|
||||
```tsx highlights={highlights}
|
||||
"use client" // include with Next.js 13+
|
||||
|
||||
import { useCustomer } from "../../../providers/customer"
|
||||
|
||||
export default function Profile() {
|
||||
const { setCustomer } = useCustomer()
|
||||
|
||||
const handleLogOut = () => {
|
||||
fetch(`http://localhost:9000/auth/session`, {
|
||||
credentials: "include",
|
||||
method: "DELETE",
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then(() => {
|
||||
setCustomer(undefined)
|
||||
// TODO redirect to login page
|
||||
alert("Logged out")
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{/* Profile details... */}
|
||||
<button
|
||||
onClick={handleLogOut}
|
||||
>
|
||||
Log Out
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
</CodeTab>
|
||||
</CodeTabs>
|
||||
|
||||
The API route returns nothing in the response. If the request was successful, you can perform any necessary work to unset the customer and redirect them to the login page.
|
||||
@@ -0,0 +1,347 @@
|
||||
import { CodeTabs, CodeTab } from "docs-ui"
|
||||
|
||||
export const metadata = {
|
||||
title: `Login Customer in Storefront`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
In this document, you'll learn about the two ways to login a customer in a storefront.
|
||||
|
||||
## 1. Using a JWT Token
|
||||
|
||||
Using the `/auth/customer/emailpass` API route, you obtain a JSON Web Token (JWT) for the customer. Then, use that token as a bearer token in the authorization header of subsequent requests, and the customer is considered authenticated.
|
||||
|
||||
For example:
|
||||
|
||||
<CodeTabs group="store-request">
|
||||
<CodeTab label="Fetch API" value="fetch">
|
||||
|
||||
export const fetchHighlights = [
|
||||
["3", "fetch", "Send a request to obtain a JWT token."],
|
||||
["21", "fetch", "Send a request as an authenticated customer."],
|
||||
["27", "token", "Pass as a Bearer token in the authorization header."],
|
||||
]
|
||||
|
||||
```ts highlights={fetchHighlights}
|
||||
const handleLogin = async () => {
|
||||
// obtain JWT token
|
||||
const { token } = await fetch(
|
||||
`http://localhost:9000/auth/customer/emailpass`,
|
||||
{
|
||||
credentials: "include",
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
email,
|
||||
password,
|
||||
}),
|
||||
}
|
||||
)
|
||||
.then((res) => res.json())
|
||||
|
||||
// use token in the authorization header of
|
||||
// all follow up requests. For example:
|
||||
const { customer } = await fetch(
|
||||
`http://localhost:9000/store/customers/me`,
|
||||
{
|
||||
credentials: "include",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": `Bearer ${token}`,
|
||||
},
|
||||
}
|
||||
)
|
||||
.then((res) => res.json())
|
||||
|
||||
console.log(customer)
|
||||
}
|
||||
```
|
||||
|
||||
</CodeTab>
|
||||
<CodeTab label="React" value="react">
|
||||
|
||||
export const highlights = [
|
||||
["21", "fetch", "Send a request to obtain a JWT token."],
|
||||
["39", "fetch", "Send a request as an authenticated customer."],
|
||||
["45", "token", "Pass as a Bearer token in the authorization header."],
|
||||
]
|
||||
|
||||
```tsx highlights={highlights} collapsibleLines="55-79" expandButtonLabel="Show form"
|
||||
"use client" // include with Next.js 13+
|
||||
|
||||
import { useState } from "react"
|
||||
|
||||
export default function Login() {
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [email, setEmail] = useState("")
|
||||
const [password, setPassword] = useState("")
|
||||
|
||||
const handleLogin = async (
|
||||
e: React.MouseEvent<HTMLButtonElement, MouseEvent>
|
||||
) => {
|
||||
e.preventDefault()
|
||||
if (!email || !password) {
|
||||
return
|
||||
}
|
||||
|
||||
setLoading(true)
|
||||
|
||||
// obtain JWT token
|
||||
const { token } = await fetch(
|
||||
`http://localhost:9000/auth/customer/emailpass`,
|
||||
{
|
||||
credentials: "include",
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
email,
|
||||
password,
|
||||
}),
|
||||
}
|
||||
)
|
||||
.then((res) => res.json())
|
||||
|
||||
// use token in the authorization header of
|
||||
// all follow up requests. For example:
|
||||
const { customer } = await fetch(
|
||||
`http://localhost:9000/store/customers/me`,
|
||||
{
|
||||
credentials: "include",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": `Bearer ${token}`,
|
||||
},
|
||||
}
|
||||
)
|
||||
.then((res) => res.json())
|
||||
|
||||
console.log(customer)
|
||||
setLoading(false)
|
||||
}
|
||||
|
||||
return (
|
||||
<form>
|
||||
<input
|
||||
type="email"
|
||||
name="email"
|
||||
value={email}
|
||||
placeholder="Email"
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
/>
|
||||
<input
|
||||
type="password"
|
||||
name="password"
|
||||
value={password}
|
||||
placeholder="Password"
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
/>
|
||||
<button
|
||||
disabled={loading}
|
||||
onClick={handleLogin}
|
||||
>
|
||||
Login
|
||||
</button>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
</CodeTab>
|
||||
</CodeTabs>
|
||||
|
||||
In the example above, you:
|
||||
|
||||
1. Create a `handleLogin` function that logs in a customer.
|
||||
2. In the function, you obtain a JWT token by sending a request to the `/auth/customer/emailpass`.
|
||||
3. You can then use that token in the authorization header of subsequent requests, and the customer is considered authenticated. As an example, you send a request to obtain the customer's details.
|
||||
|
||||
---
|
||||
|
||||
## 2. Using a Cookie Session
|
||||
|
||||
Authenticating the customer with a cookie session means the customer is authenticated in subsequent requests that use that cookie.
|
||||
|
||||
If you're using the Fetch API, using the `credentials: include` option ensures that your cookie session is passed in every request.
|
||||
|
||||
For example:
|
||||
|
||||
<CodeTabs group="store-request">
|
||||
<CodeTab label="Fetch API" value="fetch">
|
||||
|
||||
export const fetchSessionHighlights = [
|
||||
["3", "fetch", "Send a request to obtain a JWT token."],
|
||||
["20", "fetch", "Send a request to set the authenticated session ID in the cookies."],
|
||||
["27", "token", "Pass as a Bearer token in the authorization header."],
|
||||
["35", "fetch", "Retrieve the customer's details as an example of testing authentication."],
|
||||
]
|
||||
|
||||
```ts highlights={fetchSessionHighlights}
|
||||
const handleLogin = async () => {
|
||||
// obtain JWT token
|
||||
const { token } = await fetch(
|
||||
`http://localhost:9000/auth/customer/emailpass`,
|
||||
{
|
||||
credentials: "include",
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
email,
|
||||
password,
|
||||
}),
|
||||
}
|
||||
)
|
||||
.then((res) => res.json())
|
||||
|
||||
// set session
|
||||
await fetch(
|
||||
`http://localhost:9000/auth/session`,
|
||||
{
|
||||
credentials: "include",
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": `Bearer ${token}`,
|
||||
},
|
||||
}
|
||||
)
|
||||
.then((res) => res.json())
|
||||
|
||||
// customer is now authenticated using the
|
||||
// cookie session. For example
|
||||
const { customer } = await fetch(
|
||||
`http://localhost:9000/store/customers/me`,
|
||||
{
|
||||
credentials: "include",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
)
|
||||
.then((res) => res.json())
|
||||
|
||||
console.log(customer)
|
||||
setLoading(false)
|
||||
}
|
||||
```
|
||||
|
||||
</CodeTab>
|
||||
<CodeTab label="React" value="react">
|
||||
|
||||
export const sessionHighlights = [
|
||||
["21", "fetch", "Send a request to obtain a JWT token."],
|
||||
["38", "fetch", "Send a request to set the authenticated session ID in the cookies."],
|
||||
["45", "token", "Pass as a Bearer token in the authorization header."],
|
||||
["53", "fetch", "Retrieve the customer's details as an example of testing authentication."],
|
||||
]
|
||||
|
||||
```tsx highlights={sessionHighlights} collapsibleLines="68-92" expandButtonLabel="Show form"
|
||||
"use client" // include with Next.js 13+
|
||||
|
||||
import { useState } from "react"
|
||||
|
||||
export default function Login() {
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [email, setEmail] = useState("")
|
||||
const [password, setPassword] = useState("")
|
||||
|
||||
const handleLogin = async (
|
||||
e: React.MouseEvent<HTMLButtonElement, MouseEvent>
|
||||
) => {
|
||||
e.preventDefault()
|
||||
if (!email || !password) {
|
||||
return
|
||||
}
|
||||
|
||||
setLoading(true)
|
||||
|
||||
// obtain JWT token
|
||||
const { token } = await fetch(
|
||||
`http://localhost:9000/auth/customer/emailpass`,
|
||||
{
|
||||
credentials: "include",
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
email,
|
||||
password,
|
||||
}),
|
||||
}
|
||||
)
|
||||
.then((res) => res.json())
|
||||
|
||||
// set session
|
||||
await fetch(
|
||||
`http://localhost:9000/auth/session`,
|
||||
{
|
||||
credentials: "include",
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": `Bearer ${token}`,
|
||||
},
|
||||
}
|
||||
)
|
||||
.then((res) => res.json())
|
||||
|
||||
// customer is now authenticated using the
|
||||
// cookie session. For example
|
||||
const { customer } = await fetch(
|
||||
`http://localhost:9000/store/customers/me`,
|
||||
{
|
||||
credentials: "include",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
)
|
||||
.then((res) => res.json())
|
||||
|
||||
console.log(customer)
|
||||
setLoading(false)
|
||||
}
|
||||
|
||||
return (
|
||||
<form>
|
||||
<input
|
||||
type="email"
|
||||
name="email"
|
||||
value={email}
|
||||
placeholder="Email"
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
/>
|
||||
<input
|
||||
type="password"
|
||||
name="password"
|
||||
value={password}
|
||||
placeholder="Password"
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
/>
|
||||
<button
|
||||
disabled={loading}
|
||||
onClick={handleLogin}
|
||||
>
|
||||
Login
|
||||
</button>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
</CodeTab>
|
||||
</CodeTabs>
|
||||
|
||||
In the example above, you:
|
||||
|
||||
1. Create a `handleLogin` function that logs in a customer.
|
||||
2. In the function, you obtain a JWT token by sending a request to the `/auth/customer/emailpass`.
|
||||
3. You send a request to the `/auth/session` API route passing in the authorization header the token as a Bearer token. This sets the authenticated session ID in the cookies.
|
||||
4. You can now send authenticated requests, as long as you include the `credentials: include` option in your fetch requests. For example, you send a request to retrieve the customer's details.
|
||||
@@ -0,0 +1,11 @@
|
||||
import { ChildDocs } from "docs-ui"
|
||||
|
||||
export const metadata = {
|
||||
title: `Customers in Storefront`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
A customer can register, manage their account, keep track of their orders, and more.
|
||||
|
||||
<ChildDocs type="item" onlyTopLevel={true} />
|
||||
@@ -0,0 +1,145 @@
|
||||
import { CodeTabs, CodeTab } from "docs-ui"
|
||||
|
||||
export const metadata = {
|
||||
title: `Edit Customer Profile in Storefront`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
To edit the customer's profile in the storefront, send a request to the [Update Customer API route](!api!/store#customers_postcustomersme).
|
||||
|
||||
For example:
|
||||
|
||||
<CodeTabs group="store-request">
|
||||
<CodeTab label="Fetch API" value="fetch">
|
||||
|
||||
```ts
|
||||
fetch(`http://localhost:9000/store/customers/me`, {
|
||||
credentials: "include",
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
first_name,
|
||||
last_name,
|
||||
company_name,
|
||||
phone,
|
||||
}),
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then(({ customer }) => {
|
||||
// use customer...
|
||||
console.log(customer)
|
||||
})
|
||||
```
|
||||
|
||||
</CodeTab>
|
||||
<CodeTab label="React" value="react">
|
||||
|
||||
export const highlights = [
|
||||
["4", "useCustomer", "Use the hook defined in the Customer Context guide."],
|
||||
["33"], ["34"], ["35"], ["36"], ["37"], ["38"], ["39"], ["40"], ["41"], ["42"],
|
||||
["43"], ["44"], ["45"], ["46"], ["47"], ["48"], ["49"]
|
||||
]
|
||||
|
||||
```tsx highlights={highlights} collapsibleLines="53-91" expandButtonLabel="Show form"
|
||||
"use client" // include with Next.js 13+
|
||||
|
||||
import { useState } from "react"
|
||||
import { useCustomer } from "../../../providers/customer"
|
||||
|
||||
export default function EditProfile() {
|
||||
const { customer, setCustomer } = useCustomer()
|
||||
const [firstName, setFirstName] = useState(
|
||||
customer?.first_name || ""
|
||||
)
|
||||
const [lastName, setLastName] = useState(
|
||||
customer?.last_name || ""
|
||||
)
|
||||
const [company, setCompany] = useState(
|
||||
customer?.company_name || ""
|
||||
)
|
||||
const [phone, setPhone] = useState(
|
||||
customer?.phone || ""
|
||||
)
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
const handleEdit = (
|
||||
e: React.MouseEvent<HTMLButtonElement, MouseEvent>
|
||||
) => {
|
||||
e.preventDefault()
|
||||
|
||||
if (!customer) {
|
||||
return
|
||||
}
|
||||
|
||||
setLoading(true)
|
||||
|
||||
fetch(`http://localhost:9000/store/customers/me`, {
|
||||
credentials: "include",
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
first_name: firstName,
|
||||
last_name: lastName,
|
||||
company_name: company,
|
||||
phone,
|
||||
}),
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then(({ customer: updatedCustomer }) => {
|
||||
setCustomer(updatedCustomer)
|
||||
})
|
||||
.finally(() => setLoading(false))
|
||||
}
|
||||
|
||||
return (
|
||||
<form>
|
||||
<input
|
||||
type="text"
|
||||
name="first_name"
|
||||
value={firstName}
|
||||
placeholder="First Name"
|
||||
onChange={(e) => setFirstName(e.target.value)}
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
name="last_name"
|
||||
value={lastName}
|
||||
placeholder="Last Name"
|
||||
onChange={(e) => setLastName(e.target.value)}
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
name="company"
|
||||
value={company}
|
||||
placeholder="Company"
|
||||
onChange={(e) => setCompany(e.target.value)}
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
name="phone"
|
||||
value={phone}
|
||||
placeholder="Phone Number"
|
||||
onChange={(e) => setPhone(e.target.value)}
|
||||
/>
|
||||
<button
|
||||
disabled={loading}
|
||||
onClick={handleEdit}
|
||||
>
|
||||
Edit
|
||||
</button>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
</CodeTab>
|
||||
</CodeTabs>
|
||||
|
||||
In the example above, you send a request to the Update Customer API route to update the customer's details.
|
||||
|
||||
The response of the request has a `customer` field which is a [customer object](!api!/store#customers_customer_schema).
|
||||
@@ -0,0 +1,190 @@
|
||||
import { CodeTabs, CodeTab } from "docs-ui"
|
||||
|
||||
export const metadata = {
|
||||
title: `Register Customer in Storefront`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
To register a customer, you implement the following steps:
|
||||
|
||||
1. Show the customer a form to enter their details.
|
||||
2. Send a `POST` request to the `/auth/customer/emailpass` API route to obtain a JWT token.
|
||||
3. Send a request to the [Create Customer API route](!api!/store#customers_postcustomers) pass the JWT token in the header.
|
||||
|
||||
For example:
|
||||
|
||||
<CodeTabs group="store-request">
|
||||
<CodeTab label="Fetch API" value="fetch">
|
||||
|
||||
export const fetchHighlights = [
|
||||
["3", "fetch", "Send a request to obtain a JWT token."],
|
||||
["20", "fetch", "Send a request to create the customer."],
|
||||
["27", "token", "Pass as a Bearer token in the authorization header."],
|
||||
["39", "TODO", "Redirect the customer to the log in page."]
|
||||
]
|
||||
|
||||
```ts highlights={fetchHighlights}
|
||||
const handleRegistration = async () => {
|
||||
// obtain JWT token
|
||||
const { token } = await fetch(
|
||||
`http://localhost:9000/auth/customer/emailpass`,
|
||||
{
|
||||
credentials: "include",
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
email,
|
||||
password,
|
||||
}),
|
||||
}
|
||||
)
|
||||
.then((res) => res.json())
|
||||
|
||||
// create customer
|
||||
const { customer } = await fetch(
|
||||
`http://localhost:9000/store/customers`,
|
||||
{
|
||||
credentials: "include",
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": `Bearer ${token}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
first_name: firstName,
|
||||
last_name: lastName,
|
||||
email,
|
||||
}),
|
||||
}
|
||||
)
|
||||
.then((res) => res.json())
|
||||
|
||||
console.log(customer)
|
||||
// TODO redirect to login page
|
||||
}
|
||||
```
|
||||
|
||||
</CodeTab>
|
||||
<CodeTab label="React" value="react">
|
||||
|
||||
export const highlights = [
|
||||
["22", "fetch", "Send a request to obtain a JWT token."],
|
||||
["39", "fetch", "Send a request to create the customer."],
|
||||
["46", "token", "Pass as a Bearer token in the authorization header."],
|
||||
["59", "TODO", "Redirect the customer to the log in page."]
|
||||
]
|
||||
|
||||
```tsx highlights={highlights} collapsibleLines="61-100" expandButtonLabel="Show form"
|
||||
"use client" // include with Next.js 13+
|
||||
|
||||
import { useState } from "react"
|
||||
|
||||
export default function Register() {
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [firstName, setFirstName] = useState("")
|
||||
const [lastName, setLastName] = useState("")
|
||||
const [email, setEmail] = useState("")
|
||||
const [password, setPassword] = useState("")
|
||||
|
||||
const handleRegistration = async (
|
||||
e: React.MouseEvent<HTMLButtonElement, MouseEvent>
|
||||
) => {
|
||||
e.preventDefault()
|
||||
if (!firstName || !lastName || !email || !password) {
|
||||
return
|
||||
}
|
||||
setLoading(true)
|
||||
|
||||
// obtain JWT token
|
||||
const { token } = await fetch(
|
||||
`http://localhost:9000/auth/customer/emailpass`,
|
||||
{
|
||||
credentials: "include",
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
email,
|
||||
password,
|
||||
}),
|
||||
}
|
||||
)
|
||||
.then((res) => res.json())
|
||||
|
||||
// create customer
|
||||
const { customer } = await fetch(
|
||||
`http://localhost:9000/store/customers`,
|
||||
{
|
||||
credentials: "include",
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": `Bearer ${token}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
first_name: firstName,
|
||||
last_name: lastName,
|
||||
email,
|
||||
}),
|
||||
}
|
||||
)
|
||||
.then((res) => res.json())
|
||||
|
||||
console.log(customer)
|
||||
setLoading(false)
|
||||
// TODO redirect to login page
|
||||
}
|
||||
|
||||
return (
|
||||
<form>
|
||||
<input
|
||||
type="text"
|
||||
name="first_name"
|
||||
value={firstName}
|
||||
placeholder="First Name"
|
||||
onChange={(e) => setFirstName(e.target.value)}
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
name="last_name"
|
||||
value={lastName}
|
||||
placeholder="Last Name"
|
||||
onChange={(e) => setLastName(e.target.value)}
|
||||
/>
|
||||
<input
|
||||
type="email"
|
||||
name="email"
|
||||
value={email}
|
||||
placeholder="Email"
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
/>
|
||||
<input
|
||||
type="password"
|
||||
name="password"
|
||||
value={password}
|
||||
placeholder="Password"
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
/>
|
||||
<button
|
||||
disabled={loading}
|
||||
onClick={handleRegistration}
|
||||
>
|
||||
Register
|
||||
</button>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
</CodeTab>
|
||||
</CodeTabs>
|
||||
|
||||
In the above example, you create a `handleRegistration` function that:
|
||||
|
||||
- Obtains a JWT token from the `/auth/customer/emailpass` API route.
|
||||
- Send a request to the Create Customer API route, and pass the JWT token as a Bearer token in the authorization header.
|
||||
- Once the customer is registered successfully, you can either redirect the customer to the login page or log them in automatically as explained in this guide.
|
||||
@@ -0,0 +1,66 @@
|
||||
import { CodeTabs, CodeTab } from "docs-ui"
|
||||
|
||||
export const metadata = {
|
||||
title: `Retrieve Customer in Storefront`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
To retrieve a customer after it's been authenticated in your storefront, send a request to the [Get Customer API route](!api!/store#customers_getcustomersme):
|
||||
|
||||
<CodeTabs group="authenticated-request">
|
||||
<CodeTab label="Using Bearer Token" value="bearer">
|
||||
|
||||
export const bearerHighlights = [
|
||||
["7", "", "Pass JWT token as bearer token in authorization header."],
|
||||
]
|
||||
|
||||
```ts highlights={bearerHighlights}
|
||||
fetch(
|
||||
`http://localhost:9000/store/customers/me`,
|
||||
{
|
||||
credentials: "include",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": `Bearer ${token}`,
|
||||
},
|
||||
}
|
||||
)
|
||||
.then((res) => res.json())
|
||||
.then(({ customer }) => {
|
||||
// use customer...
|
||||
console.log(customer)
|
||||
})
|
||||
```
|
||||
|
||||
</CodeTab>
|
||||
<CodeTab label="Using Cookie Session" value="session">
|
||||
|
||||
export const sessionHighlights = [
|
||||
["4", "", "Pass this option to ensure the cookie session is passed in the request."],
|
||||
]
|
||||
|
||||
```ts highlights={sessionHighlights}
|
||||
fetch(
|
||||
`http://localhost:9000/store/customers/me`,
|
||||
{
|
||||
credentials: "include",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
)
|
||||
.then((res) => res.json())
|
||||
.then(({ customer }) => {
|
||||
// use customer...
|
||||
console.log(customer)
|
||||
})
|
||||
```
|
||||
|
||||
</CodeTab>
|
||||
</CodeTabs>
|
||||
|
||||
- If you authenticate the customer with bearer authorization, pass the token in the authorization header of the request.
|
||||
- If you authenticate the customer with cookie session, pass the `credentials: include` option to the `fetch` function.
|
||||
|
||||
The Get Customer API route returns a `customer` field, which is a [customer object](!api!/store#customers_customer_schema).
|
||||
Reference in new issue
Block a user