docs: update docusaurus to v3 (#5625)
* update dependencies * update onboarding mdx * fixes for mdx issues * fixes for mdx compatibility * resolve mdx errors * fixes in reference * fix check errors * revert change in vale action * fix node version in action * fix summary in markdown
This commit is contained in:
@@ -143,52 +143,52 @@ export default (rootDirectory) => {
|
||||
Then, create an object that will hold the CORS configurations. Based on whether it's storefront or admin CORS options, you pass it the respective configuration from `projectConfig`:
|
||||
|
||||
<Tabs groupId="endpoint-type" isCodeTabs={true}>
|
||||
<TabItem value="storefront" label="Storefront CORS" default>
|
||||
<TabItem value="storefront" label="Storefront CORS" default>
|
||||
|
||||
```ts
|
||||
const storeCorsOptions = {
|
||||
origin: projectConfig.store_cors.split(","),
|
||||
credentials: true,
|
||||
}
|
||||
```
|
||||
```ts
|
||||
const storeCorsOptions = {
|
||||
origin: projectConfig.store_cors.split(","),
|
||||
credentials: true,
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="admin" label="Admin CORS">
|
||||
</TabItem>
|
||||
<TabItem value="admin" label="Admin CORS">
|
||||
|
||||
```ts
|
||||
const adminCorsOptions = {
|
||||
origin: projectConfig.admin_cors.split(","),
|
||||
credentials: true,
|
||||
}
|
||||
```
|
||||
```ts
|
||||
const adminCorsOptions = {
|
||||
origin: projectConfig.admin_cors.split(","),
|
||||
credentials: true,
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
Finally, you can either pass the `cors` middleware for a specific route, or pass it to the entire router:
|
||||
|
||||
<Tabs groupId="pass-type" isCodeTabs={true}>
|
||||
<TabItem value="single" label="Pass to Endpoint" default>
|
||||
<TabItem value="single" label="Pass to Endpoint" default>
|
||||
|
||||
```ts
|
||||
adminRouter.options("/admin/hello", cors(adminCorsOptions))
|
||||
adminRouter.get(
|
||||
"/admin/hello",
|
||||
cors(adminCorsOptions),
|
||||
(req, res) => {
|
||||
// ...
|
||||
}
|
||||
)
|
||||
```
|
||||
```ts
|
||||
adminRouter.options("/admin/hello", cors(adminCorsOptions))
|
||||
adminRouter.get(
|
||||
"/admin/hello",
|
||||
cors(adminCorsOptions),
|
||||
(req, res) => {
|
||||
// ...
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="router" label="Pass to Router">
|
||||
</TabItem>
|
||||
<TabItem value="router" label="Pass to Router">
|
||||
|
||||
```ts
|
||||
adminRouter.use(cors(adminCorsOptions))
|
||||
```
|
||||
```ts
|
||||
adminRouter.use(cors(adminCorsOptions))
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
---
|
||||
@@ -252,43 +252,43 @@ import { requireCustomerAuthentication } from "@medusajs/medusa"
|
||||
Then, pass the middleware to either a single route or an entire router:
|
||||
|
||||
<Tabs groupId="pass-type" isCodeTabs={true}>
|
||||
<TabItem value="single" label="Pass to Endpoint" default>
|
||||
<TabItem value="single" label="Pass to Endpoint" default>
|
||||
|
||||
```ts
|
||||
// only necessary if you're passing cors options per route
|
||||
router.options("/store/hello", cors(storeCorsOptions))
|
||||
router.get(
|
||||
"/store/hello",
|
||||
cors(storeCorsOptions),
|
||||
requireCustomerAuthentication(),
|
||||
// authenticateCustomer()
|
||||
async (req, res) => {
|
||||
// access current customer
|
||||
const id = req.user.customer_id
|
||||
// if you're using authenticateCustomer middleware
|
||||
// check if id is set first
|
||||
```ts
|
||||
// only necessary if you're passing cors options per route
|
||||
router.options("/store/hello", cors(storeCorsOptions))
|
||||
router.get(
|
||||
"/store/hello",
|
||||
cors(storeCorsOptions),
|
||||
requireCustomerAuthentication(),
|
||||
// authenticateCustomer()
|
||||
async (req, res) => {
|
||||
// access current customer
|
||||
const id = req.user.customer_id
|
||||
// if you're using authenticateCustomer middleware
|
||||
// check if id is set first
|
||||
|
||||
const customerService = req.scope.resolve("customerService")
|
||||
|
||||
const customer = await customerService.retrieve(id)
|
||||
// ...
|
||||
}
|
||||
)
|
||||
```
|
||||
const customerService = req.scope.resolve("customerService")
|
||||
|
||||
const customer = await customerService.retrieve(id)
|
||||
// ...
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="router" label="Pass to Router">
|
||||
</TabItem>
|
||||
<TabItem value="router" label="Pass to Router">
|
||||
|
||||
```ts
|
||||
storeRouter.use(requireCustomerAuthentication())
|
||||
// all routes added to storeRouter are now protected
|
||||
// the logged in customer can be accessed using:
|
||||
// req.user.customer_id
|
||||
```ts
|
||||
storeRouter.use(requireCustomerAuthentication())
|
||||
// all routes added to storeRouter are now protected
|
||||
// the logged in customer can be accessed using:
|
||||
// req.user.customer_id
|
||||
|
||||
// storeRouter.use(authenticateCustomer())
|
||||
```
|
||||
// storeRouter.use(authenticateCustomer())
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
### Protect Admin Routes
|
||||
@@ -304,37 +304,37 @@ import { authenticate } from "@medusajs/medusa"
|
||||
Then, pass the middleware to either a single route or an entire router:
|
||||
|
||||
<Tabs groupId="pass-type" isCodeTabs={true}>
|
||||
<TabItem value="single" label="Pass to Endpoint" default>
|
||||
<TabItem value="single" label="Pass to Endpoint" default>
|
||||
|
||||
```ts
|
||||
// only necessary if you're passing cors options per route
|
||||
adminRouter.options("/admin/hello", cors(adminCorsOptions))
|
||||
adminRouter.get(
|
||||
"/admin/hello",
|
||||
cors(adminCorsOptions),
|
||||
authenticate(),
|
||||
async (req, res) => {
|
||||
// access current user
|
||||
const id = req.user.userId
|
||||
const userService = req.scope.resolve("userService")
|
||||
|
||||
const user = await userService.retrieve(id)
|
||||
// ...
|
||||
}
|
||||
)
|
||||
```
|
||||
```ts
|
||||
// only necessary if you're passing cors options per route
|
||||
adminRouter.options("/admin/hello", cors(adminCorsOptions))
|
||||
adminRouter.get(
|
||||
"/admin/hello",
|
||||
cors(adminCorsOptions),
|
||||
authenticate(),
|
||||
async (req, res) => {
|
||||
// access current user
|
||||
const id = req.user.userId
|
||||
const userService = req.scope.resolve("userService")
|
||||
|
||||
const user = await userService.retrieve(id)
|
||||
// ...
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="router" label="Pass to Router">
|
||||
</TabItem>
|
||||
<TabItem value="router" label="Pass to Router">
|
||||
|
||||
```ts
|
||||
adminRouter.use(authenticate())
|
||||
// all routes added to adminRouter are now protected
|
||||
// the logged in user can be accessed using:
|
||||
// req.user.userId
|
||||
```
|
||||
```ts
|
||||
adminRouter.use(authenticate())
|
||||
// all routes added to adminRouter are now protected
|
||||
// the logged in user can be accessed using:
|
||||
// req.user.userId
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
---
|
||||
@@ -628,318 +628,318 @@ This section services as an example of creating endpoints that perform Create, R
|
||||
You can refer to the [Entities](../entities/create.mdx#adding-relations) and [Services](../services/create-service.mdx#example-services-with-crud-operations) documentation to learn how to create the custom entities and services used in this example.
|
||||
|
||||
<Tabs groupId="files" isCodeTabs={true}>
|
||||
<TabItem value="index" label="src/api/index.ts" default>
|
||||
<TabItem value="index" label="src/api/index.ts" default>
|
||||
|
||||
```ts
|
||||
import express, { Router } from "express"
|
||||
import adminRoutes from "./admin"
|
||||
import storeRoutes from "./store"
|
||||
import { errorHandler } from "@medusajs/medusa"
|
||||
```ts
|
||||
import express, { Router } from "express"
|
||||
import adminRoutes from "./admin"
|
||||
import storeRoutes from "./store"
|
||||
import { errorHandler } from "@medusajs/medusa"
|
||||
|
||||
export default (rootDirectory, options) => {
|
||||
const router = Router()
|
||||
export default (rootDirectory, options) => {
|
||||
const router = Router()
|
||||
|
||||
router.use(express.json())
|
||||
router.use(express.urlencoded({ extended: true }))
|
||||
router.use(express.json())
|
||||
router.use(express.urlencoded({ extended: true }))
|
||||
|
||||
adminRoutes(router, options)
|
||||
storeRoutes(router, options)
|
||||
adminRoutes(router, options)
|
||||
storeRoutes(router, options)
|
||||
|
||||
router.use(errorHandler())
|
||||
router.use(errorHandler())
|
||||
|
||||
return router
|
||||
}
|
||||
```
|
||||
return router
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="admin" label="src/api/admin.ts">
|
||||
</TabItem>
|
||||
<TabItem value="admin" label="src/api/admin.ts">
|
||||
|
||||
```ts
|
||||
import { Router } from "express"
|
||||
import PostService from "../services/post"
|
||||
import {
|
||||
ConfigModule,
|
||||
} from "@medusajs/medusa/dist/types/global"
|
||||
import cors from "cors"
|
||||
import { authenticate, wrapHandler } from "@medusajs/medusa"
|
||||
import AuthorService from "../services/author"
|
||||
```ts
|
||||
import { Router } from "express"
|
||||
import PostService from "../services/post"
|
||||
import {
|
||||
ConfigModule,
|
||||
} from "@medusajs/medusa/dist/types/global"
|
||||
import cors from "cors"
|
||||
import { authenticate, wrapHandler } from "@medusajs/medusa"
|
||||
import AuthorService from "../services/author"
|
||||
|
||||
export default function adminRoutes(
|
||||
router: Router,
|
||||
options: ConfigModule
|
||||
) {
|
||||
const { projectConfig } = options
|
||||
export default function adminRoutes(
|
||||
router: Router,
|
||||
options: ConfigModule
|
||||
) {
|
||||
const { projectConfig } = options
|
||||
|
||||
const corsOptions = {
|
||||
origin: projectConfig.admin_cors.split(","),
|
||||
credentials: true,
|
||||
}
|
||||
|
||||
const adminRouter = Router()
|
||||
|
||||
router.use("/admin/blog", adminRouter)
|
||||
|
||||
adminRouter.use(cors(corsOptions))
|
||||
adminRouter.use(authenticate())
|
||||
|
||||
// it's recommended to define the routes
|
||||
// in separate files. They're done in
|
||||
// the same file here for simplicity
|
||||
|
||||
|
||||
// list all blog posts
|
||||
adminRouter.get(
|
||||
"/posts",
|
||||
wrapHandler(async (req, res) => {
|
||||
const postService: PostService = req.scope.resolve(
|
||||
"postService"
|
||||
)
|
||||
|
||||
res.json({
|
||||
posts: await postService.list(),
|
||||
})
|
||||
}))
|
||||
|
||||
|
||||
// retrieve a single blog post
|
||||
adminRouter.get(
|
||||
"/posts/:id",
|
||||
wrapHandler(async (req, res) => {
|
||||
const postService: PostService = req.scope.resolve(
|
||||
"postService"
|
||||
)
|
||||
|
||||
const post = await postService.retrieve(req.params.id)
|
||||
|
||||
res.json({
|
||||
post,
|
||||
})
|
||||
}))
|
||||
|
||||
// create a blog post
|
||||
adminRouter.post(
|
||||
"/posts",
|
||||
wrapHandler(async (req, res) => {
|
||||
const postService: PostService = req.scope.resolve(
|
||||
"postService"
|
||||
)
|
||||
|
||||
// basic validation of request body
|
||||
if (!req.body.title || !req.body.author_id) {
|
||||
throw new Error("`title` and `author_id` are required.")
|
||||
const corsOptions = {
|
||||
origin: projectConfig.admin_cors.split(","),
|
||||
credentials: true,
|
||||
}
|
||||
|
||||
const post = await postService.create(req.body)
|
||||
const adminRouter = Router()
|
||||
|
||||
res.json({
|
||||
post,
|
||||
})
|
||||
}))
|
||||
router.use("/admin/blog", adminRouter)
|
||||
|
||||
// update a blog post
|
||||
adminRouter.post(
|
||||
"/posts/:id",
|
||||
wrapHandler(async (req, res) => {
|
||||
const postService: PostService = req.scope.resolve(
|
||||
"postService"
|
||||
)
|
||||
adminRouter.use(cors(corsOptions))
|
||||
adminRouter.use(authenticate())
|
||||
|
||||
// basic validation of request body
|
||||
if (req.body.id) {
|
||||
throw new Error("Can't update post ID")
|
||||
// it's recommended to define the routes
|
||||
// in separate files. They're done in
|
||||
// the same file here for simplicity
|
||||
|
||||
|
||||
// list all blog posts
|
||||
adminRouter.get(
|
||||
"/posts",
|
||||
wrapHandler(async (req, res) => {
|
||||
const postService: PostService = req.scope.resolve(
|
||||
"postService"
|
||||
)
|
||||
|
||||
res.json({
|
||||
posts: await postService.list(),
|
||||
})
|
||||
}))
|
||||
|
||||
|
||||
// retrieve a single blog post
|
||||
adminRouter.get(
|
||||
"/posts/:id",
|
||||
wrapHandler(async (req, res) => {
|
||||
const postService: PostService = req.scope.resolve(
|
||||
"postService"
|
||||
)
|
||||
|
||||
const post = await postService.retrieve(req.params.id)
|
||||
|
||||
res.json({
|
||||
post,
|
||||
})
|
||||
}))
|
||||
|
||||
// create a blog post
|
||||
adminRouter.post(
|
||||
"/posts",
|
||||
wrapHandler(async (req, res) => {
|
||||
const postService: PostService = req.scope.resolve(
|
||||
"postService"
|
||||
)
|
||||
|
||||
// basic validation of request body
|
||||
if (!req.body.title || !req.body.author_id) {
|
||||
throw new Error("`title` and `author_id` are required.")
|
||||
}
|
||||
|
||||
const post = await postService.create(req.body)
|
||||
|
||||
res.json({
|
||||
post,
|
||||
})
|
||||
}))
|
||||
|
||||
// update a blog post
|
||||
adminRouter.post(
|
||||
"/posts/:id",
|
||||
wrapHandler(async (req, res) => {
|
||||
const postService: PostService = req.scope.resolve(
|
||||
"postService"
|
||||
)
|
||||
|
||||
// basic validation of request body
|
||||
if (req.body.id) {
|
||||
throw new Error("Can't update post ID")
|
||||
}
|
||||
|
||||
const post = await postService.update(
|
||||
req.params.id,
|
||||
req.body
|
||||
)
|
||||
|
||||
res.json({
|
||||
post,
|
||||
})
|
||||
}))
|
||||
|
||||
// delete a blog post
|
||||
adminRouter.delete(
|
||||
"/posts/:id",
|
||||
wrapHandler(async (req, res) => {
|
||||
const postService: PostService = req.scope.resolve(
|
||||
"postService"
|
||||
)
|
||||
|
||||
await postService.delete(req.params.id)
|
||||
|
||||
res.status(200).end()
|
||||
}))
|
||||
|
||||
// list all blog authors
|
||||
adminRouter.get(
|
||||
"/authors",
|
||||
wrapHandler(async (req, res) => {
|
||||
const authorService: AuthorService = req.scope.resolve(
|
||||
"authorService"
|
||||
)
|
||||
|
||||
res.json({
|
||||
authors: await authorService.list(),
|
||||
})
|
||||
}))
|
||||
|
||||
// retrieve a single blog author
|
||||
adminRouter.get(
|
||||
"/authors/:id",
|
||||
wrapHandler(async (req, res) => {
|
||||
const authorService: AuthorService = req.scope.resolve(
|
||||
"authorService"
|
||||
)
|
||||
|
||||
res.json({
|
||||
post: await authorService.retrieve(req.params.id),
|
||||
})
|
||||
}))
|
||||
|
||||
// create a blog author
|
||||
adminRouter.post(
|
||||
"/authors",
|
||||
wrapHandler(async (req, res) => {
|
||||
const authorService: AuthorService = req.scope.resolve(
|
||||
"authorService"
|
||||
)
|
||||
|
||||
// basic validation of request body
|
||||
if (!req.body.name) {
|
||||
throw new Error("`name` is required.")
|
||||
}
|
||||
|
||||
const author = await authorService.create(req.body)
|
||||
|
||||
res.json({
|
||||
author,
|
||||
})
|
||||
}))
|
||||
|
||||
// update a blog author
|
||||
adminRouter.post(
|
||||
"/authors/:id",
|
||||
wrapHandler(async (req, res) => {
|
||||
const authorService: AuthorService = req.scope.resolve(
|
||||
"authorService"
|
||||
)
|
||||
|
||||
// basic validation of request body
|
||||
if (req.body.id) {
|
||||
throw new Error("Can't update author ID")
|
||||
}
|
||||
|
||||
const author = await authorService.update(
|
||||
req.params.id,
|
||||
req.body
|
||||
)
|
||||
|
||||
res.json({
|
||||
author,
|
||||
})
|
||||
}))
|
||||
|
||||
// delete a blog author
|
||||
adminRouter.delete(
|
||||
"/authors/:id",
|
||||
wrapHandler(async (req, res) => {
|
||||
const authorService: AuthorService = req.scope.resolve(
|
||||
"authorService"
|
||||
)
|
||||
|
||||
await authorService.delete(req.params.id)
|
||||
|
||||
res.status(200).end()
|
||||
}))
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="store" label="src/api/store.ts">
|
||||
|
||||
```ts
|
||||
import { Router } from "express"
|
||||
import {
|
||||
ConfigModule,
|
||||
} from "@medusajs/medusa/dist/types/global"
|
||||
import PostService from "../services/post"
|
||||
import cors from "cors"
|
||||
import AuthorService from "../services/author"
|
||||
import { wrapHandler } from "@medusajs/medusa"
|
||||
|
||||
export default function storeRoutes(
|
||||
router: Router,
|
||||
options: ConfigModule
|
||||
) {
|
||||
const { projectConfig } = options
|
||||
|
||||
const storeCorsOptions = {
|
||||
origin: projectConfig.store_cors.split(","),
|
||||
credentials: true,
|
||||
}
|
||||
|
||||
const post = await postService.update(
|
||||
req.params.id,
|
||||
req.body
|
||||
)
|
||||
const storeRouter = Router()
|
||||
router.use("/store/blog", storeRouter)
|
||||
|
||||
res.json({
|
||||
post,
|
||||
})
|
||||
}))
|
||||
storeRouter.use(cors(storeCorsOptions))
|
||||
|
||||
// list all blog posts
|
||||
storeRouter.get(
|
||||
"/posts",
|
||||
wrapHandler(async (req, res) => {
|
||||
const postService: PostService = req.scope.resolve(
|
||||
"postService"
|
||||
)
|
||||
|
||||
// delete a blog post
|
||||
adminRouter.delete(
|
||||
"/posts/:id",
|
||||
wrapHandler(async (req, res) => {
|
||||
const postService: PostService = req.scope.resolve(
|
||||
"postService"
|
||||
)
|
||||
res.json({
|
||||
posts: await postService.list(),
|
||||
})
|
||||
}))
|
||||
|
||||
await postService.delete(req.params.id)
|
||||
// retrieve a single blog post
|
||||
storeRouter.get(
|
||||
"/posts/:id",
|
||||
wrapHandler(async (req, res) => {
|
||||
const postService: PostService = req.scope.resolve(
|
||||
"postService"
|
||||
)
|
||||
|
||||
res.status(200).end()
|
||||
}))
|
||||
res.json({
|
||||
post: await postService.retrieve(req.params.id),
|
||||
})
|
||||
}))
|
||||
|
||||
// list all blog authors
|
||||
adminRouter.get(
|
||||
"/authors",
|
||||
wrapHandler(async (req, res) => {
|
||||
const authorService: AuthorService = req.scope.resolve(
|
||||
"authorService"
|
||||
)
|
||||
// list all blog authors
|
||||
storeRouter.get(
|
||||
"/authors",
|
||||
wrapHandler(async (req, res) => {
|
||||
const authorService: AuthorService = req.scope.resolve(
|
||||
"authorService"
|
||||
)
|
||||
|
||||
res.json({
|
||||
authors: await authorService.list(),
|
||||
})
|
||||
}))
|
||||
res.json({
|
||||
authors: await authorService.list(),
|
||||
})
|
||||
}))
|
||||
|
||||
// retrieve a single blog author
|
||||
adminRouter.get(
|
||||
"/authors/:id",
|
||||
wrapHandler(async (req, res) => {
|
||||
const authorService: AuthorService = req.scope.resolve(
|
||||
"authorService"
|
||||
)
|
||||
// retrieve a single blog author
|
||||
storeRouter.get(
|
||||
"/authors/:id",
|
||||
wrapHandler(async (req, res) => {
|
||||
const authorService: AuthorService = req.scope.resolve(
|
||||
"authorService"
|
||||
)
|
||||
|
||||
res.json({
|
||||
post: await authorService.retrieve(req.params.id),
|
||||
})
|
||||
}))
|
||||
res.json({
|
||||
post: await authorService.retrieve(req.params.id),
|
||||
})
|
||||
}))
|
||||
}
|
||||
```
|
||||
|
||||
// create a blog author
|
||||
adminRouter.post(
|
||||
"/authors",
|
||||
wrapHandler(async (req, res) => {
|
||||
const authorService: AuthorService = req.scope.resolve(
|
||||
"authorService"
|
||||
)
|
||||
|
||||
// basic validation of request body
|
||||
if (!req.body.name) {
|
||||
throw new Error("`name` is required.")
|
||||
}
|
||||
|
||||
const author = await authorService.create(req.body)
|
||||
|
||||
res.json({
|
||||
author,
|
||||
})
|
||||
}))
|
||||
|
||||
// update a blog author
|
||||
adminRouter.post(
|
||||
"/authors/:id",
|
||||
wrapHandler(async (req, res) => {
|
||||
const authorService: AuthorService = req.scope.resolve(
|
||||
"authorService"
|
||||
)
|
||||
|
||||
// basic validation of request body
|
||||
if (req.body.id) {
|
||||
throw new Error("Can't update author ID")
|
||||
}
|
||||
|
||||
const author = await authorService.update(
|
||||
req.params.id,
|
||||
req.body
|
||||
)
|
||||
|
||||
res.json({
|
||||
author,
|
||||
})
|
||||
}))
|
||||
|
||||
// delete a blog author
|
||||
adminRouter.delete(
|
||||
"/authors/:id",
|
||||
wrapHandler(async (req, res) => {
|
||||
const authorService: AuthorService = req.scope.resolve(
|
||||
"authorService"
|
||||
)
|
||||
|
||||
await authorService.delete(req.params.id)
|
||||
|
||||
res.status(200).end()
|
||||
}))
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="store" label="src/api/store.ts">
|
||||
|
||||
```ts
|
||||
import { Router } from "express"
|
||||
import {
|
||||
ConfigModule,
|
||||
} from "@medusajs/medusa/dist/types/global"
|
||||
import PostService from "../services/post"
|
||||
import cors from "cors"
|
||||
import AuthorService from "../services/author"
|
||||
import { wrapHandler } from "@medusajs/medusa"
|
||||
|
||||
export default function storeRoutes(
|
||||
router: Router,
|
||||
options: ConfigModule
|
||||
) {
|
||||
const { projectConfig } = options
|
||||
|
||||
const storeCorsOptions = {
|
||||
origin: projectConfig.store_cors.split(","),
|
||||
credentials: true,
|
||||
}
|
||||
|
||||
const storeRouter = Router()
|
||||
router.use("/store/blog", storeRouter)
|
||||
|
||||
storeRouter.use(cors(storeCorsOptions))
|
||||
|
||||
// list all blog posts
|
||||
storeRouter.get(
|
||||
"/posts",
|
||||
wrapHandler(async (req, res) => {
|
||||
const postService: PostService = req.scope.resolve(
|
||||
"postService"
|
||||
)
|
||||
|
||||
res.json({
|
||||
posts: await postService.list(),
|
||||
})
|
||||
}))
|
||||
|
||||
// retrieve a single blog post
|
||||
storeRouter.get(
|
||||
"/posts/:id",
|
||||
wrapHandler(async (req, res) => {
|
||||
const postService: PostService = req.scope.resolve(
|
||||
"postService"
|
||||
)
|
||||
|
||||
res.json({
|
||||
post: await postService.retrieve(req.params.id),
|
||||
})
|
||||
}))
|
||||
|
||||
// list all blog authors
|
||||
storeRouter.get(
|
||||
"/authors",
|
||||
wrapHandler(async (req, res) => {
|
||||
const authorService: AuthorService = req.scope.resolve(
|
||||
"authorService"
|
||||
)
|
||||
|
||||
res.json({
|
||||
authors: await authorService.list(),
|
||||
})
|
||||
}))
|
||||
|
||||
// retrieve a single blog author
|
||||
storeRouter.get(
|
||||
"/authors/:id",
|
||||
wrapHandler(async (req, res) => {
|
||||
const authorService: AuthorService = req.scope.resolve(
|
||||
"authorService"
|
||||
)
|
||||
|
||||
res.json({
|
||||
post: await authorService.retrieve(req.params.id),
|
||||
})
|
||||
}))
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
---
|
||||
|
||||
@@ -538,25 +538,23 @@ After using `MedusaError`, the returned error in the response provides a clearer
|
||||
}
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
Available MedusaError Types and their respective status codes
|
||||
</summary>
|
||||
<Details>
|
||||
<Summary>Available MedusaError Types and their respective status codes</Summary>
|
||||
|
||||
The default response code is `500` unless mentioned otherwise.
|
||||
The default response code is `500` unless mentioned otherwise.
|
||||
|
||||
- `MedusaError.Types.DB_ERROR`: Sets the response code to `500`.
|
||||
- `MedusaError.Types.DUPLICATE_ERROR`: Sets the response code to `422`.
|
||||
- `MedusaError.Types.INVALID_ARGUMENT`
|
||||
- `MedusaError.Types.INVALID_DATA`: Sets the resposne code to `400`.
|
||||
- `MedusaError.Types.UNAUTHORIZED`: Sets the resposne code to `401`.
|
||||
- `MedusaError.Types.NOT_FOUND`: Sets the response code to `404`.
|
||||
- `MedusaError.Types.NOT_ALLOWED`: Sets the resposne code to `400`.
|
||||
- `MedusaError.Types.UNEXPECTED_STATE`
|
||||
- `MedusaError.Types.CONFLICT`: Sets the resposne code to `409`.
|
||||
- `MedusaError.Types.PAYMENT_AUTHORIZATION_ERROR`: Sets the resposne code to `422`.
|
||||
- `MedusaError.Types.DB_ERROR`: Sets the response code to `500`.
|
||||
- `MedusaError.Types.DUPLICATE_ERROR`: Sets the response code to `422`.
|
||||
- `MedusaError.Types.INVALID_ARGUMENT`
|
||||
- `MedusaError.Types.INVALID_DATA`: Sets the resposne code to `400`.
|
||||
- `MedusaError.Types.UNAUTHORIZED`: Sets the resposne code to `401`.
|
||||
- `MedusaError.Types.NOT_FOUND`: Sets the response code to `404`.
|
||||
- `MedusaError.Types.NOT_ALLOWED`: Sets the resposne code to `400`.
|
||||
- `MedusaError.Types.UNEXPECTED_STATE`
|
||||
- `MedusaError.Types.CONFLICT`: Sets the resposne code to `409`.
|
||||
- `MedusaError.Types.PAYMENT_AUTHORIZATION_ERROR`: Sets the resposne code to `422`.
|
||||
|
||||
</details>
|
||||
</Details>
|
||||
|
||||
### Override Error Handler
|
||||
|
||||
@@ -691,41 +689,41 @@ Files and directories prefixed with `_` are ignored. This can be helpful if you
|
||||
For example:
|
||||
|
||||
<Tabs groupId="files" isCodeTabs={true}>
|
||||
<TabItem value="custom-route" label="src/api/custom/route.ts" default>
|
||||
<TabItem value="custom-route" label="src/api/custom/route.ts" default>
|
||||
|
||||
```ts
|
||||
import getProducts from "../_methods/get-products"
|
||||
```ts
|
||||
import getProducts from "../_methods/get-products"
|
||||
|
||||
export const GET = getProducts
|
||||
```
|
||||
export const GET = getProducts
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="internal-method" label="src/api/_methods/get-product.ts">
|
||||
</TabItem>
|
||||
<TabItem value="internal-method" label="src/api/_methods/get-product.ts">
|
||||
|
||||
```ts
|
||||
import {
|
||||
MedusaRequest,
|
||||
MedusaResponse,
|
||||
ProductService,
|
||||
} from "@medusajs/medusa"
|
||||
```ts
|
||||
import {
|
||||
MedusaRequest,
|
||||
MedusaResponse,
|
||||
ProductService,
|
||||
} from "@medusajs/medusa"
|
||||
|
||||
export default async function (
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
) {
|
||||
const productService = req.scope.resolve<ProductService>(
|
||||
"productService"
|
||||
)
|
||||
export default async function (
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
) {
|
||||
const productService = req.scope.resolve<ProductService>(
|
||||
"productService"
|
||||
)
|
||||
|
||||
const products = await productService.list({})
|
||||
const products = await productService.list({})
|
||||
|
||||
res.json({
|
||||
products,
|
||||
})
|
||||
}
|
||||
```
|
||||
res.json({
|
||||
products,
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
---
|
||||
@@ -741,117 +739,117 @@ You can refer to the [Entities](../entities/create.mdx#adding-relations) and [Se
|
||||
:::
|
||||
|
||||
<Tabs groupId="files" isCodeTabs={true}>
|
||||
<TabItem value="posts-routes" label="src/api/admin/posts/route.ts" default>
|
||||
<TabItem value="posts-routes" label="src/api/admin/posts/route.ts" default>
|
||||
|
||||
```ts
|
||||
import type {
|
||||
MedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "@medusajs/medusa"
|
||||
import { PostService } from "../../../services/post"
|
||||
```ts
|
||||
import type {
|
||||
MedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "@medusajs/medusa"
|
||||
import { PostService } from "../../../services/post"
|
||||
|
||||
// list posts
|
||||
export const GET = async (
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const postService: PostService = req.scope.resolve(
|
||||
"postService"
|
||||
)
|
||||
// list posts
|
||||
export const GET = async (
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const postService: PostService = req.scope.resolve(
|
||||
"postService"
|
||||
)
|
||||
|
||||
res.json({
|
||||
posts: await postService.list(),
|
||||
})
|
||||
}
|
||||
res.json({
|
||||
posts: await postService.list(),
|
||||
})
|
||||
}
|
||||
|
||||
// create a post
|
||||
export const POST = async (
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const postService: PostService = req.scope.resolve(
|
||||
"postService"
|
||||
)
|
||||
// create a post
|
||||
export const POST = async (
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const postService: PostService = req.scope.resolve(
|
||||
"postService"
|
||||
)
|
||||
|
||||
// basic validation of request body
|
||||
if (!req.body.title || !req.body.author_id) {
|
||||
throw new Error("`title` and `author_id` are required.")
|
||||
}
|
||||
// basic validation of request body
|
||||
if (!req.body.title || !req.body.author_id) {
|
||||
throw new Error("`title` and `author_id` are required.")
|
||||
}
|
||||
|
||||
const post = await postService.create(req.body)
|
||||
const post = await postService.create(req.body)
|
||||
|
||||
res.json({
|
||||
post,
|
||||
})
|
||||
}
|
||||
```
|
||||
res.json({
|
||||
post,
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="posts-id-routes" label="src/api/admin/posts/[id]/route.ts">
|
||||
</TabItem>
|
||||
<TabItem value="posts-id-routes" label="src/api/admin/posts/[id]/route.ts">
|
||||
|
||||
```ts
|
||||
import type {
|
||||
MedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "@medusajs/medusa"
|
||||
import { PostService } from "../../../services/post"
|
||||
```ts
|
||||
import type {
|
||||
MedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "@medusajs/medusa"
|
||||
import { PostService } from "../../../services/post"
|
||||
|
||||
// retrieve a post by its ID
|
||||
export const GET = async (
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const postService: PostService = req.scope.resolve(
|
||||
"postService"
|
||||
)
|
||||
// retrieve a post by its ID
|
||||
export const GET = async (
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const postService: PostService = req.scope.resolve(
|
||||
"postService"
|
||||
)
|
||||
|
||||
const post = await postService.retrieve(req.params.id)
|
||||
const post = await postService.retrieve(req.params.id)
|
||||
|
||||
res.json({
|
||||
post,
|
||||
})
|
||||
}
|
||||
res.json({
|
||||
post,
|
||||
})
|
||||
}
|
||||
|
||||
// update a post by its ID
|
||||
export const POST = async (
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const postService: PostService = req.scope.resolve(
|
||||
"postService"
|
||||
)
|
||||
// update a post by its ID
|
||||
export const POST = async (
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const postService: PostService = req.scope.resolve(
|
||||
"postService"
|
||||
)
|
||||
|
||||
// basic validation of request body
|
||||
if (req.body.id) {
|
||||
throw new Error("Can't update post ID")
|
||||
}
|
||||
// basic validation of request body
|
||||
if (req.body.id) {
|
||||
throw new Error("Can't update post ID")
|
||||
}
|
||||
|
||||
const post = await postService.update(
|
||||
req.params.id,
|
||||
req.body
|
||||
)
|
||||
const post = await postService.update(
|
||||
req.params.id,
|
||||
req.body
|
||||
)
|
||||
|
||||
res.json({
|
||||
post,
|
||||
})
|
||||
}
|
||||
res.json({
|
||||
post,
|
||||
})
|
||||
}
|
||||
|
||||
// delete a post by its ID
|
||||
export const DELETE = async (
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const postService: PostService = req.scope.resolve(
|
||||
"postService"
|
||||
)
|
||||
// delete a post by its ID
|
||||
export const DELETE = async (
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const postService: PostService = req.scope.resolve(
|
||||
"postService"
|
||||
)
|
||||
|
||||
await postService.delete(req.params.id)
|
||||
await postService.delete(req.params.id)
|
||||
|
||||
res.status(200).end()
|
||||
}
|
||||
```
|
||||
res.status(200).end()
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
---
|
||||
|
||||
@@ -25,60 +25,48 @@ node -v
|
||||
:::
|
||||
|
||||
<Tabs groupId="operating-systems" queryString="os">
|
||||
<TabItem value="windows" label="Windows" default>
|
||||
<TabItem value="windows" label="Windows" default>
|
||||
You can install the executable directly from [the Node.js website](https://nodejs.org/en/#home-downloadhead).
|
||||
|
||||
You can install the executable directly from [the Node.js website](https://nodejs.org/en/#home-downloadhead).
|
||||
For other approaches, you can check out [Node.js’s guide](https://nodejs.org/en/download/package-manager/#windows-1).
|
||||
</TabItem>
|
||||
<TabItem value="linux" label="Linux">
|
||||
You can use the following commands to install Node.js on Ubuntu:
|
||||
|
||||
For other approaches, you can check out [Node.js’s guide](https://nodejs.org/en/download/package-manager/#windows-1).
|
||||
```bash
|
||||
#Ubuntu
|
||||
sudo apt update
|
||||
sudo apt install nodejs
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="linux" label="Linux">
|
||||
For other Linux distributions, you can check out [Node.js’s guide](https://nodejs.org/en/download/package-manager/).
|
||||
</TabItem>
|
||||
<TabItem value="macos" label="macOS">
|
||||
You can use the following commands to install Node.js on macOS:
|
||||
|
||||
You can use the following commands to install Node.js on Ubuntu:
|
||||
<Tabs groupId="homebrew" isCodeTabs={true}>
|
||||
<TabItem value="homebrew" label="Homebrew">
|
||||
```bash
|
||||
brew install node
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem value="no-homebrew" label="Without Homebrew">
|
||||
```bash
|
||||
curl \
|
||||
"https://nodejs.org/dist/latest/node-${VERSION:-$(wget -qO- \
|
||||
https://nodejs.org/dist/latest/ | sed -nE \
|
||||
's|.*>node-(.*)\.pkg</a>.*|\1|p')}.pkg" \
|
||||
> "$HOME/Downloads/node-latest.pkg" &&
|
||||
sudo installer -store -pkg "$HOME/Downloads/node-latest.pkg" -target "/"
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
```bash
|
||||
#Ubuntu
|
||||
sudo apt update
|
||||
sudo apt install nodejs
|
||||
```
|
||||
For other approaches, you can check out [Node.js’s guide](https://nodejs.org/en/download/package-manager/#macos).
|
||||
|
||||
For other Linux distributions, you can check out [Node.js’s guide](https://nodejs.org/en/download/package-manager/).
|
||||
Make sure that you have Xcode command line tools installed. If not, run the following command to install it: `xcode-select --install`.
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="macos" label="macOS">
|
||||
|
||||
You can use the following commands to install Node.js on macOS:
|
||||
|
||||
<Tabs groupId="homebrew" isCodeTabs={true}>
|
||||
<TabItem value="homebrew" label="Homebrew">
|
||||
|
||||
```bash
|
||||
brew install node
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="no-homebrew" label="Without Homebrew">
|
||||
|
||||
```bash
|
||||
curl \
|
||||
"https://nodejs.org/dist/latest/node-${VERSION:-$(wget -qO- \
|
||||
https://nodejs.org/dist/latest/ | sed -nE \
|
||||
's|.*>node-(.*)\.pkg</a>.*|\1|p')}.pkg" \
|
||||
> "$HOME/Downloads/node-latest.pkg" &&
|
||||
sudo installer -store -pkg "$HOME/Downloads/node-latest.pkg" -target "/"
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
For other approaches, you can check out [Node.js’s guide](https://nodejs.org/en/download/package-manager/#macos).
|
||||
|
||||
:::tip
|
||||
|
||||
Make sure that you have Xcode command line tools installed; if not, run the following command to install it: `xcode-select --install`
|
||||
|
||||
:::
|
||||
</TabItem>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Git
|
||||
@@ -86,35 +74,29 @@ Make sure that you have Xcode command line tools installed; if not, run the fol
|
||||
Medusa uses Git behind the scenes when you create a new project. So, you'll have to install it on your machine to get started.
|
||||
|
||||
<Tabs groupId="operating-systems" queryString="os">
|
||||
<TabItem value="windows" label="Windows" default>
|
||||
<TabItem value="windows" label="Windows" default>
|
||||
To install Git on Windows, you need to [download the installable package](https://git-scm.com/download/win).
|
||||
</TabItem>
|
||||
<TabItem value="linux" label="Linux">
|
||||
For Debian/Ubuntu, you can use the following command:
|
||||
|
||||
To install Git on Windows, you need to [download the installable package](https://git-scm.com/download/win).
|
||||
```bash
|
||||
apt-get install git
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="linux" label="Linux">
|
||||
As for other Linux distributions, please check [git’s guide](https://git-scm.com/download/linux).
|
||||
</TabItem>
|
||||
<TabItem value="macos" label="macOS">
|
||||
You should already have Git installed as part of the Xcode command-line tools.
|
||||
|
||||
For Debian/Ubuntu, you can use the following command:
|
||||
However, if for any reason you need to install it manually, you can install it with Homebrew:
|
||||
|
||||
```bash
|
||||
apt-get install git
|
||||
```
|
||||
```bash
|
||||
brew install git
|
||||
```
|
||||
|
||||
As for other Linux distributions, please check [git’s guide](https://git-scm.com/download/linux).
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="macos" label="macOS">
|
||||
|
||||
You should already have Git installed as part of the Xcode command-line tools.
|
||||
|
||||
However, if for any reason you need to install it manually, you can install it with Homebrew:
|
||||
|
||||
```bash
|
||||
brew install git
|
||||
```
|
||||
|
||||
You can also check out [git’s guide](https://git-scm.com/download/mac) for more installation options.
|
||||
|
||||
</TabItem>
|
||||
You can also check out [git’s guide](https://git-scm.com/download/mac) for more installation options.
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## PostgreSQL
|
||||
@@ -122,32 +104,26 @@ You can also check out [git’s guide](https://git-scm.com/download/mac) for mor
|
||||
The Medusa backend uses PostgreSQL to store data of your commerce system.
|
||||
|
||||
<Tabs groupId="operating-systems" queryString="os">
|
||||
<TabItem value="windows" label="Windows">
|
||||
<TabItem value="windows" label="Windows">
|
||||
You can [download the PostgreSQL Windows installer](https://www.postgresql.org/download/windows/) from their website.
|
||||
</TabItem>
|
||||
<TabItem value="linux" label="Linux">
|
||||
If you’re using Ubuntu, you can use the following commands to download and install PostgreSQL:
|
||||
|
||||
You can [download the PostgreSQL Windows installer](https://www.postgresql.org/download/windows/) from their website.
|
||||
```bash
|
||||
sudo sh -c \
|
||||
'echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list'
|
||||
wget --quiet -O - \
|
||||
https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install postgresql
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="linux" label="Linux">
|
||||
|
||||
If you’re using Ubuntu, you can use the following commands to download and install PostgreSQL:
|
||||
|
||||
```bash
|
||||
sudo sh -c \
|
||||
'echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list'
|
||||
wget --quiet -O - \
|
||||
https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install postgresql
|
||||
```
|
||||
|
||||
For other distributions, you can check out [PostgreSQL’s website for more guides](https://www.postgresql.org/download/linux/).
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="macos" label="macOS">
|
||||
|
||||
You can download PostgreSQL on your macOS using [the installer on their website](https://www.postgresql.org/download/macosx/).
|
||||
|
||||
</TabItem>
|
||||
For other distributions, you can check out [PostgreSQL’s website for more guides](https://www.postgresql.org/download/linux/).
|
||||
</TabItem>
|
||||
<TabItem value="macos" label="macOS">
|
||||
You can download PostgreSQL on your macOS using [the installer on their website](https://www.postgresql.org/download/macosx/).
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## (Optional) Medusa CLI
|
||||
|
||||
@@ -293,54 +293,54 @@ The first step is to create a batch job using the [Create Batch Job API Route](h
|
||||
For example, this creates a batch job of the type `publish-products`:
|
||||
|
||||
<Tabs groupId="request-types" isCodeTabs={true}>
|
||||
<TabItem value="client" label="Medusa JS Client" default>
|
||||
<TabItem value="client" label="Medusa JS Client" default>
|
||||
|
||||
```jsx
|
||||
medusa.admin.batchJobs.create({
|
||||
type: "publish-products",
|
||||
context: { },
|
||||
dry_run: true,
|
||||
})
|
||||
.then(( batch_job ) => {
|
||||
console.log(batch_job.status)
|
||||
})
|
||||
```
|
||||
```jsx
|
||||
medusa.admin.batchJobs.create({
|
||||
type: "publish-products",
|
||||
context: { },
|
||||
dry_run: true,
|
||||
})
|
||||
.then(( batch_job ) => {
|
||||
console.log(batch_job.status)
|
||||
})
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="fetch" label="Fetch API">
|
||||
</TabItem>
|
||||
<TabItem value="fetch" label="Fetch API">
|
||||
|
||||
```jsx
|
||||
fetch(`<BACKEND_URL>/admin/batch-jobs`, {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
type: "publish-products",
|
||||
context: { },
|
||||
dry_run: true,
|
||||
}),
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then(({ batch_job }) => {
|
||||
console.log(batch_job.status)
|
||||
})
|
||||
```
|
||||
```jsx
|
||||
fetch(`<BACKEND_URL>/admin/batch-jobs`, {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
type: "publish-products",
|
||||
context: { },
|
||||
dry_run: true,
|
||||
}),
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then(({ batch_job }) => {
|
||||
console.log(batch_job.status)
|
||||
})
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="cURL">
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="cURL">
|
||||
|
||||
```bash
|
||||
curl -L -X POST '<BACKEND_URL>/admin/batch-jobs' \
|
||||
-H 'Authorization: Bearer <API_TOKEN>' \
|
||||
-H 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
"type": "publish-products",
|
||||
"context": { },
|
||||
"dry_run": true
|
||||
}'
|
||||
```
|
||||
```bash
|
||||
curl -L -X POST '<BACKEND_URL>/admin/batch-jobs' \
|
||||
-H 'Authorization: Bearer <API_TOKEN>' \
|
||||
-H 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
"type": "publish-products",
|
||||
"context": { },
|
||||
"dry_run": true
|
||||
}'
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
@@ -354,38 +354,38 @@ Make sure to replace `<BACKEND_URL>` with the backend URL where applicable.
|
||||
You can retrieve the batch job afterward to get its status and view details about the process in the `result` property:
|
||||
|
||||
<Tabs groupId="request-type" isCodeTabs={true}>
|
||||
<TabItem value="client" label="Medusa JS Client" default>
|
||||
<TabItem value="client" label="Medusa JS Client" default>
|
||||
|
||||
```jsx
|
||||
medusa.admin.batchJobs.retrieve(batchJobId)
|
||||
.then(( batch_job ) => {
|
||||
console.log(batch_job.status, batch_job.result)
|
||||
})
|
||||
```
|
||||
```jsx
|
||||
medusa.admin.batchJobs.retrieve(batchJobId)
|
||||
.then(( batch_job ) => {
|
||||
console.log(batch_job.status, batch_job.result)
|
||||
})
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="fetch" label="Fetch API">
|
||||
</TabItem>
|
||||
<TabItem value="fetch" label="Fetch API">
|
||||
|
||||
```jsx
|
||||
fetch(`<BACKEND_URL>/admin/batch-jobs/${batchJobId}`, {
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then(({ batch_job }) => {
|
||||
console.log(batch_job.status, batch_job.result)
|
||||
})
|
||||
```
|
||||
```jsx
|
||||
fetch(`<BACKEND_URL>/admin/batch-jobs/${batchJobId}`, {
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then(({ batch_job }) => {
|
||||
console.log(batch_job.status, batch_job.result)
|
||||
})
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="cURL">
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="cURL">
|
||||
|
||||
```bash
|
||||
curl -L -X GET '<BACKEND_URL>/admin/batch-jobs/<BATCH_JOB_ID>' \
|
||||
-H 'Authorization: Bearer <API_TOKEN>'
|
||||
# <BATCH_JOB_ID> is the ID of the batch job
|
||||
```
|
||||
```bash
|
||||
curl -L -X GET '<BACKEND_URL>/admin/batch-jobs/<BATCH_JOB_ID>' \
|
||||
-H 'Authorization: Bearer <API_TOKEN>'
|
||||
# <BATCH_JOB_ID> is the ID of the batch job
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
Based on the batch job strategy implemented in this documentation, the `result` property could be something like this:
|
||||
@@ -409,39 +409,39 @@ Based on the batch job strategy implemented in this documentation, the `result`
|
||||
To process the batch job, send a request to [confirm the batch job](https://docs.medusajs.com/api/admin#batch-jobs_postbatchjobsbatchjobconfirmprocessing):
|
||||
|
||||
<Tabs groupId="request-type" isCodeTabs={true}>
|
||||
<TabItem value="client" label="Medusa JS Client" default>
|
||||
<TabItem value="client" label="Medusa JS Client" default>
|
||||
|
||||
```jsx
|
||||
medusa.admin.batchJobs.confirm(batchJobId)
|
||||
.then(( batch_job ) => {
|
||||
console.log(batch_job.status)
|
||||
})
|
||||
```
|
||||
```jsx
|
||||
medusa.admin.batchJobs.confirm(batchJobId)
|
||||
.then(( batch_job ) => {
|
||||
console.log(batch_job.status)
|
||||
})
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="fetch" label="Fetch API">
|
||||
</TabItem>
|
||||
<TabItem value="fetch" label="Fetch API">
|
||||
|
||||
```jsx
|
||||
fetch(`<BACKEND_URL>/admin/batch-jobs/${batchJobId}/confirm`, {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then(({ batch_job }) => {
|
||||
console.log(batch_job.status)
|
||||
})
|
||||
```
|
||||
```jsx
|
||||
fetch(`<BACKEND_URL>/admin/batch-jobs/${batchJobId}/confirm`, {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then(({ batch_job }) => {
|
||||
console.log(batch_job.status)
|
||||
})
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="cURL">
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="cURL">
|
||||
|
||||
```bash
|
||||
curl -L -X POST '<BACKEND_URL>/admin/batch-jobs/<BATCH_JOB_ID>/confirm' \
|
||||
-H 'Authorization: Bearer <API_TOKEN>'
|
||||
# <BATCH_JOB_ID> is the ID of the batch job
|
||||
```
|
||||
```bash
|
||||
curl -L -X POST '<BACKEND_URL>/admin/batch-jobs/<BATCH_JOB_ID>/confirm' \
|
||||
-H 'Authorization: Bearer <API_TOKEN>'
|
||||
# <BATCH_JOB_ID> is the ID of the batch job
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
The batch job will start processing afterward. Based on the batch job strategy implemented in this documentation, draft products will be published.
|
||||
|
||||
@@ -70,71 +70,71 @@ Your entity may be related to another entity. You can showcase the relation with
|
||||
For example, you can create another entity `Author` and add a `ManyToOne` relation to it from the `Post`, and a `OneToMany` relation from the `Author` to the `Post`:
|
||||
|
||||
<Tabs groupId="files" isCodeTabs={true}>
|
||||
<TabItem value="post" label="src/models/post.ts" default>
|
||||
<TabItem value="post" label="src/models/post.ts" default>
|
||||
|
||||
```ts
|
||||
import {
|
||||
BeforeInsert,
|
||||
Column,
|
||||
Entity,
|
||||
JoinColumn,
|
||||
ManyToOne,
|
||||
} from "typeorm"
|
||||
import { BaseEntity } from "@medusajs/medusa"
|
||||
import { generateEntityId } from "@medusajs/medusa/dist/utils"
|
||||
import { Author } from "./author"
|
||||
```ts
|
||||
import {
|
||||
BeforeInsert,
|
||||
Column,
|
||||
Entity,
|
||||
JoinColumn,
|
||||
ManyToOne,
|
||||
} from "typeorm"
|
||||
import { BaseEntity } from "@medusajs/medusa"
|
||||
import { generateEntityId } from "@medusajs/medusa/dist/utils"
|
||||
import { Author } from "./author"
|
||||
|
||||
@Entity()
|
||||
export class Post extends BaseEntity {
|
||||
@Column({ type: "varchar" })
|
||||
title: string | null
|
||||
@Entity()
|
||||
export class Post extends BaseEntity {
|
||||
@Column({ type: "varchar" })
|
||||
title: string | null
|
||||
|
||||
@Column({ type: "varchar" })
|
||||
author_id: string
|
||||
@Column({ type: "varchar" })
|
||||
author_id: string
|
||||
|
||||
@ManyToOne(() => Author, (author) => author.posts)
|
||||
@JoinColumn({ name: "author_id" })
|
||||
author: Author
|
||||
@ManyToOne(() => Author, (author) => author.posts)
|
||||
@JoinColumn({ name: "author_id" })
|
||||
author: Author
|
||||
|
||||
@BeforeInsert()
|
||||
private beforeInsert(): void {
|
||||
this.id = generateEntityId(this.id, "post")
|
||||
}
|
||||
}
|
||||
```
|
||||
@BeforeInsert()
|
||||
private beforeInsert(): void {
|
||||
this.id = generateEntityId(this.id, "post")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="author" label="src/models/author.ts">
|
||||
</TabItem>
|
||||
<TabItem value="author" label="src/models/author.ts">
|
||||
|
||||
```ts
|
||||
import { BaseEntity, generateEntityId } from "@medusajs/medusa"
|
||||
import {
|
||||
BeforeInsert,
|
||||
Column,
|
||||
Entity,
|
||||
OneToMany,
|
||||
} from "typeorm"
|
||||
import { Post } from "./post"
|
||||
```ts
|
||||
import { BaseEntity, generateEntityId } from "@medusajs/medusa"
|
||||
import {
|
||||
BeforeInsert,
|
||||
Column,
|
||||
Entity,
|
||||
OneToMany,
|
||||
} from "typeorm"
|
||||
import { Post } from "./post"
|
||||
|
||||
@Entity()
|
||||
export class Author extends BaseEntity {
|
||||
@Column({ type: "varchar" })
|
||||
name: string
|
||||
@Entity()
|
||||
export class Author extends BaseEntity {
|
||||
@Column({ type: "varchar" })
|
||||
name: string
|
||||
|
||||
@Column({ type: "varchar", nullable: true })
|
||||
image?: string
|
||||
@Column({ type: "varchar", nullable: true })
|
||||
image?: string
|
||||
|
||||
@OneToMany(() => Post, (post) => post.author)
|
||||
posts: Post[]
|
||||
@OneToMany(() => Post, (post) => post.author)
|
||||
posts: Post[]
|
||||
|
||||
@BeforeInsert()
|
||||
private beforeInsert(): void {
|
||||
this.id = generateEntityId(this.id, "auth")
|
||||
}
|
||||
}
|
||||
```
|
||||
@BeforeInsert()
|
||||
private beforeInsert(): void {
|
||||
this.id = generateEntityId(this.id, "auth")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
Adding these relations allows you to later on expand these relations when retrieving records of this entity with repositories.
|
||||
|
||||
@@ -417,24 +417,21 @@ While you develop your plugin, you’ll need to test it on an actual Medusa back
|
||||
|
||||
<Tabs groupId="plugin-preference">
|
||||
<TabItem value="without-admin" label="Without Admin Customizations" default>
|
||||
In the root of your plugin directory, run the `build` command:
|
||||
|
||||
In the root of your plugin directory, run the `build` command:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="with-admin" label="With Admin Customizations">
|
||||
In the root of your plugin directory, run the `prepare` command:
|
||||
|
||||
In the root of your plugin directory, run the `prepare` command:
|
||||
|
||||
```bash
|
||||
npm run prepare
|
||||
```
|
||||
|
||||
If the `prepare` script is not available in your project, you can find it in [this section](#changes-for-admin-plugins).
|
||||
```bash
|
||||
npm run prepare
|
||||
```
|
||||
|
||||
If the `prepare` script is not available in your project, you can find it in [this section](#changes-for-admin-plugins).
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
@@ -499,30 +496,30 @@ In the directory of the Medusa backend, start the backend with the `dev` command
|
||||
<Tabs groupId="npm2yarn" isCodeTabs={true}>
|
||||
<TabItem value="npm" label="npm" default>
|
||||
|
||||
```bash
|
||||
npm run dev -- -- --preserve-symlinks
|
||||
```
|
||||
```bash
|
||||
npm run dev -- -- --preserve-symlinks
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="yarn-1" label="Yarn v1">
|
||||
|
||||
```bash
|
||||
yarn dev -- -- --preserve-symlinks
|
||||
```
|
||||
```bash
|
||||
yarn dev -- -- --preserve-symlinks
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="yarn-berry" label="Yarn berry">
|
||||
|
||||
```bash
|
||||
yarn dev -- --preserve-symlinks
|
||||
```
|
||||
```bash
|
||||
yarn dev -- --preserve-symlinks
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="pnpm" label="pnpm">
|
||||
|
||||
```bash
|
||||
pnpm run dev -- -- --preserve-symlinks
|
||||
```
|
||||
```bash
|
||||
pnpm run dev -- -- --preserve-symlinks
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
@@ -45,40 +45,36 @@ Before publishing your plugin, make sure you've set the following fields in your
|
||||
|
||||
<Tabs groupId="plugin-preference">
|
||||
<TabItem value="without-admin" label="Without Admin Customizations" default>
|
||||
Make sure you add the `publish` script to your `scripts` field:
|
||||
|
||||
Make sure you add the `publish` script to your `scripts` field:
|
||||
```json title=package.json
|
||||
"scripts": {
|
||||
// other scripts...
|
||||
"build": "cross-env npm run clean && tsc -p tsconfig.json",
|
||||
"prepare": "cross-env NODE_ENV=production npm run build"
|
||||
}
|
||||
```
|
||||
|
||||
```json title=package.json
|
||||
"scripts": {
|
||||
// other scripts...
|
||||
"build": "cross-env npm run clean && tsc -p tsconfig.json",
|
||||
"prepare": "cross-env NODE_ENV=production npm run build"
|
||||
}
|
||||
```
|
||||
|
||||
The `build` script ensures that the plugin's built files are placed as explained in the [plugin structure](./create.mdx#plugin-structure) section of the Create Plugin documentation.
|
||||
|
||||
The `prepare` script facilitates your publishing process. You would typically run this script before publishing your plugin.
|
||||
The `build` script ensures that the plugin's built files are placed as explained in the [plugin structure](./create.mdx#plugin-structure) section of the Create Plugin documentation.
|
||||
|
||||
The `prepare` script facilitates your publishing process. You would typically run this script before publishing your plugin.
|
||||
</TabItem>
|
||||
<TabItem value="with-admin" label="With Admin Customizations">
|
||||
First, make sure to change `tsconfig` files as recommended in the [create guide](./create.mdx#changes-for-admin-plugins).
|
||||
|
||||
First, make sure to change `tsconfig` files as recommended in the [create guide](./create.mdx#changes-for-admin-plugins).
|
||||
Then, add the following `prepare` and `build` scripts to your `scripts`
|
||||
|
||||
Then, add the following `prepare` and `build` scripts to your `scripts`
|
||||
|
||||
```json title=package.json
|
||||
"scripts": {
|
||||
// other scripts...
|
||||
"build:server": "cross-env npm run clean && tsc -p tsconfig.json",
|
||||
"prepare": "cross-env NODE_ENV=production npm run build:server && medusa-admin bundle"
|
||||
}
|
||||
```
|
||||
|
||||
The `build:server` script builds the resources of the backend for development and ensures they are placed as explained in the [plugin structure](./create.mdx#plugin-structure) section of the Create Plugin documentation.
|
||||
|
||||
The `prepare` script creates a production build of both backend and admin resources.
|
||||
```json title=package.json
|
||||
"scripts": {
|
||||
// other scripts...
|
||||
"build:server": "cross-env npm run clean && tsc -p tsconfig.json",
|
||||
"prepare": "cross-env NODE_ENV=production npm run build:server && medusa-admin bundle"
|
||||
}
|
||||
```
|
||||
|
||||
The `build:server` script builds the resources of the backend for development and ensures they are placed as explained in the [plugin structure](./create.mdx#plugin-structure) section of the Create Plugin documentation.
|
||||
|
||||
The `prepare` script creates a production build of both backend and admin resources.
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -424,249 +424,249 @@ In this section, you'll find a full example of the `PostService` and `AuthorServ
|
||||
You can refer to the [Entities](../entities/create.mdx#adding-relations) documentation to learn how to create the custom entities used in this example.
|
||||
|
||||
<Tabs groupId="files" isCodeTabs={true}>
|
||||
<TabItem value="post" label="src/services/post.ts" default>
|
||||
<TabItem value="post" label="src/services/post.ts" default>
|
||||
|
||||
```ts
|
||||
import {
|
||||
FindConfig,
|
||||
Selector,
|
||||
TransactionBaseService,
|
||||
buildQuery,
|
||||
} from "@medusajs/medusa"
|
||||
import { PostRepository } from "../repositories/post"
|
||||
import { Post } from "../models/post"
|
||||
import { MedusaError } from "@medusajs/utils"
|
||||
```ts
|
||||
import {
|
||||
FindConfig,
|
||||
Selector,
|
||||
TransactionBaseService,
|
||||
buildQuery,
|
||||
} from "@medusajs/medusa"
|
||||
import { PostRepository } from "../repositories/post"
|
||||
import { Post } from "../models/post"
|
||||
import { MedusaError } from "@medusajs/utils"
|
||||
|
||||
class PostService extends TransactionBaseService {
|
||||
protected postRepository_: typeof PostRepository
|
||||
class PostService extends TransactionBaseService {
|
||||
protected postRepository_: typeof PostRepository
|
||||
|
||||
constructor(container) {
|
||||
super(container)
|
||||
this.postRepository_ = container.postRepository
|
||||
}
|
||||
constructor(container) {
|
||||
super(container)
|
||||
this.postRepository_ = container.postRepository
|
||||
}
|
||||
|
||||
async listAndCount(
|
||||
selector?: Selector<Post>,
|
||||
config: FindConfig<Post> = {
|
||||
skip: 0,
|
||||
take: 20,
|
||||
relations: [],
|
||||
}): Promise<[Post[], number]> {
|
||||
const postRepo = this.activeManager_.withRepository(
|
||||
this.postRepository_
|
||||
)
|
||||
async listAndCount(
|
||||
selector?: Selector<Post>,
|
||||
config: FindConfig<Post> = {
|
||||
skip: 0,
|
||||
take: 20,
|
||||
relations: [],
|
||||
}): Promise<[Post[], number]> {
|
||||
const postRepo = this.activeManager_.withRepository(
|
||||
this.postRepository_
|
||||
)
|
||||
|
||||
const query = buildQuery(selector, config)
|
||||
const query = buildQuery(selector, config)
|
||||
|
||||
return postRepo.findAndCount(query)
|
||||
}
|
||||
|
||||
async list(
|
||||
selector?: Selector<Post>,
|
||||
config: FindConfig<Post> = {
|
||||
skip: 0,
|
||||
take: 20,
|
||||
relations: [],
|
||||
}): Promise<Post[]> {
|
||||
const [posts] = await this.listAndCount(selector, config)
|
||||
return postRepo.findAndCount(query)
|
||||
}
|
||||
|
||||
async list(
|
||||
selector?: Selector<Post>,
|
||||
config: FindConfig<Post> = {
|
||||
skip: 0,
|
||||
take: 20,
|
||||
relations: [],
|
||||
}): Promise<Post[]> {
|
||||
const [posts] = await this.listAndCount(selector, config)
|
||||
|
||||
return posts
|
||||
}
|
||||
return posts
|
||||
}
|
||||
|
||||
async retrieve(
|
||||
id: string,
|
||||
config?: FindConfig<Post>
|
||||
): Promise<Post> {
|
||||
const postRepo = this.activeManager_.withRepository(
|
||||
this.postRepository_
|
||||
)
|
||||
async retrieve(
|
||||
id: string,
|
||||
config?: FindConfig<Post>
|
||||
): Promise<Post> {
|
||||
const postRepo = this.activeManager_.withRepository(
|
||||
this.postRepository_
|
||||
)
|
||||
|
||||
const query = buildQuery({
|
||||
id,
|
||||
}, config)
|
||||
const query = buildQuery({
|
||||
id,
|
||||
}, config)
|
||||
|
||||
const post = await postRepo.findOne(query)
|
||||
const post = await postRepo.findOne(query)
|
||||
|
||||
if (!post) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
"Post was not found"
|
||||
)
|
||||
if (!post) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
"Post was not found"
|
||||
)
|
||||
}
|
||||
|
||||
return post
|
||||
}
|
||||
|
||||
async create(
|
||||
data: Pick<Post, "title" | "author_id">
|
||||
): Promise<Post> {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const postRepo = manager.withRepository(
|
||||
this.postRepository_
|
||||
)
|
||||
const post = postRepo.create()
|
||||
post.title = data.title
|
||||
post.author_id = data.author_id
|
||||
const result = await postRepo.save(post)
|
||||
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
async update(
|
||||
id: string,
|
||||
data: Omit<Partial<Post>, "id">
|
||||
): Promise<Post> {
|
||||
return await this.atomicPhase_(async (manager) => {
|
||||
const postRepo = manager.withRepository(
|
||||
this.postRepository_
|
||||
)
|
||||
const post = await this.retrieve(id)
|
||||
|
||||
Object.assign(post, data)
|
||||
|
||||
return await postRepo.save(post)
|
||||
})
|
||||
}
|
||||
|
||||
async delete(id: string): Promise<void> {
|
||||
return await this.atomicPhase_(async (manager) => {
|
||||
const postRepo = manager.withRepository(
|
||||
this.postRepository_
|
||||
)
|
||||
const post = await this.retrieve(id)
|
||||
|
||||
await postRepo.remove([post])
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return post
|
||||
}
|
||||
export default PostService
|
||||
```
|
||||
|
||||
async create(
|
||||
data: Pick<Post, "title" | "author_id">
|
||||
): Promise<Post> {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const postRepo = manager.withRepository(
|
||||
this.postRepository_
|
||||
)
|
||||
const post = postRepo.create()
|
||||
post.title = data.title
|
||||
post.author_id = data.author_id
|
||||
const result = await postRepo.save(post)
|
||||
</TabItem>
|
||||
<TabItem value="author" label="src/services/author.ts">
|
||||
|
||||
return result
|
||||
})
|
||||
}
|
||||
```ts
|
||||
import {
|
||||
FindConfig,
|
||||
Selector,
|
||||
TransactionBaseService,
|
||||
buildQuery,
|
||||
} from "@medusajs/medusa"
|
||||
import { EntityManager } from "typeorm"
|
||||
import AuthorRepository from "../repositories/author"
|
||||
import { Author } from "../models/author"
|
||||
import { MedusaError } from "@medusajs/utils"
|
||||
|
||||
async update(
|
||||
id: string,
|
||||
data: Omit<Partial<Post>, "id">
|
||||
): Promise<Post> {
|
||||
return await this.atomicPhase_(async (manager) => {
|
||||
const postRepo = manager.withRepository(
|
||||
this.postRepository_
|
||||
)
|
||||
const post = await this.retrieve(id)
|
||||
class AuthorService extends TransactionBaseService {
|
||||
protected manager_: EntityManager
|
||||
protected transactionManager_: EntityManager
|
||||
protected authorRepository_: typeof AuthorRepository
|
||||
|
||||
Object.assign(post, data)
|
||||
constructor(container) {
|
||||
super(container)
|
||||
this.authorRepository_ = container.authorRepository
|
||||
}
|
||||
|
||||
return await postRepo.save(post)
|
||||
})
|
||||
}
|
||||
async listAndCount(
|
||||
selector?: Selector<Author>,
|
||||
config: FindConfig<Author> = {
|
||||
skip: 0,
|
||||
take: 20,
|
||||
relations: [],
|
||||
}): Promise<[Author[], number]> {
|
||||
const authorRepo = this.activeManager_.withRepository(
|
||||
this.authorRepository_
|
||||
)
|
||||
|
||||
async delete(id: string): Promise<void> {
|
||||
return await this.atomicPhase_(async (manager) => {
|
||||
const postRepo = manager.withRepository(
|
||||
this.postRepository_
|
||||
)
|
||||
const post = await this.retrieve(id)
|
||||
const query = buildQuery(selector, config)
|
||||
|
||||
return authorRepo.findAndCount(query)
|
||||
}
|
||||
|
||||
await postRepo.remove([post])
|
||||
})
|
||||
}
|
||||
}
|
||||
async list(
|
||||
selector?: Selector<Author>,
|
||||
config: FindConfig<Author> = {
|
||||
skip: 0,
|
||||
take: 20,
|
||||
relations: [],
|
||||
}): Promise<Author[]> {
|
||||
const [authors] = await this.listAndCount(selector, config)
|
||||
|
||||
export default PostService
|
||||
```
|
||||
return authors
|
||||
}
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="author" label="src/services/author.ts">
|
||||
async retrieve(
|
||||
id: string,
|
||||
config?: FindConfig<Author>
|
||||
): Promise<Author> {
|
||||
const authorRepo = this.activeManager_.withRepository(
|
||||
this.authorRepository_
|
||||
)
|
||||
|
||||
```ts
|
||||
import {
|
||||
FindConfig,
|
||||
Selector,
|
||||
TransactionBaseService,
|
||||
buildQuery,
|
||||
} from "@medusajs/medusa"
|
||||
import { EntityManager } from "typeorm"
|
||||
import AuthorRepository from "../repositories/author"
|
||||
import { Author } from "../models/author"
|
||||
import { MedusaError } from "@medusajs/utils"
|
||||
const query = buildQuery({
|
||||
id,
|
||||
}, config)
|
||||
|
||||
class AuthorService extends TransactionBaseService {
|
||||
protected manager_: EntityManager
|
||||
protected transactionManager_: EntityManager
|
||||
protected authorRepository_: typeof AuthorRepository
|
||||
const author = await authorRepo.findOne(query)
|
||||
|
||||
constructor(container) {
|
||||
super(container)
|
||||
this.authorRepository_ = container.authorRepository
|
||||
}
|
||||
if (!author) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
"Author was not found"
|
||||
)
|
||||
}
|
||||
|
||||
async listAndCount(
|
||||
selector?: Selector<Author>,
|
||||
config: FindConfig<Author> = {
|
||||
skip: 0,
|
||||
take: 20,
|
||||
relations: [],
|
||||
}): Promise<[Author[], number]> {
|
||||
const authorRepo = this.activeManager_.withRepository(
|
||||
this.authorRepository_
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
const query = buildQuery(selector, config)
|
||||
async create(
|
||||
data: Pick<Author, "name" | "image">
|
||||
): Promise<Author> {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const authorRepo = manager.withRepository(
|
||||
this.authorRepository_
|
||||
)
|
||||
const post = authorRepo.create(data)
|
||||
const result = await authorRepo.save(post)
|
||||
|
||||
return authorRepo.findAndCount(query)
|
||||
}
|
||||
|
||||
async list(
|
||||
selector?: Selector<Author>,
|
||||
config: FindConfig<Author> = {
|
||||
skip: 0,
|
||||
take: 20,
|
||||
relations: [],
|
||||
}): Promise<Author[]> {
|
||||
const [authors] = await this.listAndCount(selector, config)
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
return authors
|
||||
}
|
||||
async update(
|
||||
id: string,
|
||||
data: Omit<Partial<Author>, "id">
|
||||
): Promise<Author> {
|
||||
return await this.atomicPhase_(async (manager) => {
|
||||
const authorRepo = manager.withRepository(
|
||||
this.authorRepository_
|
||||
)
|
||||
const post = await this.retrieve(id)
|
||||
|
||||
Object.assign(post, data)
|
||||
|
||||
async retrieve(
|
||||
id: string,
|
||||
config?: FindConfig<Author>
|
||||
): Promise<Author> {
|
||||
const authorRepo = this.activeManager_.withRepository(
|
||||
this.authorRepository_
|
||||
)
|
||||
return await authorRepo.save(post)
|
||||
})
|
||||
}
|
||||
|
||||
const query = buildQuery({
|
||||
id,
|
||||
}, config)
|
||||
|
||||
const author = await authorRepo.findOne(query)
|
||||
|
||||
if (!author) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
"Author was not found"
|
||||
)
|
||||
async delete(id: string): Promise<void> {
|
||||
return await this.atomicPhase_(async (manager) => {
|
||||
const authorRepo = manager.withRepository(
|
||||
this.authorRepository_
|
||||
)
|
||||
const post = await this.retrieve(id)
|
||||
|
||||
await authorRepo.remove([post])
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
export default AuthorService
|
||||
```
|
||||
|
||||
async create(
|
||||
data: Pick<Author, "name" | "image">
|
||||
): Promise<Author> {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const authorRepo = manager.withRepository(
|
||||
this.authorRepository_
|
||||
)
|
||||
const post = authorRepo.create(data)
|
||||
const result = await authorRepo.save(post)
|
||||
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
async update(
|
||||
id: string,
|
||||
data: Omit<Partial<Author>, "id">
|
||||
): Promise<Author> {
|
||||
return await this.atomicPhase_(async (manager) => {
|
||||
const authorRepo = manager.withRepository(
|
||||
this.authorRepository_
|
||||
)
|
||||
const post = await this.retrieve(id)
|
||||
|
||||
Object.assign(post, data)
|
||||
|
||||
return await authorRepo.save(post)
|
||||
})
|
||||
}
|
||||
|
||||
async delete(id: string): Promise<void> {
|
||||
return await this.atomicPhase_(async (manager) => {
|
||||
const authorRepo = manager.withRepository(
|
||||
this.authorRepository_
|
||||
)
|
||||
const post = await this.retrieve(id)
|
||||
|
||||
await authorRepo.remove([post])
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default AuthorService
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
---
|
||||
|
||||
Reference in New Issue
Block a user