Skip to content
This repository was archived by the owner on Feb 6, 2025. It is now read-only.

Docs: Add Custom Code Guide #520

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
246 changes: 246 additions & 0 deletions docs/getting-started/add-custom-code.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
---
id: add-custom-code
title: How To Add Custom Code To Your Service
sidebar_label: Add Custom Code To Your Service
slug: /add-custom-code-to-your-service
---

# How To Add Custom Code To Your Service

While Amplication generates a robust, production-ready backend for your application, you'll often need to add custom business logic or additional functionality. This guide will walk you through the process of adding custom code to your Amplication-generated service while maintaining compatibility with future builds.

## Prerequisites

Before you begin, make sure you have:

1. [Created your first service](/first-service/)
2. [Set up entities](/set-up-entities/) for your service
3. [Configured roles and permissions](/configure-roles-and-permissions/)
4. [Added plugins to your service](/add-plugins-service/)
5. [Committed changes and built a new version](/commit-and-build-new-versions/)

## Understanding Custom Code in Amplication

When adding custom code to your Amplication-generated service, it's important to understand how Amplication manages and preserves your changes:

1. Custom code is added to specific files that Amplication recognizes and preserves during rebuilds.
2. You'll work directly in your git repository, making changes to the generated code.
3. Amplication uses a folder structure that separates customizable and non-customizable code.
4. The `base` folder contains files that should not be modified, as they will be overwritten by Amplication.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The non-base files can be updated (and not overwritten) by Amplication- that's the change detailed in amplication/amplication#8895.
None of the files are overwritten by Amplication- we never overwrite user's code, but issue a smart git sync.
We recommend (mostly for best practices and good software engineering practices) to update the non-base files

5. Files outside the `base` folder can be safely customized and will be preserved across builds.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above


Let's walk through the process of adding custom code to implement a password reset feature for the User entity.

## Step 1: Merge the Amplication Branch

First, ensure that your local repository is up-to-date with the latest Amplication-generated code:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I understand the flow.
I think first the user should merge the Amplication branch into the main branch (using GitHub).
Then the user can get the latest main version locally.
Am I missing something?


1. Open your terminal and navigate to your project's root directory.
2. Switch to your main branch:
```bash
git checkout main
```
3. Pull the latest changes:
```bash
git pull origin main
```
4. Merge the `amplication` branch into your main branch:
```bash
git merge amplication
```
5. Push the merged changes to your remote repository:
```bash
git push origin main
```

## Step 2: Create a New Branch for Custom Code

Create a new branch from the main branch to make your custom code changes:

```bash
git checkout -b feature/password-reset main
```

## Step 3: Locate the Correct Files

Navigate to your service's `src` folder. You'll find a folder for each entity. In this case, we'll be working with the `user` folder:

```
src
└── user
├── base
│ ├── user.controller.base.ts
│ ├── user.service.base.ts
│ └── ...
├── user.controller.ts
├── user.module.ts
├── user.resolver.ts
└── user.service.ts
```

We'll be modifying `user.service.ts` and `user.controller.ts` to add our custom password reset functionality.

## Step 4: Add Custom Logic to the Service

Open `src/user/user.service.ts`. This file extends the base service and is where we'll add our custom method.

1. Add the necessary imports at the top of the file:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The required import-s are not part of the file?
If not- do we want to write it here? I feel like it's too detailed, and it makes things look too much complicated


```typescript
import { Injectable } from "@nestjs/common";
import { PrismaService } from "nestjs-prisma";
import { UserServiceBase } from "./base/user.service.base";
import { PasswordService } from "../auth/password.service";
import { User } from "./base/User";
import { UserWhereUniqueInput } from "./base/UserWhereUniqueInput";
```

2. Add the custom `resetPassword` method to the `UserService` class:

```typescript
@Injectable()
export class UserService extends UserServiceBase {
constructor(
protected readonly prisma: PrismaService,
protected readonly passwordService: PasswordService
) {
super(prisma, passwordService);
}

async resetPassword(args: UserWhereUniqueInput): Promise<User> {
const newPassword = Math.random().toString(36).slice(-8); // Generate a random password
const hashedPassword = await this.passwordService.hashPassword(newPassword);

const updatedUser = await this.prisma.user.update({
where: args,
data: {
password: hashedPassword
}
});

// In a real-world scenario, you'd want to send this password to the user securely
console.log(`New password for user ${updatedUser.id}: ${newPassword}`);

return updatedUser;
}
}
```

## Step 5: Add a New Endpoint to the Controller

Now, let's add a new endpoint to the User controller to expose our password reset functionality. Open `src/user/user.controller.ts`:

1. Add the necessary imports at the top of the file:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above


```typescript
import * as common from "@nestjs/common";
import * as swagger from "@nestjs/swagger";
import * as nestAccessControl from "nest-access-control";
import { UserService } from "./user.service";
import { UserControllerBase } from "./base/user.controller.base";
import { User } from "./base/User";
import { UserWhereUniqueInput } from "./base/UserWhereUniqueInput";
import { AclValidateRequestInterceptor } from "../interceptors/aclValidateRequest.interceptor";
```

2. Add the new `resetPassword` endpoint to the `UserController` class:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this endpoint should be exposed as a new API, and should be generated using Amplication.
Again- Maybe I'm missing something, but I feel like this example is too complicated and raises a lot of questions and misunderstanding


```typescript
@swagger.ApiTags("users")
@common.Controller("users")
export class UserController extends UserControllerBase {
constructor(
protected readonly service: UserService,
@nestAccessControl.InjectRolesBuilder()
protected readonly rolesBuilder: nestAccessControl.RolesBuilder
) {
super(service, rolesBuilder);
}

@common.UseInterceptors(AclValidateRequestInterceptor)
@common.Patch("/:id/reset-password")
@nestAccessControl.UseRoles({
resource: "User",
action: "update",
possession: "own",
})
@swagger.ApiOkResponse({ type: User })
@swagger.ApiNotFoundResponse({ type: errors.NotFoundException })
@swagger.ApiForbiddenResponse({ type: errors.ForbiddenException })
async resetPassword(
@common.Param() params: UserWhereUniqueInput
): Promise<User> {
return this.service.resetPassword(params);
}
}
```

## Step 6: Commit and Push Your Changes

After adding your custom code, commit the changes to your git repository:

```bash
git add .
git commit -m "Added custom password reset functionality"
git push origin feature/password-reset
```

## Step 7: Create a Pull Request

Go to your repository on GitHub (or your chosen git provider) and create a new pull request:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't the PR be created from the local terminal, instead of going back to GitHub.
Moreover- is it important to detail these steps which should be very clear to every developer?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding the importance of detailing those git-focused steps, I can simplify it a bit, but I'm also addressing the question this developer raised in this issue.


1. Set the base branch to `main`
2. Set the compare branch to `feature/password-reset`
3. Give your pull request a descriptive title and description
4. Create the pull request

## Step 8: Merge the Pull Request

After reviewing your changes, merge the pull request into the main branch. This step integrates your custom code with the main codebase.

## Step 9: Rebuild Your Service in Amplication

Now that you've added custom code to your repository and merged it into the main branch, you need to rebuild your service in Amplication to ensure everything works together:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is incorrect!
After adding your custom code, you need to do nothing in Amplication. In case of infrastructure changes- new integrations / new entity / new field / new API / new DTO or such- the change should be performed in Amplication and generate the code. The PR in Amplication brach will know to respect the custom code created by the user, avoid overwriting it, and include only the diff which represents the infrastructure changes

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please run the flow yourself to be familiar with it, and make sure it's working properly.


1. Go to your service in the Amplication web interface.
2. Click on "Commit Changes & Build" in the right sidebar.
3. In the commit message, write "Integrated custom password reset functionality".
4. Click "Commit Changes & Build" to start the build process.

Amplication will now rebuild your service, integrating your custom code with the generated code.

## You're Done!

Congratulations! You've successfully added custom code to implement a password reset feature in your Amplication-generated service. This custom logic will now be available through your API, allowing users to reset their passwords.

## More Custom Code Examples

Here are more examples of how to add custom code in different layers of your service.

The purpose of these examples is to get familiar with the structure and responsibility of each of the components in the server.

- **Example**: [How to add business logic to a service](/custom-code/business-logic/)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure why do we need this file after adding this page (- feels like redundant with obsolete info)

- **Example**: [How to add an action to a REST API controller](/custom-code/controller-action/)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file feels obsolete as well. Adding a REST API action should be done using Amplication platform now

- **Example**: [How to add a query to a GraphQL resolver](/custom-code/graphql-query/)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here....


## Best Practices for Custom Code

When adding custom code to your Amplication service, keep these best practices in mind:

1. Always add custom code to the non-base files (e.g., `user.service.ts` instead of `user.service.base.ts`).
2. Use the types and interfaces generated by Amplication to ensure type safety.
3. Leverage the existing services and utilities provided by Amplication (like `PasswordService` in this example).
4. Document your custom code to make it easier for team members to understand and maintain.
5. Always create a new branch for your custom code changes.
6. Regularly merge the `amplication` branch into your main branch to stay up-to-date with Amplication-generated changes.

## Next Steps

Now that you know how to add custom code to your Amplication service, you can extend its functionality in various ways:

- Implement complex business logic specific to your application
- Add custom API endpoints for specialized operations
- Integrate with external services or APIs
- Implement advanced validation and data processing

Amplication is designed to be flexible, allowing you to leverage its powerful code generation while still giving you the freedom to customize your service as needed.
3 changes: 2 additions & 1 deletion docs/getting-started/generated-app.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,5 @@ For Node.js services, the Admin project provides a React application with ready-

## Learn more

- [How to add custom code to your application](/how-to/custom-code)
- [Understanding Custom Code in Amplication](/custom-code-overview/)
- [Add Custom Code To Your Service](/add-custom-code-to-your-service)
4 changes: 2 additions & 2 deletions docs/getting-started/view-generated-code.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
---
id: view-generated-code
title: Viewing the generated code
sidebar_label: View the generated code
sidebar_label: Code View Explained
slug: /getting-started/view-generated-code
---

# Viewing the Generated Code
# Code View Explained

Use **Code View** to view and explore the generated code. You can see the updated code before it is synced with GitHub or downloaded.

Expand Down
Loading
Loading