Создание веб-приложений, подобных администратору, с помощью NestJS и React Admin. Часть 1.

Базовая загрузка API и пользовательского интерфейса.

Введение

В этой серии статей я опишу, как быстро настроить панель администрирования на основе API для вашего проекта с помощью NestJS и React Admin.

Чтобы продолжить (в том случае, если вы читаете не для удовольствия), вам может потребоваться NodeJS 10+, npm, yarn. и MySQL, установленный на вашем компьютере. Кроме того, вы должны обладать базовыми знаниями в области TypeScript и React.

Наш проект будет состоять из 2-х частей:

  • REST API, написанный на TypeScript
  • Панель администратора, написанная на React

В демонстрационных целях мы создадим простое приложение для управления списком гостей. Это будет включать создание гостей, отображение списка и обновление информации о гостях.

Итак, начнем.

Создание API

Для создания API воспользуемся фреймворком NestJS. Мне нравится NestJS, потому что он основан на TypeScript и, таким образом, позволяет создавать более читаемый, лучше структурированный и менее подверженный ошибкам внутренний код.

Мы будем использовать инструмент NestJS CLI для инициализации нашего бэкэнда:

npm i -g @nestjs/cli
nest new api
cd api

Теперь, когда скелет проекта готов, мы добавим другие необходимые нам зависимости.

Мы будем использовать TypeORM (снова TypeScript) для работы с MySQL:

yarn add @nestjs/typeorm typeorm mysql class-validator class-transformer

Библиотека NestJS CRUD для упрощения создания наших конечных точек:

yarn add @nestjsx/crud

И NestJS Config для управления конфигурацией нашего приложения:

yarn add nestjs-config

После того, как вы закончите с зависимостями, давайте сгенерируем скелет конечной точки нашего API гостей.

nest generate module guests
nest generate controller guests
nest generate service guests

И создайте класс модели для нашей гостевой сущности (src / guest / guest.entity.ts)

import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
import { IsEmail } from 'class-validator';

@Entity({ name: 'guests' })
export class GuestEntity {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  firstName: string;

  @Column()
  lastName: string;

  @Column({
    unique: true,
  })
  @IsEmail()
  email: string;

  @Column({ default: false })
  isPresent: boolean;
}

Теперь мы добавляем код, чтобы соединить части вместе.

Обновите src / guest / guest.module.ts с зависимостями TypeORM.

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { GuestsController } from './guests.controller';
import { GuestEntity } from './guest.entity';
import { GuestsService } from './guests.service';

@Module({
  imports: [
    TypeOrmModule.forFeature([GuestEntity]),
  ],
  controllers: [
    GuestsController,
  ],
  providers: [
    GuestsService,
  ],
})
export class GuestsModule { }

Сделайте так, чтобы src / guest / guest.service.ts расширял RepositoryService из NestJS CRUD

import { Injectable } from '@nestjs/common';
import { GuestEntity } from './guest.entity';
import { RepositoryService } from '@nestjsx/crud/typeorm';
import { InjectRepository } from '@nestjs/typeorm';
@Injectable()
export class GuestsService extends RepositoryService<GuestEntity> {
  constructor(@InjectRepository(GuestEntity) repository) {
    super(repository);
  }
}

и добавьте его в src / guest / guest.controller.ts. Кроме того, добавьте в контроллер декоратор Crud, чтобы включить функции, связанные с NestJS CRUD API.

import { Controller } from '@nestjs/common';
import { Crud } from '@nestjsx/crud';
import { GuestsService } from './guests.service';
import { GuestEntity } from './guest.entity';
@Crud(GuestEntity)
@Controller('guests')
export class GuestsController {
  constructor(public service: GuestsService) { }
}

Наконец, мы настраиваем TypeORM в основном модуле (src / app.module.ts):

import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from 'nestjs-config';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { GuestsModule } from './guests/guests.module';
import * as path from 'path';
@Module({
  imports: [
    ConfigModule.load(path.resolve(__dirname, 'config', '*.{ts,js}')),
    TypeOrmModule.forRootAsync({
      useFactory: (config: ConfigService) => config.get('database'),
      inject: [ConfigService],
    }),
    GuestsModule,
  ],
  controllers: [
    AppController,
  ],
  providers: [
    AppService,
  ],
})
export class AppModule { }

И добавьте соответствующие файлы конфигурации

Src / config / database.ts:

export default {
  host: process.env.DB_HOST,
  type: 'mysql',
  port: process.env.DB_PORT || 3306,
  username: process.env.DB_USER,
  password: process.env.DB_PASSWORD,
  database: process.env.DB_DATABASE,
  entities: ['src/**/*.entity{.ts,.js}'],
  synchronize: process.env.DB_SYNCRONIZE === 'true',
  logging: process.env.DB_LOGGING === 'true',
};

.env:

DB_HOST = localhost
DB_PORT = 3306
DB_USER = 
DB_PASSWORD = 
DB_DATABASE = 
DB_SYNCRONIZE = true
DB_LOGGING = true

И мы готовы к старту!

Не так быстро :) Позже React Admin потребует, чтобы CORS был включен на стороне API. Итак, нам нужно изменить src / main.ts. И, кстати, позволяет упростить жизнь React и освободить 3000 портов!

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule, { cors: true });
  await app.listen(3001);
}
bootstrap();

Теперь действительно готово!

yarn start

и продолжаем создавать пользовательский интерфейс панели администрирования.

Создание пользовательского интерфейса администратора

Как упоминалось ранее, для этой цели мы будем использовать React Admin - библиотеку компонентов на основе React для создания интерфейсов, подобных админке.

Начнем с инициализации приложения React

npm install -g create-react-app
create-react-app admin-ui
cd admin-ui

Затем добавьте в проект React Admin.

yarn add react-admin prop-types

и небольшая библиотека, разработанная нами (FusionWorks): @ FusionWorks / ra-data-nest-crud, которая интегрирует React Admin с нашей серверной частью на основе NestJS CRUD.

yarn add @fusionworks/ra-data-nest-crud

После этого мы можем инициализировать компонент React Admin и создать гостевой редактор. Во-первых, мы обновляем src / App.js с помощью корневого компонента администратора и компонента ресурсов для гостей:

import React from 'react';
import { Admin, Resource, ShowGuesser, ListGuesser } from 'react-admin';
import crudProvider from '@fusionworks/ra-data-nest-crud';
import { GuestCreate, GuestEdit } from './Guests';
const dataProvider = crudProvider('http://localhost:3001');
const App = () => (
  <Admin dataProvider={dataProvider}>
    <Resource name="guests" list={ListGuesser} create={GuestCreate} edit={GuestEdit} show={ShowGuesser} />
  </Admin>
);
export default App;

Затем добавьте соответствующие формы в src / Гости / index.js.

Обратите внимание, что мы используем ListGuesser и ShowGuesser React Admin для просмотра списка и просмотра. При необходимости они могут быть заменены специальной реализацией так же, как создание и редактирование форм ниже.

import React from 'react';
import {
  Create,
  SimpleForm,
  TextInput,
  BooleanInput,
  Edit,
  Filter,
  required,
  email,
} from 'react-admin';
const validateEmail = [required(), email()];
const validateRequired = required();
export const GuestCreate = props => (
  <Create {...props}>
    <SimpleForm redirect="show">
      <TextInput source="firstName" validate={validateRequired} />
      <TextInput source="lastName" validate={validateRequired} />
      <TextInput source="email" validate={validateEmail} />
    </SimpleForm>
  </Create>
);
const GuestEditTitle = ({ record }) => (<span>{`${record.firstName} ${record.lastName}`}</span>);
export const GuestEdit = props => (
  <Edit {...props} title={<GuestEditTitle />}>
    <SimpleForm redirect="list">
      <TextInput source="firstName" validate={validateRequired} />
      <TextInput source="lastName" validate={validateRequired} />
      <TextInput source="email" validate={validateEmail} />
      <BooleanInput source="isPresent" />
    </SimpleForm>
  </Edit>
);

Как только это будет сделано, мы готовы к работе!

yarn start

Заключение

Пока все выглядит отлично, но мы еще не коснулись таких вещей, как аутентификация, авторизация, случаи, когда модели базы данных и API должны отличаться и т. Д. Я расскажу о них в следующих статьях, и мы расскажем, если этот стек хорошо выживет. Так что следите за новостями о FusionWorks:

Полезные ссылки

Исходный код статьи: https://github.com/FusionWorks/nestjs-crud-react-admin-boilerplate

Если вы хотите заняться дайпером самостоятельно, вот набор ссылок на документацию, которая может быть вам полезна: