docs: revamped endpoints, services, and entities (#4660)
* revamp create endpoint docs * docs: revamped endpoints, services, and entities * eslint fixes * fix metadata * fix missing closing tag * fixes to create migration doc * fixes to create endpoint doc * small fix in create service doc
This commit is contained in:
@@ -1,194 +0,0 @@
|
||||
---
|
||||
description: 'Learn how to create an entity in Medusa. This guide also explains how to create a repository and access and delete the entity.'
|
||||
addHowToData: true
|
||||
---
|
||||
|
||||
# How to Create an Entity
|
||||
|
||||
In this document, you’ll learn how you can create a custom [Entity](./overview.mdx).
|
||||
|
||||
## Step 1: Create the Entity
|
||||
|
||||
To create an entity, create a TypeScript file in `src/models`. For example, here’s a `Post` entity defined in the file `src/models/post.ts`:
|
||||
|
||||
```ts title=src/models/post.ts
|
||||
import {
|
||||
BeforeInsert,
|
||||
Column,
|
||||
Entity,
|
||||
PrimaryColumn,
|
||||
} from "typeorm"
|
||||
import { BaseEntity } from "@medusajs/medusa"
|
||||
import { generateEntityId } from "@medusajs/medusa/dist/utils"
|
||||
|
||||
@Entity()
|
||||
export class Post extends BaseEntity {
|
||||
@Column({ type: "varchar" })
|
||||
title: string | null
|
||||
|
||||
@BeforeInsert()
|
||||
private beforeInsert(): void {
|
||||
this.id = generateEntityId(this.id, "post")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This entity has one column `title` defined. However, since it extends `BaseEntity` it will also have the `id`, `created_at`, and `updated_at` columns.
|
||||
|
||||
Medusa’s core entities all have the following format for IDs: `<PREFIX>_<RANDOM>`. For example, an order might have the ID `order_01G35WVGY4D1JCA4TPGVXPGCQM`.
|
||||
|
||||
To generate an ID for your entity that matches the IDs generated for Medusa’s core entities, you should add a `BeforeInsert` event handler. Then, inside that handler use Medusa’s utility function `generateEntityId` to generate the ID. It accepts the ID as a first parameter and the prefix as a second parameter. The `Post` entity IDs will be of the format `post_<RANDOM>`.
|
||||
|
||||
If you want the entity to also be soft deletable then it should extend `SoftDeletableEntity` instead:
|
||||
|
||||
```ts
|
||||
import { SoftDeletableEntity } from "@medusajs/medusa"
|
||||
|
||||
@Entity()
|
||||
export class Post extends SoftDeletableEntity {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
You can learn more about what decorators and column types you can use in [Typeorm’s documentation](https://typeorm.io/entities).
|
||||
|
||||
---
|
||||
|
||||
## Step 2: Create a Migration
|
||||
|
||||
Additionally, you must create a migration for your entity. Migrations are used to update the database schema with new tables or changes to existing tables.
|
||||
|
||||
You can learn more about Migrations, how to create or generate them, and how to run them in the [Migration documentation](./migrations/create.md).
|
||||
|
||||
---
|
||||
|
||||
## Step 3: Create a Repository
|
||||
|
||||
Entities data can be easily accessed and modified using Typeorm [Repositories](https://typeorm.io/working-with-repository). To create a repository, create a file in `src/repositories`. For example, here’s a repository `PostRepository` created in `src/repositories/post.ts`:
|
||||
|
||||
```ts title=src/repositories/post.ts
|
||||
import { Post } from "../models/post"
|
||||
import {
|
||||
dataSource,
|
||||
} from "@medusajs/medusa/dist/loaders/database"
|
||||
|
||||
export const PostRepository = dataSource
|
||||
.getRepository(Post)
|
||||
|
||||
export default PostRepository
|
||||
```
|
||||
|
||||
The repository is created using the `getRepository` method of the data source exported from the core package in Medusa. This method accepts the entity as a parameter.
|
||||
|
||||
:::tip
|
||||
|
||||
A data source is Typeorm’s connection settings that allows you to connect to your database. You can learn more about it in [Typeorm’s documentation](https://typeorm.io/data-source).
|
||||
|
||||
:::
|
||||
|
||||
If you want to add methods to that repository or override Typeorm's Repository methods, you can do that using the `extend` method:
|
||||
|
||||
```ts title=src/repositories/post.ts
|
||||
import { Post } from "../models/post"
|
||||
import {
|
||||
dataSource,
|
||||
} from "@medusajs/medusa/dist/loaders/database"
|
||||
|
||||
export const PostRepository = dataSource
|
||||
.getRepository(Post)
|
||||
.extend({
|
||||
customFunction(): void {
|
||||
// TODO add custom implementation
|
||||
return
|
||||
},
|
||||
})
|
||||
|
||||
export default PostRepository
|
||||
```
|
||||
|
||||
You can learn about available Repository methods in [Typeorm's documentation](https://typeorm.io/repository-api).
|
||||
|
||||
---
|
||||
|
||||
## (Optional) Step 4: Run Migrations
|
||||
|
||||
Before you start using your entity, make sure to run the migrations that reflect the entity on your database schema. If you've already ran migrations, you can skip this step.
|
||||
|
||||
To run migrations, run the `build` command that transpiles your code:
|
||||
|
||||
```bash npm2yarn
|
||||
npm run build
|
||||
```
|
||||
|
||||
Then, run the `migration` command:
|
||||
|
||||
```bash
|
||||
npx medusa migrations run
|
||||
```
|
||||
|
||||
You should see that your migration have executed.
|
||||
|
||||
---
|
||||
|
||||
## Example: Use Entity in Service
|
||||
|
||||
You can access your custom entity data in the database in services or subscribers using the repository. For example, here’s a service that lists all posts:
|
||||
|
||||
```ts
|
||||
import { TransactionBaseService } from "@medusajs/medusa"
|
||||
import PostRepository from "../repositories/post"
|
||||
|
||||
class PostService extends TransactionBaseService {
|
||||
protected readonly postRepository_: typeof PostRepository
|
||||
|
||||
constructor({ postRepository, manager }) {
|
||||
super({ postRepository, manager })
|
||||
|
||||
this.postRepository_ = postRepository
|
||||
this.manager_ = manager
|
||||
}
|
||||
|
||||
async list() {
|
||||
const postRepo = this.manager_
|
||||
.withRepository(this.postRepository_)
|
||||
return await postRepo.find()
|
||||
}
|
||||
}
|
||||
|
||||
export default PostService
|
||||
```
|
||||
|
||||
In the constructor, you can use dependency injection to get access to instances of services and repositories. Here, you initialize class fields `postRepository` and `manager`. The `manager` is a [Typeorm Entity Manager](https://typeorm.io/working-with-entity-manager).
|
||||
|
||||
Then, in the method `list`, you can create an instance of the `PostRepository` using the `this.manager_.withRepository` method passing it `this.postRepository` as a parameter.
|
||||
|
||||
After getting an instance of the repository, you can then use [Typeorm’s Repository methods](https://typeorm.io/repository-api) to perform Create, Read, Update, and Delete (CRUD) operations on your entity. You can also use any custom methods that you defined in the Repository.
|
||||
|
||||
:::note
|
||||
|
||||
This same usage of repositories can be done in other resources such as subscribers or endpoints.
|
||||
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## Delete a Soft-Deletable Entity
|
||||
|
||||
To delete soft-deletable entities that extend the `SoftDeletableEntity` class, you can use the repository method `softDelete` method:
|
||||
|
||||
```ts
|
||||
await postRepository.softDelete(post.id)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Advanced Entity Definitions
|
||||
|
||||
With entities, you can create relationships, index keys, and more. As Medusa uses Typeorm, you can learn about using these functionalities through [Typeorm's documentation](https://typeorm.io/).
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [Extend Entity](./extend-entity.md)
|
||||
- [Create a Plugin](../plugins/create.mdx)
|
||||
182
docs/content/development/entities/create.mdx
Normal file
182
docs/content/development/entities/create.mdx
Normal file
@@ -0,0 +1,182 @@
|
||||
---
|
||||
description: 'Learn how to create an entity in Medusa. This guide also explains how to create a repository and access and delete the entity.'
|
||||
addHowToData: true
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
# How to Create an Entity
|
||||
|
||||
In this document, you’ll learn how you can create a custom [Entity](./overview.mdx).
|
||||
|
||||
## Step 1: Create the Entity
|
||||
|
||||
To create an entity, create a TypeScript file in `src/models`. For example, here’s a `Post` entity defined in the file `src/models/post.ts`:
|
||||
|
||||
```ts title=src/models/post.ts
|
||||
import {
|
||||
BeforeInsert,
|
||||
Column,
|
||||
Entity,
|
||||
PrimaryColumn,
|
||||
} from "typeorm"
|
||||
import { BaseEntity } from "@medusajs/medusa"
|
||||
import { generateEntityId } from "@medusajs/medusa/dist/utils"
|
||||
|
||||
@Entity()
|
||||
export class Post extends BaseEntity {
|
||||
@Column({ type: "varchar" })
|
||||
title: string | null
|
||||
|
||||
@BeforeInsert()
|
||||
private beforeInsert(): void {
|
||||
this.id = generateEntityId(this.id, "post")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This entity has one column `title` defined. However, since it extends `BaseEntity` it will also have the `id`, `created_at`, and `updated_at` columns.
|
||||
|
||||
Medusa’s core entities all have the following format for IDs: `<PREFIX>_<RANDOM>`. For example, an order might have the ID `order_01G35WVGY4D1JCA4TPGVXPGCQM`.
|
||||
|
||||
To generate an ID for your entity that matches the IDs generated for Medusa’s core entities, you should add a `BeforeInsert` event handler. Then, inside that handler use Medusa’s utility function `generateEntityId` to generate the ID. It accepts the ID as a first parameter and the prefix as a second parameter. The `Post` entity IDs will be of the format `post_<RANDOM>`.
|
||||
|
||||
You can learn more about what decorators and column types you can use in [Typeorm’s documentation](https://typeorm.io/entities).
|
||||
|
||||
### Soft-Deletable Entities
|
||||
|
||||
If you want the entity to also be soft deletable then it should extend `SoftDeletableEntity` instead:
|
||||
|
||||
```ts
|
||||
import { SoftDeletableEntity } from "@medusajs/medusa"
|
||||
|
||||
@Entity()
|
||||
export class Post extends SoftDeletableEntity {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### Adding Relations
|
||||
|
||||
Your entity may be related to another entity. You can showcase the relation with [Typeorm's relation decorators](https://typeorm.io/relations).
|
||||
|
||||
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>
|
||||
|
||||
```ts
|
||||
import {
|
||||
BeforeInsert,
|
||||
Column,
|
||||
Entity,
|
||||
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
|
||||
|
||||
@Column({ type: "varchar" })
|
||||
author_id: string
|
||||
|
||||
@ManyToOne(() => Author, (author) => author.posts)
|
||||
author: Author
|
||||
|
||||
@BeforeInsert()
|
||||
private beforeInsert(): void {
|
||||
this.id = generateEntityId(this.id, "post")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="author" label="src/services/author.ts">
|
||||
|
||||
```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
|
||||
|
||||
@Column({ type: "varchar", nullable: true })
|
||||
image?: string
|
||||
|
||||
@OneToMany(() => Post, (post) => post.author)
|
||||
posts: Post[]
|
||||
|
||||
@BeforeInsert()
|
||||
private beforeInsert(): void {
|
||||
this.id = generateEntityId(this.id, "auth")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
Adding these relations allows you to later on expand these relations when retrieving records of this entity with repositories.
|
||||
|
||||
---
|
||||
|
||||
## Step 2: Create a Migration
|
||||
|
||||
Additionally, you must create a migration for your entity. Migrations are used to update the database schema with new tables or changes to existing tables.
|
||||
|
||||
You can learn more about Migrations, how to create or generate them, and how to run them in the [Migration documentation](./migrations/create.md).
|
||||
|
||||
---
|
||||
|
||||
## Step 3: Create a Repository
|
||||
|
||||
An repository is required for every entity you create. They provide methods to access and modify an entity's records, and they're used by other resources such as services or endpoints to perform those functionalities.
|
||||
|
||||
To learn how to create a repository, refer to the [Repositories](./repositories.md) documentation.
|
||||
|
||||
---
|
||||
|
||||
## (Optional) Step 4: Run Migrations
|
||||
|
||||
Before you start using your entity, make sure to run the migrations that reflect the entity on your database schema. If you've already ran migrations, you can skip this step.
|
||||
|
||||
To run migrations, run the `build` command that transpiles your code:
|
||||
|
||||
```bash npm2yarn
|
||||
npm run build
|
||||
```
|
||||
|
||||
Then, run the `migration` command:
|
||||
|
||||
```bash
|
||||
npx medusa migrations run
|
||||
```
|
||||
|
||||
You should see that your migration have executed.
|
||||
|
||||
---
|
||||
|
||||
## Advanced Entity Definitions
|
||||
|
||||
With entities, you can create relationships, index keys, and more. As Medusa uses Typeorm, you can learn about using these functionalities through [Typeorm's documentation](https://typeorm.io/entities).
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [How to use repositories](./repositories.md)
|
||||
- [Extend an entity](./extend-entity.md)
|
||||
- [Create a plugin](../plugins/create.mdx)
|
||||
@@ -9,75 +9,117 @@ In this document, you’ll learn how to create a [Migration](./overview.mdx) usi
|
||||
|
||||
## Step 1: Create Migration File
|
||||
|
||||
To create a migration that makes changes to your Medusa schema, run the following command:
|
||||
There are two ways to create a migration file: create and write its content manually, or create and generate its content.
|
||||
|
||||
```bash
|
||||
npx typeorm migration:create src/migrations/UserChanged
|
||||
If you're creating a custom entity, then it's recommended to generate the migration file. However, if you're extending an entity from Medusa's core, then you should create and write the migration manually.
|
||||
|
||||
### Option 1: Generate Migration File
|
||||
|
||||
:::warning
|
||||
|
||||
Generating migration files for extended entities may cause unexpected errors. It's highly recommended to write them manually instead.
|
||||
|
||||
:::
|
||||
|
||||
Typeorm provides a `migration:generate` command that allows you to pass it a Typeorm [DataSource](https://typeorm.io/data-source). The `DataSource` includes database connection details, as well as the path to your custom entities.
|
||||
|
||||
Start by creating the file `datasource.js` in the root of your Medusa backend project with the following content:
|
||||
|
||||
```js
|
||||
const { DataSource } = require("typeorm")
|
||||
|
||||
const AppDataSource = new DataSource({
|
||||
type: "postgres",
|
||||
port: 5432,
|
||||
username: "<YOUR_DB_USERNAME>",
|
||||
password: "<YOUR_DB_PASSWORD>",
|
||||
database: "<YOUR_DB_NAME>",
|
||||
entities: [
|
||||
"dist/models/*.js",
|
||||
],
|
||||
migrations: [
|
||||
"dist/migrations/*.js",
|
||||
],
|
||||
})
|
||||
|
||||
module.exports = {
|
||||
datasource: AppDataSource,
|
||||
}
|
||||
```
|
||||
|
||||
This will create the migration file in the path you specify. You can use this without the need to install Typeorm's CLI tool. You can then go ahead and make changes to it as necessary.
|
||||
Make sure to replace `<YOUR_DB_USERNAME>`, `<YOUR_DB_PASSWORD>`, and `<YOUR_DB_NAME>` with the necessary values for your database connection.
|
||||
|
||||
The migration file must be inside the `src/migrations` directory. When you run the build command, it will be transpiled into the directory `dist/migrations`. The `migrations run` command can only pick up migrations under the `dist/migrations` directory on a Medusa backend. This applies to migrations created in a Medusa backend, and not in a Medusa plugin. For plugins, check out the [Plugin's Structure section](../../plugins/create.mdx).
|
||||
Then, after creating your entity, run the `build` command:
|
||||
|
||||
<details>
|
||||
<summary>Generating Migrations for Entities</summary>
|
||||
```bash npm2yarn
|
||||
npm run build
|
||||
```
|
||||
|
||||
You can alternatively use Typeorm's `generate` command to generate a Migration file from existing entity classes. As of v1.8, Medusa uses Typeorm v0.3.x. So, you have to create a [DataSource](https://typeorm.io/data-source) first before using the `migration:generate` command.
|
||||
Finally, run the following command to generate a migration for your custom entity:
|
||||
|
||||
For example, create the file `datasource.js` in the root of your Medusa server with the following content:
|
||||
```bash
|
||||
npx typeorm migration:generate -d datasource.js src/migrations/PostCreate
|
||||
```
|
||||
|
||||
```js
|
||||
const { DataSource } = require("typeorm")
|
||||
|
||||
const AppDataSource = new DataSource({
|
||||
type: "postgres",
|
||||
port: 5432,
|
||||
username: "<YOUR_DB_USERNAME>",
|
||||
password: "<YOUR_DB_PASSWORD>",
|
||||
database: "<YOUR_DB_NAME>",
|
||||
entities: [
|
||||
"dist/models/*.js",
|
||||
],
|
||||
migrations: [
|
||||
"dist/migrations/*.js",
|
||||
],
|
||||
})
|
||||
This will generate the migration file in the path you specify, where `PostCreate` is just an example of the name of the migration to create. The migration file must be inside the `src/migrations` directory. When you run the build command, it will be transpiled into the `dist/migrations` directory.
|
||||
|
||||
module.exports = {
|
||||
datasource: AppDataSource,
|
||||
The `migrations run` command can only pick up migrations under the `dist/migrations` directory on a Medusa backend. This applies to migrations created in a Medusa backend, and not in a Medusa plugin. For plugins, check out the [Plugin's Structure section](../../plugins/create.mdx).
|
||||
|
||||
You can now continue to [step 2](#step-2-build-files) of this guide.
|
||||
|
||||
### Option 2: Write Migration File
|
||||
|
||||
With this option, you'll use Typeorm's CLI tool to create the migration file, but you'll write the content yourself.
|
||||
|
||||
Run the following command in the root directory of your Medusa backend project:
|
||||
|
||||
```bash
|
||||
npx typeorm migration:create src/migrations/PostCreate
|
||||
```
|
||||
|
||||
This will create the migration file in the path you specify, where `PostCreate` is just an example of the name of the migration to create. The migration file must be inside the `src/migrations` directory. When you run the build command, it will be transpiled into the `dist/migrations` directory.
|
||||
|
||||
The `migrations run` command can only pick up migrations under the `dist/migrations` directory on a Medusa backend. This applies to migrations created in a Medusa backend, and not in a Medusa plugin. For plugins, check out the [Plugin's Structure section](../../plugins/create.mdx).
|
||||
|
||||
If you open the file, you'll find `up` and `down` methods. The `up` method is used to reflect the changes on the database. The `down` method is used to revert the changes, which will be executed if the `npx medusa migrations revert` command is used.
|
||||
|
||||
In each of the `up` and `down` methods, you can write the migration either with [SQL syntax](https://www.postgresql.org/docs/current/sql-syntax.html), or using the [migration API](https://typeorm.io/migrations#using-migration-api-to-write-migrations).
|
||||
|
||||
For example:
|
||||
|
||||
<!-- eslint-disable max-len -->
|
||||
|
||||
```ts
|
||||
import { MigrationInterface, QueryRunner } from "typeorm"
|
||||
|
||||
export class AddAuthorsAndPosts1690876698954 implements MigrationInterface {
|
||||
name = "AddAuthorsAndPosts1690876698954"
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`CREATE TABLE "post" ("id" character varying NOT NULL, "created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updated_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "title" character varying NOT NULL, "author_id" character varying NOT NULL, "authorId" character varying, CONSTRAINT "PK_be5fda3aac270b134ff9c21cdee" PRIMARY KEY ("id"))`)
|
||||
await queryRunner.query(`CREATE TABLE "author" ("id" character varying NOT NULL, "created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updated_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "name" character varying NOT NULL, "image" character varying, CONSTRAINT "PK_5a0e79799d372fe56f2f3fa6871" PRIMARY KEY ("id"))`)
|
||||
await queryRunner.query(`ALTER TABLE "post" ADD CONSTRAINT "FK_c6fb082a3114f35d0cc27c518e0" FOREIGN KEY ("authorId") REFERENCES "author"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`)
|
||||
}
|
||||
```
|
||||
|
||||
Make sure to replace `<YOUR_DB_USERNAME>`, `<YOUR_DB_PASSWORD>`, and `<YOUR_DB_NAME>` with the necessary values for your database connection.
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "post" DROP CONSTRAINT "FK_c6fb082a3114f35d0cc27c518e0"`)
|
||||
await queryRunner.query(`DROP TABLE "author"`)
|
||||
await queryRunner.query(`DROP TABLE "post"`)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Then, after creating your entity, run the `build` command:
|
||||
:::warning
|
||||
|
||||
```bash npm2yarn
|
||||
npm run build
|
||||
```
|
||||
If you're copying the code snippet above, make sure to not copy the class name or the `name` attribute in it. Your migration should keep its timestamp.
|
||||
|
||||
Finally, run the following command to generate a Migration for your new entity:
|
||||
|
||||
```bash
|
||||
npx typeorm migration:generate -d datasource.js src/migrations/PostCreate
|
||||
```
|
||||
|
||||
Where `PostCreate` is just an example of the name of the migration to generate. The migration will then be generated in `src/migrations/<TIMESTAMP>-PostCreate.ts`. You can then skip to [step 3](#step-3-build-files) of this guide.
|
||||
</details>
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## Step 2: Write Migration File
|
||||
## Step 2: Build Files
|
||||
|
||||
The migration file contains the necessary commands to create the database columns, foreign keys, and more.
|
||||
|
||||
You can learn more about writing the migration file in You can learn more about writing migrations in [Typeorm’s Documentation](https://typeorm.io/migrations#using-migration-api-to-write-migrations).
|
||||
|
||||
---
|
||||
|
||||
## Step 3: Build Files
|
||||
|
||||
Before you can run the migrations you need to run the build command to transpile the TypeScript files to JavaScript files:
|
||||
Before you can run the migrations, you need to run the build command to transpile the TypeScript files to JavaScript files:
|
||||
|
||||
```bash npm2yarn
|
||||
npm run build
|
||||
|
||||
@@ -15,7 +15,7 @@ Entities in medusa represent tables in the database as classes. An example of th
|
||||
|
||||
Aside from the entities in the Medusa core package, you can also create custom entities to use in your Medusa backend. Custom entities are TypeScript or JavaScript files located in the `src/models` directory of your Medusa backend. You then transpile these entities to be used during the backend's runtime using the `build` command, which moves them to the `dist/models` directory.
|
||||
|
||||
Entities are TypeScript files and they are based on [Typeorm’s Entities](https://typeorm.io/entities) and use Typeorm decorators.
|
||||
Entities are based on [Typeorm’s Entities](https://typeorm.io/entities) and use Typeorm decorators. Each entity also require a [repository](./repositories.md) to be created. A repository provides basic methods to access and manipulate the entity's data.
|
||||
|
||||
---
|
||||
|
||||
@@ -68,19 +68,11 @@ If you want to remove a property from the `metadata` object, you can pass the `m
|
||||
|
||||
---
|
||||
|
||||
## What are Repositories
|
||||
|
||||
Repositories provide generic helper methods for entities. For example, a `list` method to retrieve all entities with pagination, or `retrieve` to retrieve a single entity record.
|
||||
|
||||
Repostories are [Typeorm repositories](https://typeorm.io/working-with-repository), so you can refer to Typeorm's documentation on all available methods.
|
||||
|
||||
---
|
||||
|
||||
## Custom Development
|
||||
|
||||
Developers can create custom entities in the Medusa backend, a plugin, or in a module, then ensure it reflects in the database using a migration.
|
||||
|
||||
<DocCardList colSize={6} items={[
|
||||
<DocCardList colSize={4} items={[
|
||||
{
|
||||
type: 'link',
|
||||
href: '/development/entities/create',
|
||||
@@ -90,6 +82,15 @@ Developers can create custom entities in the Medusa backend, a plugin, or in a m
|
||||
description: 'Learn how to create an entity in Medusa.'
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'link',
|
||||
href: '/development/entities/repositories',
|
||||
label: 'Create a Repository',
|
||||
customProps: {
|
||||
icon: Icons['academic-cap-solid'],
|
||||
description: 'Learn how to use a repository in Medusa.'
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'link',
|
||||
href: '/development/entities/migrations/create',
|
||||
|
||||
308
docs/content/development/entities/repositories.md
Normal file
308
docs/content/development/entities/repositories.md
Normal file
@@ -0,0 +1,308 @@
|
||||
---
|
||||
description: "In this document, you'll learn what repositories are, how to use them within your Medusa backend, and what are some of their common methods."
|
||||
---
|
||||
|
||||
# Repositories
|
||||
|
||||
In this document, you'll learn what repositories are, how to use them within your Medusa backend, and what are some of their common methods.
|
||||
|
||||
## Overview
|
||||
|
||||
Repositories provide generic helper methods for entities. For example, a `find` method to retrieve all entities with pagination, or `findOne` to retrieve a single entity record.
|
||||
|
||||
Repostories are [Typeorm repositories](https://typeorm.io/working-with-repository), so you can refer to Typeorm's documentation on all available methods.
|
||||
|
||||
This guide provides some basic methods you'll need during your custom development with Medusa.
|
||||
|
||||
---
|
||||
|
||||
## Basic Implementation
|
||||
|
||||
Each entity you create needs a repository. A repository is created under the `src/repositories` directory of your Medusa backend project. The file name is the name of the repository without `Repository`.
|
||||
|
||||
For example, to create a repository for a `Post` entity, create the file `src/repositories/post.ts` with the following content:
|
||||
|
||||
```ts src=src/repositories/post.ts
|
||||
import { Post } from "../models/post"
|
||||
import {
|
||||
dataSource,
|
||||
} from "@medusajs/medusa/dist/loaders/database"
|
||||
|
||||
export const PostRepository = dataSource
|
||||
.getRepository(Post)
|
||||
|
||||
export default PostRepository
|
||||
```
|
||||
|
||||
The repository is created using the `getRepository` method of the data source exported from the core package in Medusa. This method accepts the entity as a parameter.
|
||||
|
||||
:::tip
|
||||
|
||||
A data source is Typeorm’s connection settings that allows you to connect to your database. You can learn more about it in [Typeorm’s documentation](https://typeorm.io/data-source).
|
||||
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## Customizing a Repository
|
||||
|
||||
If you want to add methods to the repository or override Typeorm's Repository methods, you can do that using the `extend` method:
|
||||
|
||||
```ts title=src/repositories/post.ts
|
||||
import { Post } from "../models/post"
|
||||
import {
|
||||
dataSource,
|
||||
} from "@medusajs/medusa/dist/loaders/database"
|
||||
|
||||
export const PostRepository = dataSource
|
||||
.getRepository(Post)
|
||||
.extend({
|
||||
customFunction(): void {
|
||||
// TODO add custom implementation
|
||||
return
|
||||
},
|
||||
})
|
||||
|
||||
export default PostRepository
|
||||
```
|
||||
|
||||
You can learn about available Repository methods in [Typeorm's documentation](https://typeorm.io/repository-api).
|
||||
|
||||
---
|
||||
|
||||
## Using Repositories in Other Resources
|
||||
|
||||
When you want to perform an action or use on an entity in one of your custom resources, such as an endpoint or a service, you need to use its repository.
|
||||
|
||||
### Endpoints
|
||||
|
||||
To access a repository within an endpoint, use the `req.scope.resolve` method. For example:
|
||||
|
||||
```ts
|
||||
import { PostRepository } from "../repositories/post"
|
||||
import { EntityManager } from "typeorm"
|
||||
|
||||
// ...
|
||||
|
||||
export default () => {
|
||||
// ...
|
||||
|
||||
storeRouter.get("/posts", async (req, res) => {
|
||||
const postRepository: typeof PostRepository =
|
||||
req.scope.resolve("postRepository")
|
||||
const manager: EntityManager = req.scope.resolve("manager")
|
||||
const postRepo = manager.withRepository(postRepository)
|
||||
|
||||
return res.json({
|
||||
posts: await postRepo.find(),
|
||||
})
|
||||
})
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
You can learn more about endpoints [here](../endpoints/overview.mdx).
|
||||
|
||||
### Services and Subscribers
|
||||
|
||||
As repositories are registered in the [dependency container](../fundamentals/dependency-injection.md#dependency-container-and-injection), they can be accessed through dependency injection in the constructor of a service or a subscriber.
|
||||
|
||||
For example:
|
||||
|
||||
```ts title=/src/services/post.ts
|
||||
import { PostRepository } from "../repositories/post"
|
||||
|
||||
class PostService extends TransactionBaseService {
|
||||
// ...
|
||||
protected postRepository_: typeof PostRepository
|
||||
|
||||
constructor(container) {
|
||||
super(container)
|
||||
// ...
|
||||
this.postRepository_ = container.postRepository
|
||||
}
|
||||
|
||||
async list(): Promise<Post[]> {
|
||||
const postRepo = this.activeManager_.withRepository(
|
||||
this.postRepository_
|
||||
)
|
||||
return await postRepo.find()
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
You can learn more about services [here](../services/overview.mdx).
|
||||
|
||||
### Other Resources
|
||||
|
||||
Resources that have access to the dependency container can access repositories just like any other resources. You can learn more about the dependency container and dependency injection in [this documentation](../fundamentals/dependency-injection.md).
|
||||
|
||||
---
|
||||
|
||||
## Common Methods
|
||||
|
||||
This section covers some common methods and use cases you'll use with repositories. You can refer to [Typeorm's documentation](https://typeorm.io/repository-api) for full details on available methods.
|
||||
|
||||
### Retrieving a List of Records
|
||||
|
||||
To retrieve a list of records of an entity, use the `find` method:
|
||||
|
||||
```ts
|
||||
const posts = await postRepository.find()
|
||||
```
|
||||
|
||||
You can also filter the retrieved items by passing an object of type [FindOption](https://typeorm.io/find-options) as a first parameter:
|
||||
|
||||
```ts
|
||||
const posts = await postRepository.find({
|
||||
where: {
|
||||
id: "1",
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
In addition, you can pass `skip` and `take` properties to the object for pagination purposes. `skip`'s value is a number that indicates how many items to skip before retrieving the results, and `take` indicates how many items to return:
|
||||
|
||||
```ts
|
||||
const posts = await postRepository.find({
|
||||
skip: 0,
|
||||
take: 20,
|
||||
})
|
||||
```
|
||||
|
||||
To expand relations and retrieve them as part of each item in the result, you can pass the `relations` property to the parameter object:
|
||||
|
||||
```ts
|
||||
const posts = await postRepository.find({
|
||||
relations: ["authors"],
|
||||
})
|
||||
```
|
||||
|
||||
Medusa provides a utility method `buildQuery` that allows you to easily format the object to pass to the `find` method. `buildQuery` accepts two parameters:
|
||||
|
||||
1. The first parameter is an object whose keys are the attributes of the entity, and their values are the value to filter by.
|
||||
2. The second parameter includes the options related to pagination (such as `skip` and `take`), the relations to expand, and fields to select in each returned item.
|
||||
|
||||
For example:
|
||||
|
||||
```ts
|
||||
import { buildQuery } from "@medusajs/medusa"
|
||||
|
||||
// ...
|
||||
const selector = {
|
||||
id: "1",
|
||||
}
|
||||
|
||||
const config = {
|
||||
skip: 0,
|
||||
take: 20,
|
||||
relations: ["authors"],
|
||||
select: ["title"],
|
||||
}
|
||||
|
||||
const query = buildQuery(selector, config)
|
||||
|
||||
const posts = await postRepository.find(query)
|
||||
```
|
||||
|
||||
### Retrieving a List of Records with Count
|
||||
|
||||
You can retrieve a list of records along with their count using the `findAndCount` method:
|
||||
|
||||
```ts
|
||||
const [posts, count] = await postRepository.findAndCount()
|
||||
```
|
||||
|
||||
This method also accepts the same options object as a parameter similar to the [find](#retrieving-a-list-of-records) method.
|
||||
|
||||
### Retrieving a Single Record
|
||||
|
||||
You can retrieve one record of an entity using the `findOne` method:
|
||||
|
||||
```ts
|
||||
const post = await postRepository.findOne({
|
||||
where: {
|
||||
id: "1",
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
If the record does not exist, `null` will be returned instead.
|
||||
|
||||
You can also pass the method an options object similar to the [find](#retrieving-a-list-of-records) method to expand relations or specify what fields to select.
|
||||
|
||||
### Create Record
|
||||
|
||||
To create a new record of an entity, use the `create` and `save` methods of the repository:
|
||||
|
||||
```ts
|
||||
const post = postRepository.create()
|
||||
post.title = data.title
|
||||
post.author_id = data.author_id
|
||||
const result = await postRepository.save(post)
|
||||
```
|
||||
|
||||
The `save` method is what actually persists the created record in the database.
|
||||
|
||||
### Update Record
|
||||
|
||||
To update a record of an entity, use the `save` method of the repository:
|
||||
|
||||
```ts
|
||||
// const data = {
|
||||
// title: ''
|
||||
// }
|
||||
|
||||
// const post = await postRepository.findOne({
|
||||
// where: {
|
||||
// id: '1'
|
||||
// }
|
||||
// })
|
||||
|
||||
Object.assign(post, data)
|
||||
|
||||
const updatedPost = await postRepository.save(post)
|
||||
```
|
||||
|
||||
### Delete a Record
|
||||
|
||||
To delete a record of an entity, use the `remove` method of the repository:
|
||||
|
||||
```ts
|
||||
const post = await postRepository.findOne({
|
||||
where: {
|
||||
id: "1",
|
||||
},
|
||||
})
|
||||
|
||||
await postRepository.remove([post])
|
||||
```
|
||||
|
||||
This method accepts an array of records to delete.
|
||||
|
||||
### Soft Delete a Record
|
||||
|
||||
If an entity extends the `SoftDeletableEntity` class, it can be soft deleted. This means that the entity won't be fully deleted from the database, but it can't be retrieved as a non-deleted entity would.
|
||||
|
||||
To soft-delete a record of an entity, use the `softRemove` method:
|
||||
|
||||
```ts
|
||||
const post = await postRepository.findOne({
|
||||
where: {
|
||||
id: "1",
|
||||
},
|
||||
})
|
||||
|
||||
await postRepository.softRemove([post])
|
||||
```
|
||||
|
||||
You can later retrieve that entity by passing the `withDeleted` option to methods like `find`, `findAndCount`, or `findOne`:
|
||||
|
||||
```ts
|
||||
const posts = await postRepository.find({
|
||||
withDeleted: true,
|
||||
})
|
||||
```
|
||||
Reference in New Issue
Block a user