Files
medusa-store/www/apps/book/app/advanced-development/modules/example-crud-module-service/page.mdx
Shahed Nasser 4fe28f5a95 chore: reorganize docs apps (#7228)
* reorganize docs apps

* add README

* fix directory

* add condition for old docs
2024-05-03 17:36:38 +03:00

412 lines
9.7 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { Tabs, TabsList, TabsTrigger, TabsContent, TabsContentWrapper, Badge } from "docs-ui"
export const metadata = {
title: `${pageNumber} Example: CRUD Module Service`,
}
# {metadata.title}
In this chapter, youll find an example of how to implement CRUD operations in your modules service and test it out with an API route.
<Note>
The example in this chapter uses the same `hello` module with the `HelloModuleService` and `MyCustom` data model from previous chapters.
</Note>
## 1. Create Types File
Create the file `src/types/hello/my-custom.ts` with the following content:
```ts title="src/types/hello/my-custom.ts"
export type MyCustomDTO = {
id: string
name: string
}
export type CreateMyCustomDTO = {
name: string
}
export type UpdateMyCustomDTO = {
name?: string
}
```
Youll be using these types in the service.
---
## 2. Create HelloModuleService
Create or change the service at `src/modules/hello/service.ts` with the following content:
```ts title="src/modules/hello/service.ts"
import {
ModulesSdkUtils,
InjectTransactionManager,
MedusaContext,
} from "@medusajs/utils"
import {
DAL,
ModulesSdkTypes,
Context,
} from "@medusajs/types"
import { MyCustom } from "./models/my-custom"
import {
CreateMyCustomDTO,
MyCustomDTO,
UpdateMyCustomDTO,
} from "../../types/hello/my-custom"
type InjectedDependencies = {
baseRepository: DAL.RepositoryService
myCustomService: ModulesSdkTypes.InternalModuleService<any>
}
type AllModelsDTO = {
MyCustom: {
dto: MyCustomDTO
}
}
class HelloModuleService extends ModulesSdkUtils
.abstractModuleServiceFactory<
InjectedDependencies,
MyCustomDTO,
AllModelsDTO
>(MyCustom, []) {
protected baseRepository_: DAL.RepositoryService
protected myCustomService_: ModulesSdkTypes.InternalModuleService<MyCustom>
constructor(
{ baseRepository, myCustomService }: InjectedDependencies
) {
// @ts-ignore
super(...arguments)
this.baseRepository_ = baseRepository
this.myCustomService_ = myCustomService
}
@InjectTransactionManager("baseRepository_")
async create(
data: CreateMyCustomDTO,
@MedusaContext() context: Context = {}
): Promise<MyCustomDTO> {
const myCustom = await this.myCustomService_.create(
data,
context
)
return myCustom
}
@InjectTransactionManager("baseRepository_")
async update(
id: string,
data: UpdateMyCustomDTO,
@MedusaContext() context: Context = {}
): Promise<MyCustomDTO> {
const myCustom = await this.myCustomService_.update({
...data,
id,
})
return myCustom
}
}
export default HelloModuleService
```
This creates a `HelloModuleService` that uses the service factory to generate data-management methods like `list`, `retrieve`, and `delete`.
<Note>
Refer to the full list of generated methods in [this chapter](../service-factory/page.mdx#generated-methods).
</Note>
In the service, you also implement the `create` and `update` methods that create and update a `MyCustom` record respectively.
---
## 3. Configure Module Definition
Make sure that the module definition at `src/modules/hello/index.ts` uses the container and connection loaders explained in previous chapters:
```ts title="src/modules/hello/index.ts"
import HelloModuleService from "./service"
import { ModulesSdkUtils, MikroOrmBaseRepository } from "@medusajs/utils"
import { MyCustom } from "./models/my-custom"
const moduleName = "hello"
const pathToMigrations = __dirname + "/migrations"
const migrationScriptOptions = {
moduleName,
models: {
MyCustom,
},
pathToMigrations,
}
export const runMigrations = ModulesSdkUtils
.buildMigrationScript(
migrationScriptOptions
)
export const revertMigration = ModulesSdkUtils
.buildRevertMigrationScript(
migrationScriptOptions
)
const containerLoader = ModulesSdkUtils.moduleContainerLoaderFactory({
moduleModels: {
MyCustom,
},
moduleRepositories: {
BaseRepository: MikroOrmBaseRepository,
},
moduleServices: [HelloModuleService],
})
const connectionLoader = ModulesSdkUtils.mikroOrmConnectionLoaderFactory({
moduleName,
moduleModels: [MyCustom],
migrationsPath: pathToMigrations,
})
export default {
service: HelloModuleService,
runMigrations,
revertMigration,
loaders: [containerLoader, connectionLoader],
}
```
<Note>
If you don't have the `MyCustom` data model, refer to [this chapter](../../../basics/data-models/page.mdx).
</Note>
Also, make sure that the module is added in the `modules` object defined in `medusa-config.js`:
```js title="medusa-config.js"
const modules = {
helloModuleService: {
resolve: "./dist/modules/hello",
},
// ...
}
```
---
## 4. Add List and Create API Routes
Create the file `src/api/store/custom/route.ts` with the following content:
```ts title="src/api/store/custom/route.ts"
import { MedusaRequest, MedusaResponse } from "@medusajs/medusa"
import HelloModuleService from "../../../modules/hello/service"
import { CreateMyCustomDTO } from "../../../types/hello/my-custom"
export async function GET(
req: MedusaRequest,
res: MedusaResponse
): Promise<void> {
const helloModuleService: HelloModuleService =
req.scope.resolve(
"helloModuleService"
)
res.json({
my_customs: await helloModuleService.list(),
})
}
type CustomReq = CreateMyCustomDTO
export async function POST(
req: MedusaRequest<CustomReq>,
res: MedusaResponse
): Promise<void> {
const helloModuleService: HelloModuleService =
req.scope.resolve(
"helloModuleService"
)
// skipping validation for simplicity
const myCustom = await helloModuleService.create(req.body)
res.json({
my_custom: myCustom,
})
}
```
This adds two API routes:
1. A `GET` API route at `/store/custom` that retrieves a list of `MyCustom` records.
2. A `POST` API route at `/store/custom` that creates a `MyCustom` record.
---
## 5. Add Retrieve, Update, and Delete API Routes
Create the file `src/api/store/custom/[id]/route.ts` with the following content:
```ts title="src/api/store/custom/[id]/route.ts"
import { MedusaRequest, MedusaResponse } from "@medusajs/medusa"
import HelloModuleService from "../../../../modules/hello/service"
export async function GET(
req: MedusaRequest,
res: MedusaResponse
): Promise<void> {
const helloModuleService: HelloModuleService =
req.scope.resolve(
"helloModuleService"
)
const myCustom = await helloModuleService.retrieve(req.params.id)
res.json({
my_custom: myCustom,
})
}
export async function POST(
req: MedusaRequest,
res: MedusaResponse
): Promise<void> {
const helloModuleService: HelloModuleService =
req.scope.resolve(
"helloModuleService"
)
// skipping validation for simplicity
const myCustom = await helloModuleService.update(
req.params.id,
req.body
)
res.json({
my_custom: myCustom,
})
}
export async function DELETE(
req: MedusaRequest,
res: MedusaResponse
): Promise<void> {
const helloModuleService: HelloModuleService =
req.scope.resolve(
"helloModuleService"
)
await helloModuleService.delete(req.params.id)
res.status(200).json({
success: true,
})
}
```
This creates three API routes:
1. A `GET` API route at `/store/custom/[id]` that retrieves a `MyCustom` record by its ID.
2. A `POST` API route at `/store/custom/[id]` that updates a `MyCustom` record.
3. A `DELETE` API route at `/store/custom/[id]` that deletes a `MyCustom` record.
---
## 6. Test it Out
Start your Medusa application:
```bash npm2yarn
npm run dev
```
Then, test out each of the API routes you created.
<Tabs defaultValue="list-route" layoutType="vertical">
<TabsList>
<TabsTrigger value="list-route">
<Badge variant="green">GET</Badge> /store/custom
</TabsTrigger>
<TabsTrigger value="create-route">
<Badge variant="blue">POST</Badge> /store/custom
</TabsTrigger>
<TabsTrigger value="retrieve-route">
<Badge variant="green">GET</Badge> /store/custom/[id]
</TabsTrigger>
<TabsTrigger value="update-route">
<Badge variant="blue">POST</Badge> /store/custom/[id]
</TabsTrigger>
<TabsTrigger value="delete-route">
<Badge variant="red">DEL</Badge> /store/custom/[id]
</TabsTrigger>
</TabsList>
<TabsContentWrapper>
<TabsContent value="list-route">
```bash apiTesting testApiMethod="GET" testApiUrl="http://localhost:9000/store/custom"
curl http://localhost:9000/store/custom
```
</TabsContent>
<TabsContent value="create-route">
```bash apiTesting testApiMethod="POST" testApiUrl="http://localhost:9000/store/custom" testBodyParams={{ "name": "test" }}
curl -X POST http://localhost:9000/store/custom \
--header 'Content-Type: application/json' \
--data-raw '{
"name": "test"
}'
```
</TabsContent>
<TabsContent value="retrieve-route">
```bash apiTesting testApiMethod="GET" testApiUrl="http://localhost:9000/store/custom/{id}" testPathParams={{ "id": "mc_123" }}
curl http://localhost:9000/store/custom/mc_123
```
</TabsContent>
<TabsContent value="update-route">
```bash apiTesting testApiMethod="POST" testApiUrl="http://localhost:9000/store/custom/{id}" testPathParams={{ "id": "mc_123" }} testBodyParams={{ "name": "test" }}
curl -X POST http://localhost:9000/store/custom/mc_123 \
--header 'Content-Type: application/json' \
--data-raw '{
"name": "test"
}'
```
</TabsContent>
<TabsContent value="delete-route">
```bash apiTesting testApiMethod="DELETE" testApiUrl="http://localhost:9000/store/custom/{id}" testPathParams={{ "id": "mc_123" }}
curl -X DELETE http://localhost:9000/store/custom/mc_123
```
</TabsContent>
</TabsContentWrapper>
</Tabs>