Nest.js Tutorial

Generating documentation with Compodoc and JSDoc

Marcin Wanago
JavaScriptNestJSNode.js

The reality of software projects is that people sometimes come and go. Therefore, the bigger our application is, the higher value we put on the ability to more easily introduce new teammates into the codebase. One of the ways to improve this process is to create documentation. This way, we can accumulate knowledge even if the people that wrote the code are no longer around and can’t explain their thought process in person.

The main issue is that writing and maintaining documentation takes valuable time. Since we could spend it on writing the actual code, it is sometimes difficult to justify it to the business side of our project. After all, time is money. Writing documentation can save us time in the long run, though. Fortunately, some tools can make writing documentation more manageable and less time-consuming.

An interesting fact about NestJS is that it shares a considerable chunk of the approach to the code structure with Angular. Because of that, a tool called Compodoc made with Angular in mind works with NestJS applications too.

Introducing Compodoc

Compodoc aims to generate the documentation for our project. It is open-source and can work offline. While it automatically creates the NestJS documentation, there are many ways to expand and customize it.

To start working with Compodoc, we first need to install it.

1npm install @compodoc/compodoc

A fitting way to run it is to add it to the scripts in our package.json:

package.json
1"scripts": {
2  "start": "nest start",
3  "documentation:serve": "npx @compodoc/compodoc -p tsconfig.json --serve",
4  // ...
5},

Thanks to adding the -serve, Compodoc generates the HTML files with our NestJS documentation and serves it at http://localhost:8080.

There are other flags that you might find useful. Type npx @compodoc/compodoc -help for a full list. For example, we can use --theme to choose one of the available themes.

The cool thing that we see right away in the project overview is a visual representation of our whole architecture with our modules and services. If our project is big, it can help get a better grasp of the code structure and how various modules work with each other.

Documenting individual files

An interesting thing about Compodoc is that it analyzes not only modules and their dependencies but also individual files.

Fortunately, it can interpret our TypeScript code along with the information about the types. So, for example, we can click on CreateCategoryDto above. This way, we can get more details about the argument of the createCategory method.

Providing additional information with JSDoc

While Compodoc can analyze our TypeScript code, we can help it by using the JSDoc markup language. It is a well-known way of writing comments in our code that various tools and IDEs can understand and support.

1/**
2 * A method that fetches the categories from the database
3 */
4getAllCategories() {
5  return this.categoriesRepository.find({ relations: ['posts'] });
6}

Compodoc supports the following tags from JSDoc:

With the @returns tag, we can provide the return type of a method and describe it. However, since we use TypeScript, we can use it to define the return type instead.

1/**
2 * A method that fetches the categories from the database
3 * @returns A promise with the list of categories
4 */
5getAllCategories(): Promise<Category[]> {
6  return this.categoriesRepository.find({ relations: ['posts'] });
7}

By using the @param tag, we provide the type of the argument and additional description. Again, we can use TypeScript to define the type of the argument.

1/**
2 * A method that deletes a category from the database
3 * @param id An id of a category. A category with this id should exist in the database
4 */
5async deleteCategory(id: number): Promise<void> {
6  const deleteResponse = await this.categoriesRepository.delete(id);
7  if (!deleteResponse.affected) {
8    throw new CategoryNotFoundException(id);
9  }
10}

With the @ignore tag, we can indicate that some parts of our code should not be presented in the documentation. A good example can be the constructor method:

1/**
2 * @ignore
3 */
4constructor(
5  @InjectRepository(Category)
6  private categoriesRepository: Repository<Category>
7) {}
We can use the @ignore not only with methods, but also with whole classes.

With the above tag, we can mark a method or a class as deprecated.

1/**
2 * @deprecated Use deleteCategory instead
3 */
4async deleteCategoryById(id: number): Promise<void> {
5  return this.deleteCategory(id);
6}

We can use @link to provide an anchor to other parts of our documentation or an external URL.

1/**
2 * See the [definition of the UpdateCategoryDto file]{@link UpdateCategoryDto} to see a list of required properties
3 */
4async updateCategory(id: number, category: UpdateCategoryDto): Promise<Category> {
5  await this.categoriesRepository.update(id, category);
6  const updatedCategory = await this.categoriesRepository.findOne(id, { relations: ['posts'] });
7  if (updatedCategory) {
8    return updatedCategory
9  }
10  throw new CategoryNotFoundException(id);
11}

With the above tag, we can provide a code example that will be displayed in the documentation.

1/**
2 * A method that fetches a category with a given id. Example:
3 * @example
4 * const category = await categoriesService.getCategoryById(1);
5 */
6async getCategoryById(id: number): Promise<Category> {
7  const category = await this.categoriesRepository.findOne(id, { relations: ['posts'] });
8  if (category) {
9    return category;
10  }
11  throw new CategoryNotFoundException(id);
12}

Documentation coverage

If you want to enforce the documentation to be written, you might find the documentation coverage to be valuable. With it, we can verify how much of our code is documented.

Compodoc doesn’t take private functions into account when calculating the coverage.

Documenting a file using markdown

Sometimes we might feel that documenting by placing the comments next to the code is not enough to paint a complete picture of our code. Thankfully, Compodoc supports creating documentations through markdown files. We need to place a markdown file next to the file we want to document and use the .md extension.

categories.service.md
1# CategoriesService
2 
3This service aims to perform various operations on the entity of the category.

Compodoc adds a new tab that we can click on to view our markdown when we do that.

Summary

In this article, we’ve gone through the idea of generating NestJS documentation with Compodoc. We’ve also learned how to customize it with JSDoc and use markdown. When we run the script, Compodoc creates a documentation directory that we can commit to our repository. Thanks to that, we can not only serve the documentation locally but also deploy it. All of the above make Compodoc a valuable tool to create and view the NestJS documentation of our application.