From 407b2a9aa124491bac98ba2b93458182b5e5e4c8 Mon Sep 17 00:00:00 2001 From: Abdelrahman Essawy Date: Wed, 24 Apr 2024 11:36:36 +0200 Subject: [PATCH 01/11] fix: make `meta.permissions` overrideable --- packages/appwrite/src/dataProvider.ts | 71 ++++++++++++++------------- 1 file changed, 37 insertions(+), 34 deletions(-) diff --git a/packages/appwrite/src/dataProvider.ts b/packages/appwrite/src/dataProvider.ts index 09869ff0e800..6b78c90db616 100644 --- a/packages/appwrite/src/dataProvider.ts +++ b/packages/appwrite/src/dataProvider.ts @@ -6,14 +6,31 @@ import { getAppwriteSorting, } from "./utils"; +type DefaultOptions = { + databaseId: string; + defaultReadPermissions?: Permission[]; + defaultWritePermissions?: Permission[]; +}; + export const dataProvider = ( appwriteClient: Appwrite, - options: { databaseId: string } = { databaseId: "default" }, + options: DefaultOptions = { + databaseId: "default", + defaultReadPermissions: [], + defaultWritePermissions: [], + }, ): Required => { const { databaseId } = options; const database = new Databases(appwriteClient); + const defaultReadPermissions = options.defaultReadPermissions ?? [ + Permission.read(Role.any()), + ]; + const defaultWritePermissions = options.defaultWritePermissions ?? [ + Permission.write(Role.any()), + ]; + return { getList: async ({ resource, pagination, filters, sorters }) => { const { current = 1, pageSize = 10, mode = "server" } = pagination ?? {}; @@ -45,7 +62,6 @@ export const dataProvider = ( resource, id.toString(), ); - return { data: { id: $id, @@ -54,18 +70,16 @@ export const dataProvider = ( } as any; }, update: async ({ resource, id, variables, meta }) => { - const permissions = [ - Permission.read(Role.any()), - Permission.write(Role.any()), - ...(meta?.readPermissions ?? ""), - ...(meta?.writePermissions ?? ""), - ]; + const readPermissions = meta?.readPermissions ?? defaultReadPermissions; + const writePermissions = + meta?.writePermissions ?? defaultWritePermissions; + const { $id, ...restData } = await database.updateDocument( databaseId, resource, id.toString(), variables as any, - permissions, + [...readPermissions, ...writePermissions], ); return { @@ -76,19 +90,16 @@ export const dataProvider = ( } as any; }, create: async ({ resource, variables, meta }) => { - const permissions = [ - Permission.read(Role.any()), - Permission.write(Role.any()), - ...(meta?.readPermissions ?? ""), - ...(meta?.writePermissions ?? ""), - ]; + const readPermissions = meta?.readPermissions ?? defaultReadPermissions; + const writePermissions = + meta?.writePermissions ?? defaultWritePermissions; const { $id, ...restData } = await database.createDocument( databaseId, resource, meta?.documentId ?? ID.unique(), variables as unknown as object, - permissions, + [...readPermissions, ...writePermissions], ); return { @@ -99,12 +110,10 @@ export const dataProvider = ( } as any; }, createMany: async ({ resource, variables, meta }) => { - const permissions = [ - Permission.read(Role.any()), - Permission.write(Role.any()), - ...(meta?.readPermissions ?? ""), - ...(meta?.writePermissions ?? ""), - ]; + const readPermissions = meta?.readPermissions ?? defaultReadPermissions; + const writePermissions = + meta?.writePermissions ?? defaultWritePermissions; + const data = await Promise.all( variables.map((document) => database.createDocument( @@ -112,7 +121,7 @@ export const dataProvider = ( resource, meta?.documentId ?? ID.unique(), document as unknown as any, - permissions, + [...readPermissions, ...writePermissions], ), ), ); @@ -126,7 +135,6 @@ export const dataProvider = ( }, deleteOne: async ({ resource, id }) => { await database.deleteDocument(databaseId, resource, id.toString()); - return { data: { id }, } as any; @@ -137,7 +145,6 @@ export const dataProvider = ( database.deleteDocument(databaseId, resource, id.toString()), ), ); - return { data: ids.map((id) => ({ id, @@ -150,7 +157,6 @@ export const dataProvider = ( database.getDocument(databaseId, resource, id.toString()), ), ); - return { data: data.map(({ $id, ...restData }) => ({ id: $id, @@ -159,12 +165,10 @@ export const dataProvider = ( } as any; }, updateMany: async ({ resource, ids, variables, meta }) => { - const permissions = [ - Permission.read(Role.any()), - Permission.write(Role.any()), - ...(meta?.readPermissions ?? ""), - ...(meta?.writePermissions ?? ""), - ]; + const readPermissions = meta?.readPermissions ?? defaultReadPermissions; + const writePermissions = + meta?.writePermissions ?? defaultWritePermissions; + const data = await Promise.all( ids.map((id) => database.updateDocument( @@ -172,11 +176,10 @@ export const dataProvider = ( resource, id.toString(), variables as unknown as object, - permissions, + [...readPermissions, ...writePermissions], ), ), ); - return { data: data.map(({ $id, ...restData }) => ({ id: $id, From 6e60e76b06bec2d7648de8482991c1c987f008db Mon Sep 17 00:00:00 2001 From: Abdelrahman Essawy Date: Wed, 24 Apr 2024 11:45:59 +0200 Subject: [PATCH 02/11] fix(appwrite): remove default fallback values --- packages/appwrite/src/dataProvider.ts | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/packages/appwrite/src/dataProvider.ts b/packages/appwrite/src/dataProvider.ts index 6b78c90db616..38402b564422 100644 --- a/packages/appwrite/src/dataProvider.ts +++ b/packages/appwrite/src/dataProvider.ts @@ -20,17 +20,14 @@ export const dataProvider = ( defaultWritePermissions: [], }, ): Required => { - const { databaseId } = options; + const { + databaseId, + defaultReadPermissions = [], + defaultWritePermissions = [], + } = options; const database = new Databases(appwriteClient); - const defaultReadPermissions = options.defaultReadPermissions ?? [ - Permission.read(Role.any()), - ]; - const defaultWritePermissions = options.defaultWritePermissions ?? [ - Permission.write(Role.any()), - ]; - return { getList: async ({ resource, pagination, filters, sorters }) => { const { current = 1, pageSize = 10, mode = "server" } = pagination ?? {}; From 8371462fb3e9dc502f98c9919758cdaa51e0241f Mon Sep 17 00:00:00 2001 From: Abdelrahman Essawy Date: Wed, 24 Apr 2024 12:13:12 +0200 Subject: [PATCH 03/11] Create tasty-garlics-notice.md --- .changeset/tasty-garlics-notice.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/tasty-garlics-notice.md diff --git a/.changeset/tasty-garlics-notice.md b/.changeset/tasty-garlics-notice.md new file mode 100644 index 000000000000..ebcc0cd6de71 --- /dev/null +++ b/.changeset/tasty-garlics-notice.md @@ -0,0 +1,5 @@ +--- +"@refinedev/appwrite": minor +--- + +fixing an issue which didn't allow users to override the default `readPermissions` / `writePermissions` values `Role.any()`, by passing `defaultReadPermissions` / `defaultWritePermissions` OR by passing `meta?.readPermissions` / `meta?.writePermissions`. From 2f1f62a0a9d2edfa41bc84a514e4abf48525bfdf Mon Sep 17 00:00:00 2001 From: Abdelrahman Essawy Date: Fri, 26 Apr 2024 13:12:51 +0300 Subject: [PATCH 04/11] fix: revert default permissions --- packages/appwrite/src/dataProvider.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/appwrite/src/dataProvider.ts b/packages/appwrite/src/dataProvider.ts index 38402b564422..973af4fd7cec 100644 --- a/packages/appwrite/src/dataProvider.ts +++ b/packages/appwrite/src/dataProvider.ts @@ -22,8 +22,8 @@ export const dataProvider = ( ): Required => { const { databaseId, - defaultReadPermissions = [], - defaultWritePermissions = [], + defaultReadPermissions = [Permission.read(Role.any())], + defaultWritePermissions = [Permission.write(Role.any())], } = options; const database = new Databases(appwriteClient); From 6ee3d68a77b6928a7d095f09a3a55b039d9e78b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ali=20Emir=20=C5=9Een?= Date: Tue, 30 Apr 2024 22:40:44 +0300 Subject: [PATCH 05/11] docs(blog): add missing meta values in Refine + shadcn data table post (#5905) --- documentation/blog/2024-03-19-ts-shadcn.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/documentation/blog/2024-03-19-ts-shadcn.md b/documentation/blog/2024-03-19-ts-shadcn.md index 119c7b8d7977..7d34bd2ab879 100644 --- a/documentation/blog/2024-03-19-ts-shadcn.md +++ b/documentation/blog/2024-03-19-ts-shadcn.md @@ -1693,6 +1693,14 @@ export const BlogPostList: React.FC = () => { }, }); + tableProps?.setOptions((prev) => ({ + ...prev, + meta: { + ...prev.meta, + categoryData, + }, + })); + return (
From b7ade8c540c7e446620751f27245a374f40a7ac6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ali=20Emir=20=C5=9Een?= Date: Tue, 30 Apr 2024 22:47:36 +0300 Subject: [PATCH 06/11] chore(react-hook-dynamic-form): update edit page to watch for array fields (#5903) --- .../src/pages/userEdit.tsx | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/examples/blog-react-hook-dynamic-form/src/pages/userEdit.tsx b/examples/blog-react-hook-dynamic-form/src/pages/userEdit.tsx index 8390407bc57f..33bb41fe942d 100644 --- a/examples/blog-react-hook-dynamic-form/src/pages/userEdit.tsx +++ b/examples/blog-react-hook-dynamic-form/src/pages/userEdit.tsx @@ -1,4 +1,3 @@ -import React from "react"; import { Edit } from "@refinedev/mui"; import Box from "@mui/material/Box"; import TextField from "@mui/material/TextField"; @@ -7,40 +6,42 @@ import DeleteIcon from "@mui/icons-material/Delete"; import { useForm } from "@refinedev/react-hook-form"; import { Controller, useFieldArray } from "react-hook-form"; +import { HttpError } from "@refinedev/core"; interface IPost { firstName: string; email: string; - skills: string; + skills: string[]; } const defaultValues = { firstName: "", email: "", - skills: "", + skills: [], }; -function PostEdit(Props: any) { +function PostEdit() { const { - refineCore: { onFinish }, saveButtonProps, control, formState: { errors }, - handleSubmit, watch, - } = useForm({ + } = useForm({ mode: "onChange", defaultValues, }); const { fields, append, remove } = useFieldArray({ control, + // @ts-expect-error - `useFieldArray` works without an `id` field in the array items but the type definition requires it name: "skills", rules: { required: "please add at least one skill", }, }); + const skills = watch("skills"); + return ( @@ -79,10 +80,10 @@ function PostEdit(Props: any) { /> )} /> - {fields.map(({ id }, index) => { + {skills.map((_, index) => { return ( ( Date: Thu, 2 May 2024 09:50:29 +0300 Subject: [PATCH 07/11] docs(blog): add nodemon post (#5901) --- documentation/blog/2024-04-30-nodemon.md | 154 +++++++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 documentation/blog/2024-04-30-nodemon.md diff --git a/documentation/blog/2024-04-30-nodemon.md b/documentation/blog/2024-04-30-nodemon.md new file mode 100644 index 000000000000..9141e466f683 --- /dev/null +++ b/documentation/blog/2024-04-30-nodemon.md @@ -0,0 +1,154 @@ +--- +title: How to Use Nodemon to Automatically Restart Node.js Applications +description: This guide shows you how to install and use Nodemon to streamline your development process. +slug: nodemon +authors: necati +tags: [dev-tools] +image: https://refine.ams3.cdn.digitaloceanspaces.com/blog/2024-04-30-nodemon/social.png +hide_table_of_contents: false +--- + +## Update: Enhanced Watch Mode in Node.js Version 22 + +One of the standout features in the latest release of Node.js, [version 22](https://nodejs.org/en/blog/announcements/v22-release-announce), is the improved watch mode. This feature is now considered stable, which means it's no longer experimental and can be relied upon for regular use. + +Watch mode simplifies developers' lives by automatically restarting the Node.js process whenever it detects changes in the files being monitored. This is particularly useful if you're working on large projects where manual restarts can be tedious and time-consuming. + +Previously, developers might have used tools like Nodemon and Watchman to manage file changes, especially on Windows, where detecting these changes could be challenging. Watch mode offers a more integrated and straightforward approach, eliminating the need for external tools and streamlining the development process. + +By using watch mode, you no longer need to manually restart your server or tools like Nodemon after making changes to your code. This saves time and allows you to see the effects of your changes immediately, making your development workflow much more efficient. + +## Introduction + +As a developer, you know how tedious it is to restart the application every time you tweak the code. Nodemon eliminates this hassle by automatically restarting the app whenever any change is detected. It's incredibly helpful as it lets you concentrate on coding without interruption. + +[Nodemon](https://nodemon.io/) watches the files in your project and instantly applies updates by restarting your app—no more manual resets. This has noticeably sped up my development process, making it smoother and more productive. It’s especially crucial in a fast-paced work environment where every second counts. + +## Installing Nodemon + +I just went through setting up Nodemon. You install it using npm, the Node.js package manager. Just open your terminal and type: + +```bash +npm install -g nodemon +``` + +This installs Nodemon globally on your system, so you can use it for any project. To make sure it's installed correctly, you can check its version with: + +```bash +nodemon --version +``` + +If it shows the version, you're all set! Nodemon will now automatically restart your Node.js applications whenever you make changes. It’s a real time-saver, making it smoother to develop without breaking your flow. + +## Using Nodemon + +Here's a simple Express.js application example using Nodemon: + +```javascript +// 1. First, install the Express.js and Nodemon modules +const express = require("express"); +const app = express(); + +// 2. Define a simple endpoint to listen for HTTP requests +app.get("/", (req, res) => { + res.send("Hello, World!"); +}); + +// 3. Start listening for the application on a specific port +const port = 3000; +app.listen(port, () => { + console.log(`Server successfully connected to port ${port}.`); +}); +``` + +This represents a basic Express.js application. The main file can be named `app.js`, for example. Now let's demonstrate how to use Nodemon to automatically restart this application. + +First, make sure you're in your project directory where your Node.js application, like an Express.js app, is located. + +1. Install Express.js and Nodemon if you haven’t already. + +```bash +npm install express nodemon +``` + +2. After installing Nodemon, using it to automatically restart your Node.js apps is simple. Navigate to the directory where your Node.js application is located using the terminal or command prompt. Once you're in the project directory, you can start your Node.js application with Nodemon by typing: + +```bash +nodemon app.js +``` + +This command fires up your application with Nodemon, so it automatically restarts whenever you change any files. For instance, if you update the response in your `app.js` from 'Hello, World!' to something else and save, Nodemon immediately restarts and applies the update without you needing to do anything. + +Also, here’s what you typically see in the terminal when you start: + +```tsx +[nodemon] 2.0.15 +[nodemon] to restart at any time, enter `rs` +[nodemon] watching path(s): *.* +[nodemon] watching extensions: js,json +[nodemon] starting `node app.js` +Server successfully connected to port 3000. +``` + +It shows Nodemon is watching all your files and is ready to restart anytime you make edits. + +In this output: + +- `[nodemon] 2.0.15`: This indicates the version of Nodemon being used. +- `[nodemon] to restart at any time, enter 'rs'`: This message informs you that you can manually restart the application at any time by entering 'rs' in the terminal. +- `[nodemon] watching path(s): *.*`: Nodemon is watching for changes in all files and directories. +- `[nodemon] watching extensions: js,json`: Nodemon is specifically watching for changes in JavaScript and JSON files. +- `[nodemon] starting 'node app.js'`: Nodemon is starting the Node.js application (`app.js` in this case). +- `Server successfully connected to port 3000.`: This message indicates that the Express.js server has successfully started and is listening on port 3000. + +## Advanced Configuration + +I’ve been diving deeper into Nodemon’s capabilities and found some cool ways to customize its behavior to better suit our projects. Nodemon allows you to tailor its restart rules according to our specific needs, which is super handy. + +Here’s a breakdown: + +1. **File Watch Patterns**: You can set Nodemon to watch only specific directories. This is great because it lets us focus on crucial parts of our project without getting distracted by unnecessary restarts. + + For instance, if you only want to watch the `src` and `config` directories, your config would look like this: + + ```json + { + "watch": ["src", "config"] + } + ``` + +2. **Ignore Patterns**: We can also tell Nodemon to ignore certain directories that don’t impact our app’s functionality, like log files or public assets. This means Nodemon won’t restart when changes occur in these areas. + + Here’s how you could set it up to ignore the `logs` and `public` directories: + + ```json + { + "ignore": ["logs", "public"] + } + ``` + +3. **Delaying Restarts**: If rapid successive changes are causing too many restarts, we can introduce a delay. This gives us a buffer after making changes before Nodemon restarts the app, helping stabilize our development environment. + + For adding a one-second delay, you’d configure it like this: + + ```json + { + "delay": 1000 + } + ``` + +These tweaks have made a noticeable difference in managing the development flow, especially in complex projects. I think implementing these could really streamline how we handle automatic restarts in our development process! + +## Conclusion + +As we wrap up discussing Nodemon, I wanted to share some common issues you might run into and how to troubleshoot them. Even though Nodemon simplifies our development by auto-restarting our apps, sometimes it might not work as expected. + +Here are a few things to check if you encounter problems: + +1. **Monitoring Files**: If Nodemon isn't restarting when you change files, double-check your configuration to ensure it’s watching the right files and directories. Sometimes, if we ignore too many files, Nodemon might miss changes in crucial areas. + +2. **Crashes or Non-starts**: Make sure there are no errors in your Node.js app and that all environment variables are set correctly. These can often stop Nodemon from running properly. + +3. **Permissions on Windows**: If you’re on Windows and facing permission issues, try running Nodemon in an administrator command prompt, which can resolve these problems. + +4. **Software Updates**: Lastly, keeping Nodemon and Node.js updated to their latest versions can solve a lot of known issues. From 6211195eb4bf804ce163c53a6beb459643916748 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Necati=20=C3=96zmen?= Date: Thu, 2 May 2024 09:50:50 +0300 Subject: [PATCH 08/11] docs(blog): add nest js post (#5897) --- documentation/blog/2024-04-29-nest-js.md | 344 +++++++++++++++++++++++ 1 file changed, 344 insertions(+) create mode 100644 documentation/blog/2024-04-29-nest-js.md diff --git a/documentation/blog/2024-04-29-nest-js.md b/documentation/blog/2024-04-29-nest-js.md new file mode 100644 index 000000000000..d34887b2582d --- /dev/null +++ b/documentation/blog/2024-04-29-nest-js.md @@ -0,0 +1,344 @@ +--- +title: Introduction to Nest JS +description: In this article, we will discuss how to create a simple web application using NestJS and its major components. +slug: nest-js +authors: muhammad_khabbab +tags: [dev-tools] +image: https://refine.ams3.cdn.digitaloceanspaces.com/blog/2024-04-29-nest-js/social-2.png +hide_table_of_contents: false +--- + +## Introduction to NestJS Framework + +Node.js is a popular environment for building complex and advanced applications. While working with Node.js on a complex application, it becomes difficult to maintain when scaling it. NestJS comes into play at this point! It is a Node.js server-side framework, which is built on the top of the typescript that allows you to efficiently build scalable and easily maintainable applications. Nest utilizes reliable HTTP Server frameworks such as Express by default, and it can be customized to use Fastify as well. + +Steps we'll cover in this article: + +- [Introduction to NestJS Framework](#introduction-to-nestjs-framework) +- [Why NestJS?](#why-nestjs) +- [Steps required for Installation and Setup of NestJS Project](#steps-required-for-installation-and-setup-of-nestjs-project) +- [How to create a simple web application using NestJS?](#how-to-create-a-simple-web-application-using-nestjs) + - [Major Components of Application](#major-components-of-application) + - [• Modules](#modules) + - [• Controllers](#controllers) + - [• Providers](#providers) +- [Step-by-Step Implementation of Components](#step-by-step-implementation-of-components) + - [• Step 1: Create a Module](#step-1-create-a-module) + - [• Step 2: Define Endpoints using Controllers](#step-2-define-endpoints-using-controllers) + - [• Step 3: Create Entity](#step-3-create-entity) + - [• Step 4: Encapsulate the business logic in Provider (Service)](#step-4-encapsulate-the-business-logic-in-provider-service) + - [• Step 5: Setup Modules](#step-5-setup-modules) + - [• Step 6: Run and Test Application](#step-6-run-and-test-application) + +## Why NestJS? + +NestJS follows a modular structure, which helps you keep your code organized, and maintainable, accelerate development, and increase efficiency. Due to its modular architecture, your team, whether working on small or large projects, can work simultaneously on different features without affecting other areas. Additionally, NestJS also provides native support for monolith as well as microservices architecture, including the integration of modern concepts and technologies such as GraphQL, CRONJobs, and WebSockets. + +It equips developers with a set of tools, such as a built-in validation system, to handle errors before they occur, enforcing validation rules and reducing the chance of errors. If you have prior experience with Angular, then NestJs is a perfect choice as it requires you to start with a small learning curve. Due to its extensive features, developers are attracted to it and are actively contributing, which helps in finding solutions for most use cases and cutting down development time. + +## Steps required for Installation and Setup of NestJS Project + +### • Pre-Requisites + +Before getting started with NestJS, you will be required to install: + +**1. NodeJS Environment** +**2. NPM(Node Package Manager)** + +You can download the installer from **[NodeJS Website](https://nodejs.org/en)**. It will install the package that includes both of the above listed items. + +
+ Snapshot of completing nodejs installation wizard +
+ +### • Installation + +1. Create a directory for your NestJS project and navigate to the desired location. +2. Install Nest CLI, which will help you initialize your application. To do that, run the following command: `npm i -g @nestjs/cli` +3. Verify the installation by typing and running “_nest_” command in your command line interface. + +
+ Snapshot of verifying nestcli installation +
+ +### • Project Setup +Create your NestJS project using NestJS Cli. We are creating the project in the current directory with the name `auto-garage-nest `by using the following command: + +`nest new auto-garage-nest` + +**Note:** _Choose `npm` as a package manager_ + +**The above command will create a basic project folder structure as below:** + +
+ Snapshot of basic project folder structure created through nest command +
+ +## How to create a simple web application using NestJS? + +### Major Components of Application + +#### • Modules + +By creating these modules, you are grouping services, controllers, or any other components. All the code for a single feature will be available by just importing the FeatureModule. That means a module can encapsulate the whole functionality into it, like having modules for authentication, logging, etc. + +#### • Controllers + +Controllers are the entry point of your application. They receive specific HTTP requests and send back the response to the client side. Each controller is connected to different routes and their specific HTTP methods (`GET`, `POST`, `PUT`, `DELETE`). + +#### • Providers + +The providers, also known as services, contain the business logic where the code interacts with the database, processes the data, or interacts with external APIs. A provider can create an instance of a service that contains the logic and can be reused in any other component. + +## Step-by-Step Implementation of Components + +### • Step 1: Create a Module + +When we create the NestJS project above, it will automatically create `app.module.ts`. AppModule is a root module that contains the reference of all modules. For effective arrangement of components, we will create a `vehicles` module to group vehicle management features for the auto garage. + +Using NestCli, we will navigate to the Nest project and then create a module with the following commands: + +`cd auto-garage-nest` + +`nest generate module vehicles` + +The above commands will create `vehicles.module.ts` in the `src/vehicles` folder and update `app.module.ts` by importing vehicles. + +```tsx +import { Module } from "@nestjs/common"; + +@Module({}) +export class VehiclesModule {} +``` + +### • Step 2: Define Endpoints using Controllers + +Now let’s create a container to define the endpoints for vehicle management. Run the following command: + +`nest generate controller vehicles` + +The above command will automatically create the `vehicles.controller.ts` file inside the `src/vehicles` folder. We will define the `GE`T,`POST`,`PUT`,`DELETE` endpoints in that file like below: + +```tsx +import { + Controller, + Get, + Post, + Put, + Delete, + Body, + Param, +} from "@nestjs/common"; +import { VehiclesService } from "./vehicles.service"; +import { Vehicle } from "./entities/vehicle.entity"; + +@Controller("vehicles") +export class VehiclesController { + constructor(private readonly vehiclesService: VehiclesService) {} + + @Get() + async findAll(): Promise { + return this.vehiclesService.findAll(); + } + + @Get(":id") + async findOne(@Param("id") id: string): Promise { + return this.vehiclesService.findOne(+id); + } + + @Post() + async create(@Body() vehicle: Vehicle): Promise { + return this.vehiclesService.create(vehicle); + } + + @Put(":id") + async update( + @Param("id") id: string, + @Body() vehicle: Vehicle, + ): Promise { + return this.vehiclesService.update(+id, vehicle); + } + + @Delete(":id") + async remove(@Param("id") id: string): Promise { + return this.vehiclesService.remove(+id); + } +} +``` + +### • Step 3: Create Entity + +In order to create structure in the database and to interact with it, we need to create entities. To do that, we will create `entities` directory in `src/vehicles` folder, and inside it, we will create a file with the name `vehicle.entity.ts` + +Inside the `vehicle.entity.ts` file, we will create an entity class and use the `typeORM` package to convert the entity to the table in the database. + +```tsx +import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"; + +@Entity() +export class Vehicle { + @PrimaryGeneratedColumn() + id: number; + + @Column() + make: string; + + @Column() + model: string; + + @Column() + year: number; + + @Column() + color: string; +} +``` + +**Note:** If you get an error on `typeorm` package, then install it using the command below: + +`npm install typeorm` + +To use the SQLite database with typeORM, install the package using the following command:' + +`npm install @nestjs/typeorm typeorm sqlite3` + +After installing the package, create `ormconfig.json` in your root config and add the configurations below: + +```tsx +{ + "type": "sqlite", + "database": "garage.sqlite", + "entities": ["dist/**/*.entity.js"], + "synchronize": true + } +``` + +### • Step 4: Encapsulate the business logic in Provider (Service) + +We will create the services to place our business logic that will be interacting directly with the database. To do that, inside `src/vehicles` folder where we have our controller and module, we will create a file with the name `vehicles.service.ts`. +Inside that file, we have placed the logic to: + +- Find all Vehicles +- Find specific vehicle +- Create a Vehicle +- Update Vehicle Details +- Remove a Vehicle + +After placing our logic, the service file for the vehicle will look like this: + +```tsx +import { Injectable } from "@nestjs/common"; +import { InjectRepository } from "@nestjs/typeorm"; +import { Repository, FindOneOptions } from "typeorm"; // Import FindOneOptions +import { Vehicle } from "./entities/vehicle.entity"; + +@Injectable() +export class VehiclesService { + constructor( + @InjectRepository(Vehicle) + private vehicleRepository: Repository, + ) {} + + async findAll(): Promise { + return this.vehicleRepository.find(); + } + + async findOne(id: number): Promise { + const options: FindOneOptions = { where: { id } }; // Construct FindOneOptions + return this.vehicleRepository.findOne(options); + } + + async create(vehicle: Vehicle): Promise { + return this.vehicleRepository.save(vehicle); + } + + async update(id: number, vehicle: Vehicle): Promise { + await this.vehicleRepository.update(id, vehicle); + const options: FindOneOptions = { where: { id } }; + return this.vehicleRepository.findOne(options); + } + + async remove(id: number): Promise { + await this.vehicleRepository.delete(id); + } +} +``` + +**Note:** If you face an error while importing `@nestjs/typeorm`, which is a NestJS-specific module that provides integration utilities for using TypeORM within NestJS applications. Use the following command to fix this error: + +`npm install @nestjs/typeorm ` + +### • Step 5: Setup Modules + +In the last step, we will place all components of the vehicle feature into the module. The controller was automatically added as it was created through the command. We will add other components such as TypeORM, entities, and provide services in `vehicles.module.ts` file. It will look like this: + +```tsx +import { Module } from "@nestjs/common"; +import { TypeOrmModule } from "@nestjs/typeorm"; +import { Vehicle } from "./entities/vehicle.entity"; +import { VehiclesController } from "./vehicles.controller"; +import { VehiclesService } from "./vehicles.service"; + +@Module({ + imports: [TypeOrmModule.forFeature([Vehicle])], + controllers: [VehiclesController], + providers: [VehiclesService], +}) +export class VehiclesModule {} +``` + +Add `TypeOrmModule.forRoot()` in `app.module.ts` to integrate TypeORM and create a database connection. + +```tsx +import { Module } from "@nestjs/common"; +import { AppController } from "./app.controller"; +import { AppService } from "./app.service"; +import { VehiclesModule } from "./vehicles/vehicles.module"; +import { TypeOrmModule } from "@nestjs/typeorm"; +import { Vehicle } from "./vehicles/entities/vehicle.entity"; + +@Module({ + imports: [ + TypeOrmModule.forRoot({ + type: "sqlite", + database: "garage.sqlite", + entities: [Vehicle], + synchronize: true, + }), + VehiclesModule, + ], + controllers: [AppController], + providers: [AppService], +}) +export class AppModule {} +``` + +### • Step 6: Run and Test Application + +Once we have setup all components, we will run our NestJS application by running the following command: + +`npm run start:dev` + +**_The application will be running at_** http://localhost:3000/ + +**We can test the CRUD operation we created by making HTTP calls through tools like Postman etc. See the example below:** + +
+ Snapshot of making POST API call through postman +
+ +
+ Snapshot of getting the list of inserted vehicle item into the database through GET API call +
+ +## Bonus: Refine’s Support for NestJS + +Finally, as a pleasant bonus, Refine has excellent support for NestJS. More specifically, Refine has data providers for APIs implemented with [Nest.js CRUD](https://refine.dev/docs/data/packages/nestjsx-crud) and [Nest.js Query](https://refine.dev/docs/data/packages/nestjs-query/) as well. In case you do not know, Nest.js CRUD is a module to help you simplify creating CRUD RESTful APIs. With Refine’s data provider, you can connect your NestJS CRUD APIs to Refine and manage your data efficiently. + +Similarly, you can also integrate your APIs with Refine using Refine’s Nest.js Query provider. With the help of Node.js powered GraphQL + TypeScript, you can make more efficient and independent APIs and connect them to Refine easily. + +## Conclusion + +In this article, we have discussed NestJS, which is a powerful framework for building server-side applications. First, we understood the "what and why" of NestJS then we created a simple NestJS application step-by-step where we handled HTTP calls, applied business logic, interacted with the database, and provided the responses. + +Overall, we have gained knowledge about the major components of any Nest application and the benefits each one offers. Although there is still much to learn about using NestJS to develop enterprise-ready applications but in this article, we have been able to cover the fundamental concepts that will help us get started in the right direction. From 0eb6110550290afc3141dd28fb799cc0a885253b Mon Sep 17 00:00:00 2001 From: Batuhan Wilhelm Date: Thu, 2 May 2024 11:17:06 +0300 Subject: [PATCH 09/11] chore: add default value to databaseId --- packages/appwrite/src/dataProvider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/appwrite/src/dataProvider.ts b/packages/appwrite/src/dataProvider.ts index 973af4fd7cec..362d15138142 100644 --- a/packages/appwrite/src/dataProvider.ts +++ b/packages/appwrite/src/dataProvider.ts @@ -21,7 +21,7 @@ export const dataProvider = ( }, ): Required => { const { - databaseId, + databaseId = "default", defaultReadPermissions = [Permission.read(Role.any())], defaultWritePermissions = [Permission.write(Role.any())], } = options; From debcbdcffd6ef5fc783ef221b046ea9d6cbcb946 Mon Sep 17 00:00:00 2001 From: Batuhan Wilhelm Date: Thu, 2 May 2024 11:32:53 +0300 Subject: [PATCH 10/11] chore: remove default options object and make options optional --- packages/appwrite/src/dataProvider.ts | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/packages/appwrite/src/dataProvider.ts b/packages/appwrite/src/dataProvider.ts index 362d15138142..27292386809b 100644 --- a/packages/appwrite/src/dataProvider.ts +++ b/packages/appwrite/src/dataProvider.ts @@ -6,7 +6,7 @@ import { getAppwriteSorting, } from "./utils"; -type DefaultOptions = { +type DataProviderOptions = { databaseId: string; defaultReadPermissions?: Permission[]; defaultWritePermissions?: Permission[]; @@ -14,17 +14,13 @@ type DefaultOptions = { export const dataProvider = ( appwriteClient: Appwrite, - options: DefaultOptions = { - databaseId: "default", - defaultReadPermissions: [], - defaultWritePermissions: [], - }, + options?: DataProviderOptions, ): Required => { const { databaseId = "default", defaultReadPermissions = [Permission.read(Role.any())], defaultWritePermissions = [Permission.write(Role.any())], - } = options; + } = options ?? {}; const database = new Databases(appwriteClient); From 0391c8c0b9c3649198d6be38c9d988eaf6fc780e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ali=20Emir=20=C5=9Een?= Date: Thu, 2 May 2024 11:46:13 +0300 Subject: [PATCH 11/11] chore: update changeset --- .changeset/tasty-garlics-notice.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.changeset/tasty-garlics-notice.md b/.changeset/tasty-garlics-notice.md index ebcc0cd6de71..895f7ff25348 100644 --- a/.changeset/tasty-garlics-notice.md +++ b/.changeset/tasty-garlics-notice.md @@ -2,4 +2,6 @@ "@refinedev/appwrite": minor --- +fix: add ability to customize default permission without explicitly passing them on each mutation + fixing an issue which didn't allow users to override the default `readPermissions` / `writePermissions` values `Role.any()`, by passing `defaultReadPermissions` / `defaultWritePermissions` OR by passing `meta?.readPermissions` / `meta?.writePermissions`.