docs: create docs workspace (#5174)
* docs: migrate ui docs to docs universe * created yarn workspace * added eslint and tsconfig configurations * fix eslint configurations * fixed eslint configurations * shared tailwind configurations * added shared ui package * added more shared components * migrating more components * made details components shared * move InlineCode component * moved InputText * moved Loading component * Moved Modal component * moved Select components * Moved Tooltip component * moved Search components * moved ColorMode provider * Moved Notification components and providers * used icons package * use UI colors in api-reference * moved Navbar component * used Navbar and Search in UI docs * added Feedback to UI docs * general enhancements * fix color mode * added copy colors file from ui-preset * added features and enhancements to UI docs * move Sidebar component and provider * general fixes and preparations for deployment * update docusaurus version * adjusted versions * fix output directory * remove rootDirectory property * fix yarn.lock * moved code component * added vale for all docs MD and MDX * fix tests * fix vale error * fix deployment errors * change ignore commands * add output directory * fix docs test * general fixes * content fixes * fix announcement script * added changeset * fix vale checks * added nofilter option * fix vale error
This commit is contained in:
@@ -0,0 +1,164 @@
|
||||
---
|
||||
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`:
|
||||
|
||||
:::note
|
||||
|
||||
Entities can only be placed in the top level of the `src/models` directory. So, you can't create an entity in a subfolder.
|
||||
|
||||
:::
|
||||
|
||||
```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,
|
||||
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
|
||||
|
||||
@Column({ type: "varchar" })
|
||||
author_id: string
|
||||
|
||||
@ManyToOne(() => Author, (author) => author.posts)
|
||||
@JoinColumn({ name: "author_id" })
|
||||
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 and Run Migrations
|
||||
|
||||
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).
|
||||
|
||||
Make sure to run the migrations before you start using the entity.
|
||||
|
||||
---
|
||||
|
||||
## 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)
|
||||
@@ -0,0 +1,177 @@
|
||||
---
|
||||
description: 'Learn how to extend a core entity in Medusa to add custom attributes.'
|
||||
addHowToData: true
|
||||
---
|
||||
|
||||
# How to Extend an Entity
|
||||
|
||||
In this document, you’ll learn how to extend a core entity in Medusa.
|
||||
|
||||
## Overview
|
||||
|
||||
Medusa uses entities to represent tables in the database. As you build your custom commerce application, you’ll often need to add your own properties to those entities. This guide explains the necessary steps to extend core Medusa entities.
|
||||
|
||||
This guide will use the Product entity as an example to demonstrate the steps.
|
||||
|
||||
### Word of Caution about Overriding
|
||||
|
||||
Extending entities to add new attributes or methods shouldn't cause any issues within your commerce application. However, if you extend them to override their existing methods or attributes, you should be aware that this could have negative implications, such as unanticipated bugs, especially when you try to upgrade the core Medusa package to a newer version.
|
||||
|
||||
---
|
||||
|
||||
## Step 1: Create Entity File
|
||||
|
||||
In your Medusa backend, create the file `src/models/product.ts`. This file will hold your extended entity.
|
||||
|
||||
Note that the name of the file must be the same as the name of the original entity in the core package. Since in this guide you’re overriding the Product entity, it’s named `product` to match the core. If you’re extending the customer entity, for example, the file should be named `customer.ts`.
|
||||
|
||||
---
|
||||
|
||||
## Step 2: Implement Extended Entity
|
||||
|
||||
In the file you created, you can import the entity you’re extending from the core package, then create a class that extends that entity. You can add in that class the new attributes and methods.
|
||||
|
||||
Here’s an example of extending the Product entity:
|
||||
|
||||
```ts title=src/models/product.ts
|
||||
import { Column, Entity } from "typeorm"
|
||||
import {
|
||||
// alias the core entity to not cause a naming conflict
|
||||
Product as MedusaProduct,
|
||||
} from "@medusajs/medusa"
|
||||
|
||||
@Entity()
|
||||
export class Product extends MedusaProduct {
|
||||
@Column()
|
||||
customAttribute: string
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## (Optional) Step 3: Create a TypeScript Declaration File
|
||||
|
||||
If you’re using JavaScript instead of TypeScript in your implementation, you can skip this step.
|
||||
|
||||
To ensure that TypeScript is aware of your extended entity and affects the typing of the Medusa package itself, create the file `src/index.d.ts` with the following content:
|
||||
|
||||
```ts title=src/index.d.ts
|
||||
export declare module "@medusajs/medusa/dist/models/product" {
|
||||
declare interface Product {
|
||||
customAttribute: string;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Notice that you must pass the attributes you added to the entity into the `interface`. The attributes will be merged with the attributes defined in the core `Product` entity.
|
||||
|
||||
---
|
||||
|
||||
## Step 4: Create Migration
|
||||
|
||||
To reflect your entity changes on the database schema, you must create a migration with those changes.
|
||||
|
||||
You can learn how to create or generate a migration in [this documentation](./migrations/create.md).
|
||||
|
||||
Here’s an example of a migration of the entity extended in this guide:
|
||||
|
||||
```ts title=src/migration/1680013376180-changeProduct.ts
|
||||
import { MigrationInterface, QueryRunner } from "typeorm"
|
||||
|
||||
class changeProduct1680013376180 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
"ALTER TABLE \"product\"" +
|
||||
" ADD COLUMN \"customAttribute\" text"
|
||||
)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
"ALTER TABLE \"product\" DROP COLUMN \"customAttribute\""
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default changeProduct1680013376180
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 5: Use Custom Entity
|
||||
|
||||
For changes to take effect, you must transpile your code by running the `build` command in the root of the Medusa backend:
|
||||
|
||||
```bash npm2yarn
|
||||
npm run build
|
||||
```
|
||||
|
||||
Then, run the following command to migrate your changes to the database:
|
||||
|
||||
```bash
|
||||
npx medusa migrations run
|
||||
```
|
||||
|
||||
You should see that your migration was executed, which means your changes were reflected in the database schema.
|
||||
|
||||
You can now use your extended entity throughout your commerce application.
|
||||
|
||||
---
|
||||
|
||||
## Access Custom Attributes and Relations in Core Endpoints
|
||||
|
||||
### Request Parameters
|
||||
|
||||
In most cases, after you extend an entity to add new attributes, you'll likely need to pass these attributes to endpoints defined in the core. By default, this causes an error, as request parameters are validated to ensure only those that are defined are passed to the endpoint.
|
||||
|
||||
To allow passing your custom attribute, you'll need to [extend the validator](../endpoints/extend-validator.md) of the endpoint.
|
||||
|
||||
### Response Fields
|
||||
|
||||
After you add custom attributes, you'll notice that these attributes aren't returned as part of the response fields of core endpoints. Core endpoints have a defined set of fields and relations that can be returned by default in requests.
|
||||
|
||||
To change that and ensure your custom attribute is returned in your request, you can extend the allowed fields of a set of endpoints in a loader file and add your attribute into them.
|
||||
|
||||
For example, if you added a custom attribute in the `Product` entity and you want to ensure it's returned in all the product's store endpoints (endpoints under the prefix `/store/products`), you can create a file under the `src/loaders` directory in your Medusa backend with the following content:
|
||||
|
||||
```ts title=src/loaders/extend-product-fields.ts
|
||||
export default async function () {
|
||||
const imports = (await import(
|
||||
"@medusajs/medusa/dist/api/routes/store/products/index"
|
||||
)) as any
|
||||
imports.allowedStoreProductsFields = [
|
||||
...imports.allowedStoreProductsFields,
|
||||
"customAttribute",
|
||||
]
|
||||
imports.defaultStoreProductsFields = [
|
||||
...imports.defaultStoreProductsFields,
|
||||
"customAttribute",
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
In the code snippet above, you import `@medusajs/medusa/dist/api/routes/store/products/index`, which is where all the product's store endpoints are exported. In that file, there are the following defined variables:
|
||||
|
||||
- `allowedStoreProductsFields`: The fields or attributes of a product that are allowed to be retrieved and returned in the product's store endpoints. This would allow you to pass your custom attribute in the `fields` request parameter of the product's store endpoints.
|
||||
- `defaultStoreProductsFields`: The fields or attributes of a product that are retrieved and returned by default in the product's store endpoints.
|
||||
|
||||
You change the values of these variables and pass the name of your custom attribute. Make sure to change `customAttribute` to the name of your custom attribute.
|
||||
|
||||
:::tip
|
||||
|
||||
Before you test out the above change, make sure to build your changes before you start the Medusa backend.
|
||||
|
||||
:::
|
||||
|
||||
You can also add custom relations by changing the following defined variables:
|
||||
|
||||
- `allowedStoreProductsRelations`: The relations of a product that are allowed to be retrieved and returned in the product's store endpoints. This would allow you to pass your custom relation in the `expand` request parameter of the product's store endpoints.
|
||||
- `defaultStoreProductsRelations`: The relations of a product that are retrieved and returned by default in the product's store endpoints.
|
||||
|
||||
If you want to apply this example for a different entity or set of endpoints, you would need to change the import path `@medusajs/medusa/dist/api/routes/store/products/index` to the path of the endpoints you're targeting. You also need to change `allowedStoreProductsFields` and `defaultStoreProductsFields` to the names of the variables in that file, and the same goes for relations. Typically, these names would be of the format `(allowed|default)(Store|Admin)(Entity)(Fields|Relation)`.
|
||||
|
||||
---
|
||||
|
||||
## 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/).
|
||||
@@ -0,0 +1,131 @@
|
||||
---
|
||||
description: 'Learn how to extend a core repository in Medusa to add custom methods.'
|
||||
addHowToData: true
|
||||
---
|
||||
|
||||
# How to Extend a Repository
|
||||
|
||||
In this document, you’ll learn how to extend a core repository in Medusa.
|
||||
|
||||
## Overview
|
||||
|
||||
Medusa uses Typeorm’s Repositories to perform operations on an entity, such as retrieve or update the entity. Typeorm already provides these basic functionalities within a repository, but sometimes you need to implement a custom implementation to handle the logic behind these operations differently. You might also want to add custom methods related to processing entities that aren’t available in the default repositories.
|
||||
|
||||
In this guide, you’ll learn how to extend a repository in the core Medusa package. This guide will use the Product repository as an example to demonstrate the steps.
|
||||
|
||||
### Word of Caution about Overriding
|
||||
|
||||
Extending repositories to add new methods shouldn't cause any issues within your commerce application. However, if you extend them to override their existing methods, you should be aware that this could have negative implications, such as unanticipated bugs, especially when you try to upgrade the core Medusa package to a newer version.
|
||||
|
||||
---
|
||||
|
||||
## Step 1: Create Repository File
|
||||
|
||||
In your Medusa backend, create the file `src/repositories/product.ts`. This file will hold your extended repository.
|
||||
|
||||
Note that the name of the file must be the same as the name of the original repository in the core package. Since in this guide you’re extending the Product repository, it’s named `product` to match the core. If you’re extending the customer repository, for example, the file should be named `customer.ts`.
|
||||
|
||||
---
|
||||
|
||||
## Step 2: Implement Extended Repository
|
||||
|
||||
In the file you created, you must retrieve both the repository you're extending along with its entity from the core. You’ll then use the data source exported from the core package to extend the repository.
|
||||
|
||||
:::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).
|
||||
|
||||
:::
|
||||
|
||||
Here’s an example of the implementation of the extended Product repository:
|
||||
|
||||
```ts title=src/repositories/product.ts
|
||||
import { Product } from "@medusajs/medusa"
|
||||
import {
|
||||
dataSource,
|
||||
} from "@medusajs/medusa/dist/loaders/database"
|
||||
import {
|
||||
// alias the core repository to not cause a naming conflict
|
||||
ProductRepository as MedusaProductRepository,
|
||||
} from "@medusajs/medusa/dist/repositories/product"
|
||||
|
||||
export const ProductRepository = dataSource
|
||||
.getRepository(Product)
|
||||
.extend({
|
||||
// it is important to spread the existing repository here.
|
||||
// Otherwise you will end up losing core properties
|
||||
...MedusaProductRepository,
|
||||
|
||||
/**
|
||||
* Here you can create your custom function
|
||||
* For example
|
||||
*/
|
||||
customFunction(): void {
|
||||
// TODO add custom implementation
|
||||
return
|
||||
},
|
||||
})
|
||||
|
||||
export default ProductRepository
|
||||
```
|
||||
|
||||
You first import all necessary resources from the core package: the `Product` entity, the `dataSource` instance, and the core’s `ProductRepository` aliased as `MedusaProductRepository` to avoid naming conflict.
|
||||
|
||||
You then use the `dataSource` instance to retrieve the `Product` entity’s repository and extend it using the repository’s `extend` method. This method is available as part of Typeorm Repository API. This method returns your extended repository.
|
||||
|
||||
The `extend` method accepts an object with all the methods to add to the extended repository.
|
||||
|
||||
You must first add the properties of the repository you’re extending, which in this case is the product repository (aliased as `MedusaProductRepository`). This will ensure you don’t lose core methods, which can lead to the core not working as expected. You add use the spread operator (`…`) with the `MedusaProductRepository` to spread its properties.
|
||||
|
||||
After that, you can add your custom methods to the repository. In the example above, you add the method `customFunction`. You can use any name for your methods.
|
||||
|
||||
---
|
||||
|
||||
## Step 3: Use Your Extended Repository
|
||||
|
||||
You can now use your extended repository in other resources such as services or endpoints.
|
||||
|
||||
Here’s an example of using it in an endpoint:
|
||||
|
||||
```ts
|
||||
import ProductRepository from "./path/to/product.ts"
|
||||
import EntityManager from "@medusajs/medusa"
|
||||
|
||||
export default () => {
|
||||
// ...
|
||||
|
||||
router.get("/custom-endpoint", (req, res) => {
|
||||
// ...
|
||||
|
||||
const productRepository: typeof ProductRepository =
|
||||
req.scope.resolve(
|
||||
"productRepository"
|
||||
)
|
||||
const manager: EntityManager = req.scope.resolve("manager")
|
||||
const productRepo = manager.withRepository(
|
||||
productRepository
|
||||
)
|
||||
productRepo.customFunction()
|
||||
|
||||
// ...
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 4: Test Your Implementation
|
||||
|
||||
For changes to take effect, you must transpile your code by running the `build` command in the root of the Medusa backend:
|
||||
|
||||
```bash npm2yarn
|
||||
npm run build
|
||||
```
|
||||
|
||||
Then, run the following command to start your backend:
|
||||
|
||||
```bash npm2yarn
|
||||
npx medusa develop
|
||||
```
|
||||
|
||||
You should see your custom implementation working as expected.
|
||||
@@ -0,0 +1,144 @@
|
||||
---
|
||||
description: 'Learn how to create a migration in Medusa. This guide explains how to write and run migrations.'
|
||||
addHowToData: true
|
||||
---
|
||||
|
||||
# How to Create Migrations
|
||||
|
||||
In this document, you’ll learn how to create a [Migration](./overview.mdx) using [Typeorm](https://typeorm.io) in Medusa.
|
||||
|
||||
## Step 1: Create Migration File
|
||||
|
||||
There are two ways to create a migration file: create and write its content manually, or create and generate its content.
|
||||
|
||||
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,
|
||||
}
|
||||
```
|
||||
|
||||
Make sure to replace `<YOUR_DB_USERNAME>`, `<YOUR_DB_PASSWORD>`, and `<YOUR_DB_NAME>` with the necessary values for your database connection.
|
||||
|
||||
Then, after creating your entity, run the `build` command:
|
||||
|
||||
```bash npm2yarn
|
||||
npm run build
|
||||
```
|
||||
|
||||
Finally, run the following command to generate a migration for your custom entity:
|
||||
|
||||
```bash
|
||||
npx typeorm migration:generate -d datasource.js src/migrations/PostCreate
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
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`)
|
||||
}
|
||||
|
||||
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"`)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
:::warning
|
||||
|
||||
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.
|
||||
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## Step 2: Build 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
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 3: Run Migration
|
||||
|
||||
The last step is to run the migration with the command detailed earlier
|
||||
|
||||
```bash
|
||||
npx medusa migrations run
|
||||
```
|
||||
|
||||
If you check your database now you should see that the change defined by the migration has been applied successfully.
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [Create a Plugin](../../plugins/create.mdx)
|
||||
@@ -0,0 +1,89 @@
|
||||
---
|
||||
description: 'Learn what Migrations are in Medusa and how to run them. Migrations are used to make changes to the database schema that Medusa is linked to.'
|
||||
---
|
||||
|
||||
import DocCardList from '@theme/DocCardList';
|
||||
import Icons from '@theme/Icon';
|
||||
|
||||
# Migrations
|
||||
|
||||
In this document, you'll learn what Migrations are in Medusa.
|
||||
|
||||
## What are Migrations
|
||||
|
||||
Migrations are scripts that are used to make additions or changes to your database schema. In Medusa, they are essential for both when you first install your backend and for subsequent backend upgrades later on.
|
||||
|
||||
When you first create your Medusa backend, the database schema used must have all the tables necessary for the backend to run.
|
||||
|
||||
When a new Medusa version introduces changes to the database schema, you'll have to run migrations to apply them to your own database.
|
||||
|
||||
:::tip
|
||||
|
||||
Migrations are used to apply changes to the database schema. However, there are some version updates of Medusa that require updating the data in your database to fit the new schema. Those are specific to each version and you should check out the version under Upgrade Guides for details on the steps.
|
||||
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## Migration Commands
|
||||
|
||||
### Migrate Command
|
||||
|
||||
Using the Medusa CLI tool, you can run migrations with the following command:
|
||||
|
||||
```bash
|
||||
npx medusa migrations run
|
||||
```
|
||||
|
||||
This will check for any migrations that contain changes to your database schema that aren't applied yet and run them on your backend.
|
||||
|
||||
### Seed Command
|
||||
|
||||
Seeding is the process of filling your database with data that is either essential or for testing and demo purposes. In Medusa, the `seed` command will run the migrations to your database if necessary before it seeds your database with dummy data.
|
||||
|
||||
You can use the following command to seed your database:
|
||||
|
||||
```bash
|
||||
npx @medusajs/medusa-cli@latest seed -f ./data/seed.json
|
||||
```
|
||||
|
||||
This assumes that you have the file `data/seed.json` in your Medusa backend, which is available by default. It includes the demo data to seed into your database.
|
||||
|
||||
### Revert Migrations
|
||||
|
||||
To revert the last migration you ran, run the following command:
|
||||
|
||||
```bash
|
||||
npx @medusajs/medusa-cli@latest migrations revert
|
||||
```
|
||||
|
||||
### Other Migrations Commands
|
||||
|
||||
You can learn about migration commands available in the Medusa CLI tool by referring to the [Medusa CLI reference](../../../cli/reference.mdx#migrations)
|
||||
|
||||
---
|
||||
|
||||
## 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={[
|
||||
{
|
||||
type: 'link',
|
||||
href: '/development/entities/migrations/create',
|
||||
label: 'Create a Migration',
|
||||
customProps: {
|
||||
icon: Icons['academic-cap-solid'],
|
||||
description: 'Learn how to create migrations in Medusa.'
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'link',
|
||||
href: '/development/entities/create',
|
||||
label: 'Create an Endpoint',
|
||||
customProps: {
|
||||
icon: Icons['academic-cap-solid'],
|
||||
description: 'Learn how to create endpoints in Medusa.'
|
||||
}
|
||||
},
|
||||
]} />
|
||||
@@ -0,0 +1,127 @@
|
||||
---
|
||||
description: 'Learn what entities are in Medusa. There are entities defined in the Medusa backend, and developers can create custom entities.'
|
||||
---
|
||||
|
||||
import DocCardList from '@theme/DocCardList';
|
||||
import Icons from '@theme/Icon';
|
||||
import LearningPath from '@site/src/components/LearningPath';
|
||||
|
||||
# Entities
|
||||
|
||||
In this document, you'll learn what Entities are in Medusa.
|
||||
|
||||
## What are Entities
|
||||
|
||||
Entities in medusa represent tables in the database as classes. An example of this would be the `Order` entity which represents the `order` table in the database. Entities provide a uniform way of defining and interacting with data retrieved from the database.
|
||||
|
||||
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 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.
|
||||
|
||||
<LearningPath pathName="entity-and-api" />
|
||||
|
||||
---
|
||||
|
||||
## Base Entities
|
||||
|
||||
All entities must extend either the `BaseEntity` or `SoftDeletableEntity` classes. The `BaseEntity` class holds common columns including the `id`, `created_at`, and `updated_at` columns.
|
||||
|
||||
The `SoftDeletableEntity` class extends the `BaseEntity` class and adds another column `deleted_at`. If an entity can be soft deleted, meaning that a row in it can appear to the user as deleted but still be available in the database, it should extend `SoftDeletableEntity`.
|
||||
|
||||
---
|
||||
|
||||
## metadata Attribute
|
||||
|
||||
Most entities in Medusa have a `metadata` attribute. This attribute is an object that can be used to store custom data related to that entity. In the database, this attribute is stored as a [JSON Binary (JSONB)](https://www.postgresql.org/docs/current/datatype-json.html#JSON-CONTAINMENT) column. On retrieval, the attribute is parsed into an object.
|
||||
|
||||
Some example use cases for the `metadata` attribute include:
|
||||
|
||||
- Store an external ID of an entity related to a third-party integration.
|
||||
- Store product customization such as personalization options.
|
||||
|
||||
### Add and Update Metadata
|
||||
|
||||
You can add or update metadata entities either through the REST APIs or through create and update methods in the entity's respective [service](../services/overview.mdx).
|
||||
|
||||
In the [admin REST APIs](https://docs.medusajs.com/api/admin), you'll find that in create or update requests of some entities you can also set the `metadata`.
|
||||
|
||||
In services, there are typically `create` or `update` methods that allow you to set or update the metadata.
|
||||
|
||||
If you want to add a property to the `metadata` object or update a property in the `metadata` object, you can pass the `metadata` object with the properties you want to add or update in it. For example:
|
||||
|
||||
```json
|
||||
{
|
||||
// other data
|
||||
"metadata": {
|
||||
"is_b2b": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If you want to remove a property from the `metadata` object, you can pass the `metadata` object with the property you want to delete. The property should have an empty string value. For example:
|
||||
|
||||
```json
|
||||
{
|
||||
// other data
|
||||
"metadata": {
|
||||
"is_b2b": "" // this deletes the `is_b2b` property from `metadata`
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 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={4} items={[
|
||||
{
|
||||
type: 'link',
|
||||
href: '/development/entities/create',
|
||||
label: 'Create an Entity',
|
||||
customProps: {
|
||||
icon: Icons['academic-cap-solid'],
|
||||
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',
|
||||
label: 'Create a Migration',
|
||||
customProps: {
|
||||
icon: Icons['academic-cap-solid'],
|
||||
description: 'Learn how to create migrations in Medusa.'
|
||||
}
|
||||
},
|
||||
]} />
|
||||
|
||||
<DocCardList colSize={6} items={[
|
||||
{
|
||||
type: 'link',
|
||||
href: '/development/entities/extend-entity',
|
||||
label: 'Extend an Entity',
|
||||
customProps: {
|
||||
icon: Icons['academic-cap-solid'],
|
||||
description: 'Learn how to extend a core Medusa entity.'
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'link',
|
||||
href: '/development/entities/extend-repository',
|
||||
label: 'Extend a Repository',
|
||||
customProps: {
|
||||
icon: Icons['academic-cap-solid'],
|
||||
description: 'Learn how to extend a core Medusa repository.'
|
||||
}
|
||||
},
|
||||
]} />
|
||||
@@ -0,0 +1,339 @@
|
||||
---
|
||||
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, you can use the `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.
|
||||
|
||||
By default, you don't need to create a repository for your custom entities. You can retrieve the default repository of an entity using the Entity Manager. You should only create a repository if you want to implement custom methods in it.
|
||||
|
||||
---
|
||||
|
||||
## Access Default Repository
|
||||
|
||||
If you haven't created a custom repository, you can access the default repository using the [Entity Manager](https://typeorm.io/entity-manager-api). The Entity Manager is registered in the [dependency container](../fundamentals/dependency-injection.md) under the name `manager`. So, you can [resolve it](../fundamentals/dependency-injection.md#resolve-resources) and use its method `getRepository` to retrieve the repository of an entity.
|
||||
|
||||
For example, to retrieve the default repository of an entity in a service:
|
||||
|
||||
```ts title=src/services/post.ts
|
||||
import { Post } from "../models/post"
|
||||
|
||||
class PostService extends TransactionBaseService {
|
||||
// ...
|
||||
|
||||
async list(): Promise<Post[]> {
|
||||
const postRepo = this.activeManager_.getRepository(
|
||||
Post
|
||||
)
|
||||
return await postRepo.find()
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Another example is retrieving the default repository of an entity in an endpoint:
|
||||
|
||||
```ts title=src/api/index.ts
|
||||
import { Post } from "../models/post"
|
||||
import { EntityManager } from "typeorm"
|
||||
|
||||
// ...
|
||||
|
||||
export default () => {
|
||||
// ...
|
||||
|
||||
storeRouter.get("/posts", async (req, res) => {
|
||||
const manager: EntityManager = req.scope.resolve("manager")
|
||||
const postRepo = manager.getRepository(Post)
|
||||
|
||||
return res.json({
|
||||
posts: await postRepo.find(),
|
||||
})
|
||||
})
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Create Custom Repository
|
||||
|
||||
If you want to add custom methods or override existing methods in a repository, you can create a custom repository.
|
||||
|
||||
Repositories are 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)
|
||||
.extend({
|
||||
customMethod(): void {
|
||||
// TODO add custom implementation
|
||||
return
|
||||
},
|
||||
})
|
||||
|
||||
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. You then use the `extend` method to add a new method `customMethod`.
|
||||
|
||||
You can learn about available Repository methods that you can override in [Typeorm's documentation](https://typeorm.io/repository-api).
|
||||
|
||||
:::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).
|
||||
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## Using Custom Repositories in Other Resources
|
||||
|
||||
### Endpoints
|
||||
|
||||
To access a custom repository within an endpoint, use the `req.scope.resolve` method. For example:
|
||||
|
||||
```ts title=src/api/index.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 custom 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