DEV Community

usegen
usegen

Posted on

Refine.dev and useGenerated.com - business apps extremely fast

What is Refine.dev ?

Open-source enterprise application framework - it allows you to build business apps and admin panels in React extremely fast.

A React-admin alternative.

Cool part about it is that it auto-generates React code as one would have written by hand. This approach is extremely productive and flexible.

The generated code is in Material UI, Ant Design, ChakraUI, Mantine or headless/you own custom.

What is useGenerated.com ?

A CLI that auto-generates a CRUD NestJS, Prisma ORM, GraphQL API in minutes.

  1. Define the database models in Prisma ORM.
  2. Describe your API, query depth, authorization/ authentication in a simple configuration file.
  3. Run a CLI command. Congrats! you have a GraphQL CRUD API, as nested as you want, with filter and sort for nested queries.
  4. **Customize, extend anything **from the output. You are in full control. The output is code as you(or me) would have written.

Sounds like a good match right ?

It absolutely is! They both give you the speed of a framework but full flexibility as you would have written it from scratch, because of their approach auto-generate code as the "hand written one".

Enough talk! let's code

Go to useGenerated.com and use the code EGDCONX to get it free (limited offer). Download and extract the content.

Getting started with the backend - Initial setup

  1. Create a NestJs App

npm i -g @nestjs/cli
nest new project-name

  1. replace the package.json with the one received with this module

  2. Add the use-generated folder at the same level as the NestJs app and run yarn or npm install

  3. Run yarn use-generated init

If everything worked well you should now have a \use-generated-config folder created at the root level. Your app should look like this

For this example we will only change appconfig.json from that folder.

First DB models

Have a .env file that points to a postgress db like this. (keep the other 2 variables with random strings we'll talk about that later)

DATABASE_URL="postgresql://johndoe:randompass@localhost:5432/mydb?schema=public"

SALT ="0bsfe607f676ee7cfd9678580b86f501be8cbf5cb04174e2"
JWT_SECRET = "32472YKJHDfdfsdkasjhdjwu.fvfdfd"
Enter fullscreen mode Exit fullscreen mode

Add the following DB models in prisma/schema.prisma file

model BlogPost {
  id        Int       @id @default(autoincrement())
  title     String
  content   String?
  published Boolean
  authorId  Int?
  author    User?     @relation(fields: [authorId], references: [id])
  comments  Comment[]
}

model Comment {
  id       Int      @id @default(autoincrement())
  content  String
  authorId Int?
  author   User?    @relation(fields: [authorId], references: [id])
  postId   Int
  post     BlogPost @relation(fields: [postId], references: [id])
}

model User {
  id           Int        @id @default(autoincrement())
  email        String     @unique
  passwordHash String
  age          String?
  resetToken   String?    @unique
  roles        UserRole[]
  comments     Comment[]
  posts        BlogPost[]
}

enum UserRole {
  AuthenticatedUser
  Admin
  SuperAdmin
}

Enter fullscreen mode Exit fullscreen mode

Run yarn prisma generate and yarn prisma migrate dev.

It generates the Prisma client and the db migrations, you will need to run this commands every time prisma/schema.prisma file changes.

If this doesn't look familiar please take a look at prisma ORM

Let's configure now how the backend app should be structred and how the GraphQL api will look like.
Go to appconfig.json and add the following to the modules property

...
  "modules": [{
      "name": "user",
      "models": [
        {
          "name": "User",
          "queryDepthLevel":1
        }
      ]
    },
    {
      "name": "post",
      "models": [
        {
          "queryDepthLevel":2,
          "name": "BlogPost"
        },
        {
          "name": "Comment",
          "queryDepthLevel":2
        } 
      ]
    }],
...
Enter fullscreen mode Exit fullscreen mode

Modules will be the folder structure and the NestJS modules.

queryDepthLevel defines how many levesl can the queries be for that specific resource / model. it can also be configured at the root level with defaultQueryDepthLevel

Run yarn use-generated api. Congrats! you just created a GraphQL API

the code should look like this

run yarn start:dev, navigate to http://localhost:3000/graphql and check that the API is working

Change the port of the application to 3001 so that the frontend can use the 3000. you can do that in src/main.ts
line 6 :await app.listen(3000); becomes await app.listen(3001);

Getting started with the frontend - refine.dev

feel free to check out the refine tutorial

Creating the project:

yarn create refine-app@latest -- -o refine-mui frontend-app
Enter fullscreen mode Exit fullscreen mode

after all is installed
cd frontend-app
yarn dev

Let's do some clean up!

In the src/App.tsx remove the <GitHubBanner /> component and the <Route index element={<WelcomePage />} />

Remove the simple-rest dataProvider, this line from src/App.tsx

import dataProvider from "@refinedev/simple-rest";
Enter fullscreen mode Exit fullscreen mode

Add the refine-use-generated dataProvider.

In the terminal run

yarn add refine-use-generated
Enter fullscreen mode Exit fullscreen mode

add in src/App.tsx

import dataProvider from "refine-use-generated";
Enter fullscreen mode Exit fullscreen mode

and change the dataProvider initialization to this

dataProvider(new GraphQLClient('http://localhost:3001/graphql'))
Enter fullscreen mode Exit fullscreen mode

full snippet

<Refine notificationProvider={notificationProvider}
routerProvider={routerBindings}
dataProvider={dataProvider(new GraphQLClient('http://localhost:3001/graphql'))}
options={{
  syncWithLocation: true,
  warnWhenUnsavedChanges: true,

  }}
>
Enter fullscreen mode Exit fullscreen mode

Add the first resource

  1. Add the resources prop in the <Refine /> component
<Refine notificationProvider={notificationProvider}
              routerProvider={routerBindings}
              dataProvider={dataProvider(new GraphQLClient('http://localhost:3001/graphql'))}
              options={{
                syncWithLocation: true,
                warnWhenUnsavedChanges: true,

              }}
              resources={[
                {
                  name: "blog-posts",
                  list: "/blog-posts",
                  show: "/blog-posts/show/:id",
                  create: "/blog-posts/create",
                  edit: "/blog-posts/edit/:id",
                },
              ]}
            >
Enter fullscreen mode Exit fullscreen mode

Take a closer look at the section:

 resources={[
                {
                  name: "blog-posts",
                  list: "/blog-posts",
                  show: "/blog-posts/show/:id",
                  create: "/blog-posts/create",
                  edit: "/blog-posts/edit/:id",
                },
              ]}
Enter fullscreen mode Exit fullscreen mode

The name of the resource is in plural and kebab-case, the rest are routes for corresponding operation.

  1. Add the routes and components.
<Routes>
  <Route path="blog-posts">
    <Route index
      element={
        <MuiInferencer
          meta={{
            'blog-posts': {
              getList: {
                fields: ["id", "title", "content"],
              },
            },
          }}
        />}
    />
    <Route
      path="show/:id"
      element={
        <MuiInferencer
          meta={{
            'blog-posts': {
              getOne: {
                fields: ["id", "title", "content"],
              },
            },
          }}
        />}
    />
  </Route>
</Routes>
Enter fullscreen mode Exit fullscreen mode

also add the MuiInferencer import like so

import { MuiInferencer } from "@refinedev/inferencer/mui";
Enter fullscreen mode Exit fullscreen mode

Go to http://localhost:3000/blog-posts and see you newly created page

What happen here ? what is the MuiInferencer ?

Inferencers from Refine are components that have the CRUD functionality, it needs a basic configuration in the meta prop like this

[resource-name-plural]:{[operationName]:{fileds:["field1","field2","field3"]
Enter fullscreen mode Exit fullscreen mode

Note the operationName for the list is getList and for show is getOne.
the path of the route should match the ones defined in <Refine /> component property resources

Generated code by MuiInferencer

If you go to http://localhost:3000/blog-post you will notice a black banner with a blue button show the auto-generated code press on that and you get the code of component which can replace the MuiInferencer.

your code should look like this

to be continued ...

Top comments (0)