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:
Shahed Nasser
2023-11-13 20:11:50 +02:00
committed by GitHub
parent cedab58339
commit c6dff873de
2265 changed files with 46163 additions and 47195 deletions

View File

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

View File

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

View File

@@ -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.jss 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.jss 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.jss 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.jss guide](https://nodejs.org/en/download/package-manager/#macos).
For other Linux distributions, you can check out [Node.jss 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.jss 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 [gits 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 [gits 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 [gits guide](https://git-scm.com/download/mac) for more installation options.
</TabItem>
You can also check out [gits guide](https://git-scm.com/download/mac) for more installation options.
</TabItem>
</Tabs>
## PostgreSQL
@@ -122,32 +104,26 @@ You can also check out [gits 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 youre 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 youre 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 [PostgreSQLs 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 [PostgreSQLs 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

View File

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

View File

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

View File

@@ -417,24 +417,21 @@ While you develop your plugin, youll 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>

View File

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

View File

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