How to do CRUD in GraphQL Nest.js API?
Published: 2024-05-17 13:30:18

Implementing CRUD operations in a GraphQL API using Nest.js involves setting up mutations and queries that correspond to create, read, update, and delete actions. Below, I'll outline how to implement these CRUD operations using a simplified example with a `Recipe` entity.

### 1. Setup the GraphQL Module

Assuming you have already set up your Nest.js project with GraphQL as outlined in the previous response, ensure your GraphQL module is configured correctly in `app.module.ts`.

### 2. Define the GraphQL Types

Using the code-first approach, define your entity as a GraphQL type. Here’s the `Recipe` class:

```typescript
import { ObjectType, Field, ID } from '@nestjs/graphql';

@ObjectType()
export class Recipe {
  @Field(() => ID)
  id: string;

  @Field()
  title: string;

  @Field({ nullable: true })
  description?: string;
}
```

### 3. Create Input Types

For mutations, you will need input types. Here's how you can define them for creating and updating recipes:

```typescript
import { InputType, Field } from '@nestjs/graphql';

@InputType()
export class CreateRecipeInput {
  @Field()
  title: string;

  @Field({ nullable: true })
  description?: string;
}

@InputType()
export class UpdateRecipeInput {
  @Field(() => ID)
  id: string;

  @Field({ nullable: true })
  title?: string;

  @Field({ nullable: true })
  description?: string;
}
```

### 4. Define Resolvers

Create a resolver for handling queries and mutations:

```typescript
import { Resolver, Query, Mutation, Args } from '@nestjs/graphql';
import { Recipe } from './entities/recipe.entity';
import { CreateRecipeInput, UpdateRecipeInput } from './dto/recipe.input';

@Resolver(of => Recipe)
export class RecipeResolver {
  private recipes: Recipe[] = []; // This will act as our in-memory database

  @Query(returns => Recipe, { nullable: true })
  async recipe(@Args('id', { type: () => ID }) id: string): Promise<Recipe> {
    return this.recipes.find(recipe => recipe.id === id);
  }

  @Query(returns => [Recipe])
  async recipes(): Promise<Recipe[]> {
    return this.recipes;
  }

  @Mutation(returns => Recipe)
  async createRecipe(@Args('createRecipeData') createRecipeData: CreateRecipeInput): Promise<Recipe> {
    const recipe = {
      id: Math.random().toString(), // generate a random ID for example purposes
      ...createRecipeData,
    };
    this.recipes.push(recipe);
    return recipe;
  }

  @Mutation(returns => Recipe)
  async updateRecipe(@Args('updateRecipeData') updateRecipeData: UpdateRecipeInput): Promise<Recipe> {
    const recipe = this.recipes.find(r => r.id === updateRecipeData.id);
    if (!recipe) {
      throw new Error('Recipe not found');
    }
    Object.assign(recipe, updateRecipeData);
    return recipe;
  }

  @Mutation(returns => Boolean)
  async deleteRecipe(@Args('id', { type: () => ID }) id: string): Promise<boolean> {
    const index = this.recipes.findIndex(recipe => recipe.id === id);
    if (index === -1) {
      throw new Error('Recipe not found');
    }
    this.recipes.splice(index, 1);
    return true;
  }
}
```

### 5. Connect to a Real Database

In a real application, you should replace the in-memory array with a database. You can integrate with a database like PostgreSQL using TypeORM or any other database module. Use repositories or custom services to handle data operations.

### 6. Considerations for Production

- **Validation**: Add DTO validation using class-validator to ensure data integrity.
- **Authorization**: Secure your API using guards and decorators to manage who can perform CRUD operations.
- **Error Handling**: Implement proper error handling to manage and respond to errors gracefully.
- **Testing**: Write unit and integration tests for your resolvers and services to ensure they work as expected.

This example demonstrates basic CRUD operations using Nest.js and GraphQL. The code-first approach allows you to maintain your GraphQL schema in TypeScript, making it easy to refactor and understand.

Disclaimer: This content is generated by AI.