


Lassen Sie uns darüber sprechen, wie Sie Nest.js verwenden, um eine Verbindung zur MongoDB-Datenbank im Knoten herzustellen
Wie verwende ich Nest.js, um eine Verbindung zur MongoDB-Datenbank in
node herzustellen? Der folgende Artikel stellt Ihnen vor, wie das Node-Framework Nest.js MongoDB verwendet. Ich hoffe, er wird Ihnen hilfreich sein!
Wenn Sie lernen, Nest mit einer Datenbank zu verbinden, werden Sie unweigerlich auf das Problem stoßen, eine Datenbank auszuwählen. Hier wählte der Autor MongoDB
, um die einfache Verwendung aufzuzeichnen. Sie können je nach Bedarf die entsprechende Datenbank auswählen. MongoDB
记录一下简单使用。 大家可以根据不同需求选择合适的数据库。
贴出跟进看的文档以方便大家进一步学习 Nest 中文文档 ,MongoDB菜鸟教程
数据库简介
MongoDB 是一个基于分布式文件存储的数据库。由 C++ 语言编写。旨在为 WEB 应用提供可扩展的高性能数据存储解决方案。
MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。
数据库选择
- 目前市面上有很多成熟的数据库可供大家选择。
- 据翻看各种资料作者这里得出的结论为大项目用
PostgreSql
小项目用MongoDB
所以作者准备一起学习下,这次因为想做一个小项目练练手所以先用MongoDB
看看怎么样。 - 大家有不同看法欢迎在评论区讨论。
配置基本服务
确保电脑已经安装了
MongoDB
没记得弄完做一下环境配置,可以开机自启, 也可以选择自己启动哈hhh看个人
Mongoose
简单介绍一下 ,
Mongoose
是一个操作MongoDB
的Nodejs
驱动库MongoDB
是数据库,Nodejs
是js的一个运行环境,Nodejs
不直接操作Mongodb
,这个时候就需要相应的驱动程序来提供接口。-
在 Nest 项目中安装一下依赖项,两种安装方式,自行选择
$ npm install --save @nestjs/mongoose mongoose // NPM 安装 $ yarn add @nestjs/mongoose mongoose // YARN 安装复制代码
-
安装完成后我们在 AppModule 文件中引入一下
/* app.module.ts */ import { Module } from '@nestjs/common'; import { AppController } from './app.controller'; import { AppService } from './app.service'; // 我自己准备的 USER 模块 import { UserModule } from './user/user.module'; // 引入 Mongoose import { MongooseModule } from '@nestjs/mongoose'; @Module({ // 用 forRoot 方法连接数据库 imports: [UserModule, MongooseModule.forRoot('mongodb://localhost/test')], controllers: [AppController], providers: [AppService], }) export class AppModule {}
基础功能模块
这里用一个 User 模块来做 demo
这里我理解的基础功能模块包括
module
(模块)Controller
(控制器)Service
(提供者)Schema
(数据模型) 我们主要是用Nest对
MongoDB
做增删改查 这几个模块目前暂时够用。对这几个模块做一些简单介绍:
由于我们上面已经对 app.module.ts 该根模块已经引入过了
mongoose
所以下面我们之间看一下功能模块是怎样的
Schema
在
Mongoose
中,一切都源于 Scheme,每个Schema
都会映射到MongoDB
的一个集合,并定义集合内文档的结构。Schema
被用来定义模型,而模型负责从底层创建和读取MongoDB
的文档。-
Veröffentlichen Sie die Folgedokumente, um das weitere Lernen zu erleichternNest Chinese-DokumentationSchema
可以用NestJS
内置的装饰器来创建,或者也可以自己动手使用Mongoose
的常规方式。使用装饰器来创建Schema
,MongoDB-Anfänger-Tutorial -
Datenbankeinführung
-
MongoDB ist eine verteilte Datenbank, die auf The basiert Datenbank, in der Dateien gespeichert sind. Geschrieben in der Sprache C++. Entwickelt, um skalierbare, leistungsstarke Datenspeicherlösungen für WEB-Anwendungen bereitzustellen.
🎜🎜🎜MongoDB ist ein Produkt zwischen einer relationalen Datenbank und einer nicht relationalen Datenbank. Es ist die funktionsreichste unter den nicht relationalen Datenbanken und ähnelt am ehesten einer relationalen Datenbank. 🎜🎜
Datenbankauswahl
- 🎜Es gibt viele ausgereifte Datenbanken auf dem Markt, aus denen Sie wählen können. 🎜
🎜
- 🎜Nach der Lektüre verschiedener Materialien kommt der Autor hier zu dem Schluss, dass große Projekte
PostgreSql
und kleine ProjekteMongoDB
verwenden, sodass der Autor eine Untersuchung plant Dieses Mal wollte ich ein kleines Projekt machen, um meine Fähigkeiten zu üben. Ich habe zuerstMongoDB
verwendet, um zu sehen, wie es geht. 🎜🎜Wenn Sie anderer Meinung sind, können Sie diese gerne im Kommentarbereich diskutieren. 🎜Grundlegende Dienste konfigurieren
- 🎜🎜Stellen Sie sicher, dass
MongoDB
auf Ihrem Computer installiert ist 🎜Denken Sie daran, es fertigzustellen. Sie können es nach dem Booten automatisch starten, oder Sie können es selbst starten. 🎜🎜Mongoose
- 🎜🎜Eine kurze Einführung:
Mongoose
ist eineNodejs
-Treiberbibliothek, dieMongoDB
betreibt🎜🎜🎜 🎜MongoDB
ist eine Datenbank,Nodejs
ist eine laufende Umgebung für js.Nodejs
betreibt nicht direkt Dieses Mal wird ein entsprechender Treiber benötigt, um eine Schnittstelle bereitzustellen. 🎜🎜🎜🎜Installieren Sie die Abhängigkeiten im Nest-Projekt. Es gibt zwei Installationsmethoden, die Sie selbst auswählen können.🎜/* user.schema.ts */ import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; // @Prop 装饰器接受一个可选的参数,通过这个,你可以指示这个属性是否是必须的,是否需要默认值,或者是标记它作为一个常量,下面是例子 // SchemaFactory 是 mongoose 内置的一个方法做用是读取模式文档 并创建 Schema 对象 import { Document } from 'mongoose'; export type UserDocument = User & Document; @Schema() export class User extends Document { @Prop() name: string; // 设置值为必填 @Prop({ required: true }) age: number; @Prop() height: number; } export const UserSchema = SchemaFactory.createForClass(User);
🎜🎜🎜Nachdem die Installation abgeschlossen ist, werden wir sie in die AppModule-Datei einführen -id="heading -6">Grundfunktionsmodul- 🎜🎜Ein Benutzermodul wird hier für die Demo verwendet🎜🎜🎜🎜Das Grundfunktionsmodul, das ich hier verstehe, enthält
module
( Modul)Controller
(Controller)Dienst
(Anbieter)Schema
(Datenmodell) Wir verwenden hauptsächlichNest to pair
MongoDB Diese Module zum Hinzufügen, Löschen, Ändern und Überprüfen sind derzeit ausreichend. 🎜🎜🎜🎜Eine kurze Einführung in diese Module:🎜🎜🎜
- 🎜🎜Da wir oben
mongoose
in das Stammmodul von app.module.ts eingeführt haben, werden wir uns ansehen, was Die Funktionsmodule sehen so aus: 🎜🎜Schema
- 🎜🎜In
Mongoose
ist alles From Scheme🎜, jedesSchema
wird einer Sammlung vonMongoDB
zugeordnet und definiert die Struktur des Dokumente innerhalb der Sammlung.Schema
wird zum Definieren des Modells verwendet, und das Modell ist für die Erstellung und das Lesen vonMongoDB
-Dokumenten von unten verantwortlich. 🎜🎜🎜🎜Schema
kann mit dem integrierten Dekorator vonNestJS
erstellt werden, oder Sie können es selbst mit Mongoose verwenden href="https ://link.juejin.cn?target=https%3A%2F%2Fmongoosejs.com%2Fdocs%2Fguide.html" target="_blank" rel="nofollow noopener noreferrer" title="https:// mongoosejs.com/docs/guide.html" ref="nofollow noopener noreferrer">Normaler Weg 🎜. Durch die Verwendung von Dekoratoren zum Erstellen vonSchema
werden Referenzen erheblich reduziert und die Lesbarkeit des Codes verbessert. Der Autor verwendet hier die offiziell empfohlene Methode, um es mit einem Dekorateur zu erstellen. Schließlich verwende ich Nest und darf nichts Besonderes verwenden, hhh. 🎜🎜🎜/* user.service.ts */ import { Model } from 'mongoose'; import { InjectModel } from '@nestjs/mongoose'; import { User, UserDocument } from 'src/schema/user.schema'; import { CreateUserDto } from './user.dto'; @Injectable() export class UserService { // 注册Schema后,可以使用 @InjectModel() 装饰器将 User 模型注入到 UserService 中: constructor(@InjectModel('User') private userTest: Model<UserDocument>) {} // 添加 async create(createUserDto: CreateUserDto): Promise<User> { const createUser = new this.userTest(createUserDto); const temp = await createUser.save(); return temp; } // 查找 async findAll(): Promise<User[]> { // 这里是异步的 const temp = await this.userTest.find().exec(); return temp; } // 查找 async findOne(name: string): Promise<User[]> { // 这里是异步的 const temp = await this.userTest.find({ name }); return temp; } // 删除 async delete(sid: number) { // 这里是异步的 remove 方法删除成功并返回相应的个数 const temp = await this.userTest.remove({ _id: sid }); return temp; } // 修改 async updateUser(sid: string, data: any) { // 这里是异步的 remove 方法删除成功并返回相应的个数 const temp = await this.userTest.updateOne({ _id: sid }, { $set: data }); return temp; } }
🎜🎜🎜 wird später zusammen mit anderen Funktionen im Modul eingeführt. 🎜Service
-
控制器的目的是接收应用的特定请求。路由机制控制哪个控制器接收哪些请求。通常,每个控制器有多个路由,不同的路由可以执行不同的操作。
/* user.service.ts */ import { Model } from 'mongoose'; import { InjectModel } from '@nestjs/mongoose'; import { User, UserDocument } from 'src/schema/user.schema'; import { CreateUserDto } from './user.dto'; @Injectable() export class UserService { // 注册Schema后,可以使用 @InjectModel() 装饰器将 User 模型注入到 UserService 中: constructor(@InjectModel('User') private userTest: Model<UserDocument>) {} // 添加 async create(createUserDto: CreateUserDto): Promise<User> { const createUser = new this.userTest(createUserDto); const temp = await createUser.save(); return temp; } // 查找 async findAll(): Promise<User[]> { // 这里是异步的 const temp = await this.userTest.find().exec(); return temp; } // 查找 async findOne(name: string): Promise<User[]> { // 这里是异步的 const temp = await this.userTest.find({ name }); return temp; } // 删除 async delete(sid: number) { // 这里是异步的 remove 方法删除成功并返回相应的个数 const temp = await this.userTest.remove({ _id: sid }); return temp; } // 修改 async updateUser(sid: string, data: any) { // 这里是异步的 remove 方法删除成功并返回相应的个数 const temp = await this.userTest.updateOne({ _id: sid }, { $set: data }); return temp; } }
等下和其他功能一起在 Module 中引入。
Controller
-
控制器的目的是接收应用的特定请求。路由机制控制哪个控制器接收哪些请求。通常,每个控制器有多个路由,不同的路由可以执行不同的操作。
/* user.controller.ts */ // 引入 Nest.js 内置的各个功能 import { Body, Controller, Delete, Get, Param, Post, Put, Query } from '@nestjs/common'; // 引入用户服务 import { UserService } from './user.service'; // 引入创建用户 DTO 用于限制从接口处传来的参数 import { CreateUserDto } from './user.dto'; // 配置局部路由 @Controller('user') export class UserController { constructor(private readonly userService: UserService) {} // 创建user路由 user/createUser @Post('createUser') async createUser(@Body() body: CreateUserDto) { return this.userService.create(body); } //查找所有 user 路由 @Get('findAll') async findAll() { return this.userService.findAll(); } // 查找某一个用户路由 @Get('findOne') async findOne(@Query() query: any) { return this.userService.findOne(query.name); } // 删除一个用户的路由 @Delete(':sid') deleteUser(@Param() param: any) { return this.userService.delete(param.sid); } // 更改用户信息的路由 @Put(':sid') updateUser(@Body() body: any, @Param() param: any) { return this.userService.updateUser(param.sid, body); } }
Moudle
模块是具有
@Module()
装饰器的类。@Module()
装饰器提供了元数据,Nest 用它来组织应用程序结构。-
我们把以上内容引入到我们的 User 模块中
/* user.module.ts */ import { Module } from '@nestjs/common'; import { UserController } from './user.controller'; import { UserService } from './user.service'; import { MongooseModule } from '@nestjs/mongoose'; import { UserSchema } from 'src/schema/user.schema'; @Module({ // MongooseModule提供了forFeature()方法来配置模块,包括定义哪些模型应该注册在当前范围中。 // 如果你还想在另外的模块中使用这个模型,将MongooseModule添加到CatsModule的exports部分并在其他模块中导入CatsModule。 // 这里的 name:'User' 为数据库表名称与 service 中注入的表名称对应两者不一样会报错 imports: [MongooseModule.forFeature([{ name: 'User', schema: UserSchema }])], controllers: [UserController], providers: [UserService], }) export class UserModule {}
- 以上我们的基础布局完成,可以进行接口检验了
接口检验
- 处理这些配置我们还在 main.ts 文件中配置了全局路由
app.setGlobalPrefix('api');
意思就是所有请求前面会有一个/api/
- 这里我们用的
PostMan
和MongoDB Compass
官方推荐的可视化工具查看效果
POST 增
这里我使用
POST
请求,路由为/api/user/createUser
因为要限制请求参数的数据类型所以这里方式为application/json
因为这里我们之前定义的 User 数据模型为 name,age,height, 所以请求里面只需要这几个参数即可,别的就算写进去也添加不到集合中
Postman
打开 MongoDB Compass 查看数据
可以看到我们已经添加到数据库中一条数据,接下来我们在添加两条,方便等会的查询/删除/更改操作
GET 查所有
这里我使用
GET
请求,,路由为/api/user/findAll
因为这里是查 User 集合内所有数据,所以不用添加请求参数-
Postman
打开 MongoDB Compass 查看数据
可以看到我们已经查询到数据库中刚才在
User
集合中添加的三条数据切记要点REFRESH
建不然软件不会自己刷新
GET 查单个用户
这里我使用
GET
请求,路由为/api/user/findOne
因为这里是查 User 集合内对应搜索条件的数据集合,这里我们用的是name 去查询的。也可以用唯一值 id 去查询。Postman
可以看到返回结果是一个集合,了解更多查询方式可以看下官网
PUT 改
这里我使用
PUT
请求,路由为/api/user/:sid
因为要限制请求参数的数据类型所以这里方式为application/json
因为这里我们之前定义的 User 数据模型为 age,height, 所以请求里面只需要这几个参数即可,别的就算写进去也添加不到集合中,我们这里传入数据库中小明的_id
61eea1b4144ea374a5b8455a
传入Param
中 ,然后把要修改的内容放入Body
中-
Postman
打开 MongoDB Compass 查看数据
可以看到我们已经把小明的年龄与身高做了修改
DELETE Löschen
Hier verwende ich
DELETE
zum Anfordern, und die Route ist/api/user/:sid
, weil ich den Datentyp von einschränken möchte die Anfrageparameter, also ist die Methode hierapplication/json
DELETE
请求,路由为/api/user/:sid
因为要限制请求参数的数据类型所以这里方式为application/json
我们这里传入数据库中小明的_id
61eea1b4144ea374a5b8455a
传入Param
中 ,并发起请求Postman
打开 MongoDB Compass 查看数据
可以看到小明的信息已经不存在了
总结
- 至此我们已经完成在
Nest.js
中使用 Mongoose 对MongoDB
数据的基础操作。并完成了在 Nest 中使用装饰器来创建 数据模型Schema
- Hier übergeben wir Xiao Mings _id
61eea1b4144ea374a5b8455a
anParam
und initiieren eine Anfrage - Postman
🎜🎜🎜🎜Sie können sehen, dass Xiao Mings Informationen nicht mehr vorhanden sind🎜🎜🎜
Zusammenfassung
🎜🎜Also Bisher haben wir die Verwendung des Ziels Nest.js abgeschlossen ="_blank" rel="nofollow noopener noreferrer" title="https://mongoosejs.com/docs/api.html#model_Model.find" ref="nofollow noopener noreferrer">Mongoose🎜 fürMongoDB
Grundlegende Operationen an Daten. Und fertig mit der Verwendung von Dekoratoren zum Erstellen von DatenmodellenSchema
in Nest. 🎜🎜Ein Blick auf die Dokumentation zeigt, dass Sie auch das in Nest integrierte 🎜TypeORM🎜 zum Erstellen von Modellen verwenden können. Interessierte Freunde können einen Blick darauf werfen. Wenn ich wieder andere Datenbankverbindungen lerne, werde ich es lesen und sehen, wie man es bedient. 🎜🎜Es gibt noch viel über Nest zu lernen, wie Pipelines, Middleware, Interceptors, Routing Guards usw. Ich habe vor, diese beim Schreiben kleiner Demos zu verwenden, um mein persönliches Verständnis zu vertiefen, da es sonst schwierig ist, es allein durch Lesen zu verstehen Ich werde hier nicht auf Details eingehen. Was ich bisher weiß, ist, dass die Verwendung von Pipelines zur Bestimmung von Anforderungstypen sehr nützlich ist. Interessierte Freunde können sich über 🎜🎜🎜🎜Weitere Informationen zu Knoten informieren Besuchen Sie: 🎜nodejs Tutorial 🎜! 🎜 -
Das obige ist der detaillierte Inhalt vonLassen Sie uns darüber sprechen, wie Sie Nest.js verwenden, um eine Verbindung zur MongoDB-Datenbank im Knoten herzustellen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Es ist für Entwickler wichtig, zu verstehen, wie die JavaScript -Engine intern funktioniert, da sie effizientere Code schreibt und Leistungs Engpässe und Optimierungsstrategien verstehen kann. 1) Der Workflow der Engine umfasst drei Phasen: Parsen, Kompilieren und Ausführung; 2) Während des Ausführungsprozesses führt die Engine dynamische Optimierung durch, wie z. B. Inline -Cache und versteckte Klassen. 3) Zu Best Practices gehören die Vermeidung globaler Variablen, die Optimierung von Schleifen, die Verwendung von const und lass und die Vermeidung übermäßiger Verwendung von Schließungen.

Python eignet sich besser für Anfänger mit einer reibungslosen Lernkurve und einer kurzen Syntax. JavaScript ist für die Front-End-Entwicklung mit einer steilen Lernkurve und einer flexiblen Syntax geeignet. 1. Python-Syntax ist intuitiv und für die Entwicklung von Datenwissenschaften und Back-End-Entwicklung geeignet. 2. JavaScript ist flexibel und in Front-End- und serverseitiger Programmierung weit verbreitet.

Python und JavaScript haben ihre eigenen Vor- und Nachteile in Bezug auf Gemeinschaft, Bibliotheken und Ressourcen. 1) Die Python-Community ist freundlich und für Anfänger geeignet, aber die Front-End-Entwicklungsressourcen sind nicht so reich wie JavaScript. 2) Python ist leistungsstark in Bibliotheken für Datenwissenschaft und maschinelles Lernen, während JavaScript in Bibliotheken und Front-End-Entwicklungsbibliotheken und Frameworks besser ist. 3) Beide haben reichhaltige Lernressourcen, aber Python eignet sich zum Beginn der offiziellen Dokumente, während JavaScript mit Mdnwebdocs besser ist. Die Wahl sollte auf Projektbedürfnissen und persönlichen Interessen beruhen.

Die Verschiebung von C/C zu JavaScript erfordert die Anpassung an dynamische Typisierung, Müllsammlung und asynchrone Programmierung. 1) C/C ist eine statisch typisierte Sprache, die eine manuelle Speicherverwaltung erfordert, während JavaScript dynamisch eingegeben und die Müllsammlung automatisch verarbeitet wird. 2) C/C muss in den Maschinencode kompiliert werden, während JavaScript eine interpretierte Sprache ist. 3) JavaScript führt Konzepte wie Verschlüsse, Prototypketten und Versprechen ein, die die Flexibilität und asynchrone Programmierfunktionen verbessern.

Unterschiedliche JavaScript -Motoren haben unterschiedliche Auswirkungen beim Analysieren und Ausführen von JavaScript -Code, da sich die Implementierungsprinzipien und Optimierungsstrategien jeder Engine unterscheiden. 1. Lexikalanalyse: Quellcode in die lexikalische Einheit umwandeln. 2. Grammatikanalyse: Erzeugen Sie einen abstrakten Syntaxbaum. 3. Optimierung und Kompilierung: Generieren Sie den Maschinencode über den JIT -Compiler. 4. Führen Sie aus: Führen Sie den Maschinencode aus. V8 Engine optimiert durch sofortige Kompilierung und versteckte Klasse.

Zu den Anwendungen von JavaScript in der realen Welt gehören die serverseitige Programmierung, die Entwicklung mobiler Anwendungen und das Internet der Dinge. Die serverseitige Programmierung wird über node.js realisiert, die für die hohe gleichzeitige Anfrageverarbeitung geeignet sind. 2. Die Entwicklung der mobilen Anwendungen erfolgt durch reaktnative und unterstützt die plattformübergreifende Bereitstellung. 3.. Wird für die Steuerung von IoT-Geräten über die Johnny-Five-Bibliothek verwendet, geeignet für Hardware-Interaktion.

Ich habe eine funktionale SaaS-Anwendung mit mehreren Mandanten (eine EdTech-App) mit Ihrem täglichen Tech-Tool erstellt und Sie können dasselbe tun. Was ist eine SaaS-Anwendung mit mehreren Mietern? Mit Multi-Tenant-SaaS-Anwendungen können Sie mehrere Kunden aus einem Sing bedienen

Dieser Artikel zeigt die Frontend -Integration mit einem Backend, das durch die Genehmigung gesichert ist und eine funktionale edtech SaaS -Anwendung unter Verwendung von Next.js. erstellt. Die Frontend erfasst Benutzerberechtigungen zur Steuerung der UI-Sichtbarkeit und stellt sicher, dass API-Anfragen die Rollenbasis einhalten


Heiße KI -Werkzeuge

Undresser.AI Undress
KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover
Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Undress AI Tool
Ausziehbilder kostenlos

Clothoff.io
KI-Kleiderentferner

AI Hentai Generator
Erstellen Sie kostenlos Ai Hentai.

Heißer Artikel

Heiße Werkzeuge

SecLists
SecLists ist der ultimative Begleiter für Sicherheitstester. Dabei handelt es sich um eine Sammlung verschiedener Arten von Listen, die häufig bei Sicherheitsbewertungen verwendet werden, an einem Ort. SecLists trägt dazu bei, Sicherheitstests effizienter und produktiver zu gestalten, indem es bequem alle Listen bereitstellt, die ein Sicherheitstester benötigen könnte. Zu den Listentypen gehören Benutzernamen, Passwörter, URLs, Fuzzing-Payloads, Muster für vertrauliche Daten, Web-Shells und mehr. Der Tester kann dieses Repository einfach auf einen neuen Testcomputer übertragen und hat dann Zugriff auf alle Arten von Listen, die er benötigt.

PHPStorm Mac-Version
Das neueste (2018.2.1) professionelle, integrierte PHP-Entwicklungstool

DVWA
Damn Vulnerable Web App (DVWA) ist eine PHP/MySQL-Webanwendung, die sehr anfällig ist. Seine Hauptziele bestehen darin, Sicherheitsexperten dabei zu helfen, ihre Fähigkeiten und Tools in einem rechtlichen Umfeld zu testen, Webentwicklern dabei zu helfen, den Prozess der Sicherung von Webanwendungen besser zu verstehen, und Lehrern/Schülern dabei zu helfen, in einer Unterrichtsumgebung Webanwendungen zu lehren/lernen Sicherheit. Das Ziel von DVWA besteht darin, einige der häufigsten Web-Schwachstellen über eine einfache und unkomplizierte Benutzeroberfläche mit unterschiedlichen Schwierigkeitsgraden zu üben. Bitte beachten Sie, dass diese Software

Dreamweaver Mac
Visuelle Webentwicklungstools

Dreamweaver CS6
Visuelle Webentwicklungstools