diff --git a/src/content/docs/de/4x/guide/writing-middleware.mdx b/src/content/docs/de/4x/guide/writing-middleware.mdx index 6a51a76ff7..7cff17d926 100644 --- a/src/content/docs/de/4x/guide/writing-middleware.mdx +++ b/src/content/docs/de/4x/guide/writing-middleware.mdx @@ -19,36 +19,7 @@ Wenn die aktuelle Middleware-Funktion den Request-Antwort-Zyklus nicht beendet, Die folgende Abbildung zeigt die Elemente eines Middleware-Funktionsaufrufs: -
- - - - - -
-Elemente eines Middleware Funktionsaufrufs - -
HTTP-Methode, für die die Middleware-Funktion gilt.
- -
- Pfad (Route), für den die Middleware-Funktion gilt. -
- -
- Die Middleware-Funktion. -
- -
- Callback-Argument an die Middleware-Funktion, genannt "Next" durch Konvention. -
- -
- HTTP [response](/api/response) argument to the middleware function, called "res" by convention. -
- -
HTTP [request](/api/request) argument to the middleware function, called "req" by convention.
-
-
+![Elemente einer Middleware-Funktion aufruf](/images/express-mw.png) Beginnend mit Express 5 ruft Middleware-Funktionen, die ein Versprechen zurückgeben, `next(value)` auf, wenn sie einen Fehler ablehnen oder werfen. `next` wird entweder mit dem abgelehnten Wert oder mit dem Wurffehler aufgerufen. diff --git a/src/content/docs/de/5x/guide/routing.mdx b/src/content/docs/de/5x/guide/routing.mdx index d042415b10..33320e873f 100644 --- a/src/content/docs/de/5x/guide/routing.mdx +++ b/src/content/docs/de/5x/guide/routing.mdx @@ -55,7 +55,7 @@ app.get('/', (req: Request, res: Response) => { ## Routenmethoden -Eine Route-Methode wird von einer der HTTP-Methoden abgeleitet und an eine Instanz der Klasse \`express angehängt. +Eine Route-Methode wird von einer der HTTP-Methoden abgeleitet und an eine Instanz der Klasse `express` angehängt. Der folgende Code ist ein Beispiel für Routen, die für die `GET` und die `POST` Methoden im Root der App definiert sind. @@ -153,7 +153,7 @@ app.get('/random.text', (req: Request, res: Response) => { }); ``` - + The characters `?`, `+`, `*`, `[]`, `()`, and `!` are reserved and cannot be used as literal characters in route paths, and braces are reserved for [optional segments](#optional-segments). Benutze `\`, um sie bei Bedarf zu maskieren. @@ -227,7 +227,7 @@ const sendParams = (req: Request<{ userId: string; bookId: string }>, res: Respo app.get('/users/:userId/books/:bookId', sendParams); ``` - + The name of route parameters must be a valid JavaScript identifier. Other names can be used by quoting them, for example `:"user-name"`. @@ -247,7 +247,7 @@ Request URL: http://localhost:3000/plantae/Prunus.persica req.params: { "genus": "Prunus", "species": "persica" } ``` - + Regexp characters are not supported inside string paths, so a parameter cannot be restricted with a suffix such as `:userId(\d+)`. Use an array of paths or a full regular expression instead. Weitere Informationen finden Sie in der Syntax [Pfadroute Match](/guide/migrating-5#path-route-matching-syntax). @@ -325,7 +325,7 @@ app.get('/:file{.:ext}', (req: Request, res: Response) => { The braces can also wrap a whole parameter to make it optional. Note that everything inside the braces is optional, so the position of the slash matters: ```js -app.get('/user/\{:id}', (req, res) => { +app.get('/user/{:id}', (req, res) => { // GET /user/42 => req.params = { id: '42' } // GET /user/ => req.params = {} // GET /user => 404, only the parameter is optional @@ -342,7 +342,7 @@ app.get('/order{/:id}', (req, res) => { ```ts import { type Request, type Response } from 'express'; -app.get('/user/\{:id}', (req: Request, res: Response) => { +app.get('/user/{:id}', (req: Request, res: Response) => { // GET /user/42 => req.params = { id: '42' } // GET /user/ => req.params = {} // GET /user => 404, only the parameter is optional @@ -356,7 +356,7 @@ app.get('/order{/:id}', (req: Request, res: Response) => { }); ``` -Verwechsle die Position des Schrägstrichs im Routenpfad nicht mit der [`strict routing`-Einstellung](/api/application/#application-settings), die sich auf die Anfrage-URL bezieht: Sie steuert, ob eine URL, die mit einem Schrägstrich endet, den der Routenpfad nicht verlangt, trotzdem übereinstimmt. Zum Beispiel stimmt eine Anfrage an `/order/` standardmäßig mit der Route `/order{/:id}` überein, gibt aber einen 404-Fehler zurück, wenn strict routing aktiviert ist; der abschließende Schrägstrich von `/user/` ist davon nicht betroffen, weil die Route `/user/\{:id}` ihn verlangt. Alle in den obigen Beispielen kommentierten Anfragen verhalten sich unabhängig von dieser Einstellung gleich. +Verwechsle die Position des Schrägstrichs im Routenpfad nicht mit der [`strict routing`-Einstellung](/api/application/#application-settings), die sich auf die Anfrage-URL bezieht: Sie steuert, ob eine URL, die mit einem Schrägstrich endet, den der Routenpfad nicht verlangt, trotzdem übereinstimmt. Zum Beispiel stimmt eine Anfrage an `/order/` standardmäßig mit der Route `/order{/:id}` überein, gibt aber einen 404-Fehler zurück, wenn strict routing aktiviert ist; der abschließende Schrägstrich von `/user/` ist davon nicht betroffen, weil die Route `/user/{:id}` ihn verlangt. Alle in den obigen Beispielen kommentierten Anfragen verhalten sich unabhängig von dieser Einstellung gleich. ## Routenhandler diff --git a/src/content/docs/es/4x/guide/writing-middleware.mdx b/src/content/docs/es/4x/guide/writing-middleware.mdx index ae6acc80cc..904ab4d46b 100644 --- a/src/content/docs/es/4x/guide/writing-middleware.mdx +++ b/src/content/docs/es/4x/guide/writing-middleware.mdx @@ -19,36 +19,7 @@ Si la función actual de middleware no termina el ciclo de solicitud de respuest La siguiente figura muestra los elementos de una llamada a una función de middleware: -
- - - - - -
-Elementos de una llamada a función middleware - -
método HTTP para el cual se aplica la función middleware.
- -
- Ruta (ruta) para la que se aplica la función middleware. -
- -
- La función Middleware. -
- -
- Argumento de llamada a la función middleware, llamado "siguiente" por convención. -
- -
- HTTP [response](/api/response) argument to the middleware function, called "res" by convention. -
- -
HTTP [request](/api/request) argument to the middleware function, called "req" by convention.
-
-
+![Elementos de una llamada a función middleware](/images/express-mw.png) A partir de Express 5, las funciones de middleware que devuelven una Promise llamarán `next(value)` cuando rechacen o lancen un error. `next` será llamado con el valor rechazado o con el error arrojado. diff --git a/src/content/docs/es/5x/guide/routing.mdx b/src/content/docs/es/5x/guide/routing.mdx index e33451039b..0c44ae5b0c 100644 --- a/src/content/docs/es/5x/guide/routing.mdx +++ b/src/content/docs/es/5x/guide/routing.mdx @@ -153,7 +153,7 @@ app.get('/random.text', (req: Request, res: Response) => { }); ``` - + The characters `?`, `+`, `*`, `[]`, `()`, and `!` are reserved and cannot be used as literal characters in route paths, and braces are reserved for [optional segments](#optional-segments). Usa `\` para escapar de ellos si es necesario. @@ -227,7 +227,7 @@ const sendParams = (req: Request<{ userId: string; bookId: string }>, res: Respo app.get('/users/:userId/books/:bookId', sendParams); ``` - + The name of route parameters must be a valid JavaScript identifier. Other names can be used by quoting them, for example `:"user-name"`. @@ -247,7 +247,7 @@ Request URL: http://localhost:3000/plantae/Prunus.persica req.params: { "genus": "Prunus", "species": "persica" } ``` - + Regexp characters are not supported inside string paths, so a parameter cannot be restricted with a suffix such as `:userId(\d+)`. Use an array of paths or a full regular expression instead. Vea la [sintaxis de coincidencia de ruta de ruta](/guide/migrating-5#path-syntax) para más información. @@ -325,7 +325,7 @@ app.get('/:file{.:ext}', (req: Request, res: Response) => { The braces can also wrap a whole parameter to make it optional. Note that everything inside the braces is optional, so the position of the slash matters: ```js -app.get('/user/\{:id}', (req, res) => { +app.get('/user/{:id}', (req, res) => { // GET /user/42 => req.params = { id: '42' } // GET /user/ => req.params = {} // GET /user => 404, only the parameter is optional @@ -342,7 +342,7 @@ app.get('/order{/:id}', (req, res) => { ```ts import { type Request, type Response } from 'express'; -app.get('/user/\{:id}', (req: Request, res: Response) => { +app.get('/user/{:id}', (req: Request, res: Response) => { // GET /user/42 => req.params = { id: '42' } // GET /user/ => req.params = {} // GET /user => 404, only the parameter is optional @@ -356,7 +356,7 @@ app.get('/order{/:id}', (req: Request, res: Response) => { }); ``` -No confundas la posición de la barra en la ruta con la [configuración `strict routing`](/api/application/#application-settings), que trata sobre la URL de la solicitud: controla si una URL que termina en barra sigue coincidiendo cuando la ruta no la exige. Por ejemplo, una solicitud a `/order/` coincide con la ruta `/order{/:id}` por defecto, pero devuelve un error 404 cuando strict routing está habilitado; la barra final de `/user/` no se ve afectada porque la ruta `/user/\{:id}` la exige. Todas las solicitudes comentadas en los ejemplos anteriores se comportan igual independientemente de esa configuración. +No confundas la posición de la barra en la ruta con la [configuración `strict routing`](/api/application/#application-settings), que trata sobre la URL de la solicitud: controla si una URL que termina en barra sigue coincidiendo cuando la ruta no la exige. Por ejemplo, una solicitud a `/order/` coincide con la ruta `/order{/:id}` por defecto, pero devuelve un error 404 cuando strict routing está habilitado; la barra final de `/user/` no se ve afectada porque la ruta `/user/{:id}` la exige. Todas las solicitudes comentadas en los ejemplos anteriores se comportan igual independientemente de esa configuración. ## Manejadores de rutas diff --git a/src/content/docs/fr/4x/guide/writing-middleware.mdx b/src/content/docs/fr/4x/guide/writing-middleware.mdx index e1a489f638..6bb81f0b1f 100644 --- a/src/content/docs/fr/4x/guide/writing-middleware.mdx +++ b/src/content/docs/fr/4x/guide/writing-middleware.mdx @@ -19,36 +19,7 @@ Si la fonction middleware actuelle ne met pas fin au cycle de réponse de requê La figure suivante montre les éléments d'un appel de fonction middleware : -
- - - - - -
-Éléments d'un appel de fonction middleware - -
méthode HTTP pour laquelle la fonction middleware s'applique.
- -
- Chemin (route) pour lequel la fonction middleware s'applique. -
- -
- La fonction middleware. -
- -
- Argument de rappel à la fonction middleware, appelé "next" par convention. -
- -
- HTTP [response](/api/response) argument to the middleware function, called "res" by convention. -
- -
HTTP [request](/api/request) argument to the middleware function, called "req" by convention.
-
-
+![Elements of a middleware function call](/images/express-mw.png) À partir de Express 5, les fonctions middleware qui retournent une Promise appelleront `next(value)` quand elles rejettent ou lancent une erreur. `next` sera appelé avec la valeur rejetée ou avec l'erreur levée. diff --git a/src/content/docs/fr/5x/guide/routing.mdx b/src/content/docs/fr/5x/guide/routing.mdx index f0f9faccbe..d6bcd7c173 100644 --- a/src/content/docs/fr/5x/guide/routing.mdx +++ b/src/content/docs/fr/5x/guide/routing.mdx @@ -110,7 +110,7 @@ app.all('/secret', (req: Request, res: Response, next: NextFunction) => { Les chemins de la route, en combinaison avec une méthode de requête, définissent les points de terminaison à partir desquels les requêtes peuvent être faites. Les chemins de route peuvent être des chaînes de caractères ou des expressions régulières. They can also capture values from the URL, as described in [Route parameters](#route-parameters) below. - + Express utilise [path-to-regexp](https://www.npmjs.com/package/path-to-regexp) v8 pour correspondre aux chemins de route ; consultez la documentation path-to-regexp pour toutes les possibilités dans la définition des chemins de route. [Express Playground Router](https://bjohansebas.github.io/playground-router/) est un outil pratique pour tester les routes Express de base, bien qu'il ne supporte pas la recherche de patterns. @@ -153,7 +153,7 @@ app.get('/random.text', (req: Request, res: Response) => { }); ``` - + The characters `?`, `+`, `*`, `[]`, `()`, and `!` are reserved and cannot be used as literal characters in route paths, and braces are reserved for [optional segments](#optional-segments). Utilisez `\` pour les échapper si nécessaire. @@ -227,7 +227,7 @@ const sendParams = (req: Request<{ userId: string; bookId: string }>, res: Respo app.get('/users/:userId/books/:bookId', sendParams); ``` - + The name of route parameters must be a valid JavaScript identifier. Other names can be used by quoting them, for example `:"user-name"`. @@ -247,7 +247,7 @@ Request URL: http://localhost:3000/plantae/Prunus.persica req.params: { "genus": "Prunus", "species": "persica" } ``` - + Regexp characters are not supported inside string paths, so a parameter cannot be restricted with a suffix such as `:userId(\d+)`. Use an array of paths or a full regular expression instead. Voir la [syntaxe correspondante à la route des chemins](/en/guide/migrating-5#path-syntax) pour plus d'informations. @@ -325,7 +325,7 @@ app.get('/:file{.:ext}', (req: Request, res: Response) => { The braces can also wrap a whole parameter to make it optional. Note that everything inside the braces is optional, so the position of the slash matters: ```js -app.get('/user/\{:id}', (req, res) => { +app.get('/user/{:id}', (req, res) => { // GET /user/42 => req.params = { id: '42' } // GET /user/ => req.params = {} // GET /user => 404, only the parameter is optional @@ -342,7 +342,7 @@ app.get('/order{/:id}', (req, res) => { ```ts import { type Request, type Response } from 'express'; -app.get('/user/\{:id}', (req: Request, res: Response) => { +app.get('/user/{:id}', (req: Request, res: Response) => { // GET /user/42 => req.params = { id: '42' } // GET /user/ => req.params = {} // GET /user => 404, only the parameter is optional @@ -356,7 +356,7 @@ app.get('/order{/:id}', (req: Request, res: Response) => { }); ``` -Ne confondez pas la position de la barre oblique dans le chemin de la route avec le [paramètre `strict routing`](/api/application/#application-settings), qui concerne l'URL de la requête : il contrôle si une URL se terminant par une barre oblique que le chemin de la route n'exige pas correspond quand même. Par exemple, une requête vers `/order/` correspond par défaut à la route `/order{/:id}`, mais renvoie une erreur 404 lorsque strict routing est activé ; la barre oblique finale de `/user/` n'est pas concernée car la route `/user/\{:id}` l'exige. Toutes les requêtes commentées dans les exemples ci-dessus se comportent de la même manière quel que soit ce paramètre. +Ne confondez pas la position de la barre oblique dans le chemin de la route avec le [paramètre `strict routing`](/api/application/#application-settings), qui concerne l'URL de la requête : il contrôle si une URL se terminant par une barre oblique que le chemin de la route n'exige pas correspond quand même. Par exemple, une requête vers `/order/` correspond par défaut à la route `/order{/:id}`, mais renvoie une erreur 404 lorsque strict routing est activé ; la barre oblique finale de `/user/` n'est pas concernée car la route `/user/{:id}` l'exige. Toutes les requêtes commentées dans les exemples ci-dessus se comportent de la même manière quel que soit ce paramètre. ## Gestionnaires de routes diff --git a/src/content/docs/it/4x/guide/writing-middleware.mdx b/src/content/docs/it/4x/guide/writing-middleware.mdx index b7beae09e9..7c12a2df3d 100644 --- a/src/content/docs/it/4x/guide/writing-middleware.mdx +++ b/src/content/docs/it/4x/guide/writing-middleware.mdx @@ -19,36 +19,7 @@ Se la funzione middleware corrente non termina il ciclo richiesta-risposta, deve La figura seguente mostra gli elementi di una chiamata di una funzione middleware: -
- - - - - -
-Elementi di una chiamata a funzione middleware - -Metodo
HTTP per il quale si applica la funzione middleware.
- -
- Percorso (percorso) per il quale si applica la funzione middleware. -
- -
- La funzione middleware. -
- -
- Argomento di richiamo alla funzione middleware, chiamato "next" per convenzione. -
- -
- HTTP [response](/api/response) argument to the middleware function, called "res" by convention. -
- -
HTTP [request](/api/request) argument to the middleware function, called "req" by convention.
-
-
+![Elementi di una chiamata di una funzione middleware](/images/express-mw.png) A partire da Express 5, le funzioni middleware che restituiscono una Promise chiameranno `next(value)` quando rifiutano o lanciano un errore. `next` verrà chiamato con il valore rifiutato o con l'errore lanciato. diff --git a/src/content/docs/it/5x/guide/routing.mdx b/src/content/docs/it/5x/guide/routing.mdx index 28d82036b0..2e210c2d05 100644 --- a/src/content/docs/it/5x/guide/routing.mdx +++ b/src/content/docs/it/5x/guide/routing.mdx @@ -153,7 +153,7 @@ app.get('/random.text', (req: Request, res: Response) => { }); ``` - + The characters `?`, `+`, `*`, `[]`, `()`, and `!` are reserved and cannot be used as literal characters in route paths, and braces are reserved for [optional segments](#optional-segments). Usa `\` per sfuggire se necessario. @@ -227,7 +227,7 @@ const sendParams = (req: Request<{ userId: string; bookId: string }>, res: Respo app.get('/users/:userId/books/:bookId', sendParams); ``` - + The name of route parameters must be a valid JavaScript identifier. Other names can be used by quoting them, for example `:"user-name"`. @@ -247,7 +247,7 @@ Request URL: http://localhost:3000/plantae/Prunus.persica req.params: { "genus": "Prunus", "species": "persica" } ``` - + Regexp characters are not supported inside string paths, so a parameter cannot be restricted with a suffix such as `:userId(\d+)`. Use an array of paths or a full regular expression instead. Per maggiori informazioni consultare il [percorso percorso corrispondente alla sintassi](/guide/migrating-5#path-syntax). @@ -325,7 +325,7 @@ app.get('/:file{.:ext}', (req: Request, res: Response) => { The braces can also wrap a whole parameter to make it optional. Note that everything inside the braces is optional, so the position of the slash matters: ```js -app.get('/user/\{:id}', (req, res) => { +app.get('/user/{:id}', (req, res) => { // GET /user/42 => req.params = { id: '42' } // GET /user/ => req.params = {} // GET /user => 404, only the parameter is optional @@ -342,7 +342,7 @@ app.get('/order{/:id}', (req, res) => { ```ts import { type Request, type Response } from 'express'; -app.get('/user/\{:id}', (req: Request, res: Response) => { +app.get('/user/{:id}', (req: Request, res: Response) => { // GET /user/42 => req.params = { id: '42' } // GET /user/ => req.params = {} // GET /user => 404, only the parameter is optional @@ -356,7 +356,7 @@ app.get('/order{/:id}', (req: Request, res: Response) => { }); ``` -Non confondere la posizione della barra nel percorso della rotta con l'[impostazione `strict routing`](/api/application/#application-settings), che riguarda l'URL della richiesta: controlla se un URL che termina con una barra non richiesta dal percorso della rotta corrisponde comunque. Ad esempio, una richiesta a `/order/` corrisponde per impostazione predefinita alla rotta `/order{/:id}`, ma restituisce un errore 404 quando strict routing è abilitato; la barra finale di `/user/` non è interessata perché la rotta `/user/\{:id}` la richiede. Tutte le richieste commentate negli esempi precedenti si comportano allo stesso modo indipendentemente da questa impostazione. +Non confondere la posizione della barra nel percorso della rotta con l'[impostazione `strict routing`](/api/application/#application-settings), che riguarda l'URL della richiesta: controlla se un URL che termina con una barra non richiesta dal percorso della rotta corrisponde comunque. Ad esempio, una richiesta a `/order/` corrisponde per impostazione predefinita alla rotta `/order{/:id}`, ma restituisce un errore 404 quando strict routing è abilitato; la barra finale di `/user/` non è interessata perché la rotta `/user/{:id}` la richiede. Tutte le richieste commentate negli esempi precedenti si comportano allo stesso modo indipendentemente da questa impostazione. ## Gestori del percorso diff --git a/src/content/docs/ja/4x/guide/routing.mdx b/src/content/docs/ja/4x/guide/routing.mdx index 1a4b2a071c..8ad1319d43 100644 --- a/src/content/docs/ja/4x/guide/routing.mdx +++ b/src/content/docs/ja/4x/guide/routing.mdx @@ -10,8 +10,8 @@ For an introduction to routing, see [Basic routing](/starter/basic-routing). For an introduction to routing, see [Basic routing](/starter/basic-routing). HTTPメソッドに対応するExpress `app` オブジェクトのメソッドを使用してルーティングを定義します。 -のように、`app。 POST リクエストを処理する GET リクエストと `app.post\` を処理します。 For a full list, -see [app.METHOD](/api/application#appmethod). You can also use [app.all()](/api/application#appall) to handle all HTTP methods and [app.use()](/api/application#appuse) to +のように、`app。 POST リクエストを処理する GET リクエストと `app.post\` を処理します。 You can also use [app.all()](/api/application#appall) to handle all HTTP methods and [app.use()](/api/application#appuse) to +specify middleware as the callback function (See [Using middleware](/guide/using-middleware) for details). You can also use [app.all()](/api/application#appall) to handle all HTTP methods and [app.use()](/api/application#appuse) to specify middleware as the callback function (See [Using middleware](/guide/using-middleware) for details). 言い換えれば、アプリケーションは指定されたルートとメソッドに一致するリクエストを「リッスン」します。 マッチを検出すると、指定されたコールバック関数を呼び出します。 These routing methods specify a callback function (sometimes called a "handler function") that Express automatically runs when the application receives a request matching the specified route (endpoint) and HTTP method. diff --git a/src/content/docs/ja/4x/guide/writing-middleware.mdx b/src/content/docs/ja/4x/guide/writing-middleware.mdx index d1944dedc1..c0bb6e18e1 100644 --- a/src/content/docs/ja/4x/guide/writing-middleware.mdx +++ b/src/content/docs/ja/4x/guide/writing-middleware.mdx @@ -19,36 +19,7 @@ _Middleware_ 関数は、[request object](/api#req) (`req`) にアクセスで 次の図はミドルウェア関数呼び出しの要素を示しています。 -
- - - - - -
-ミドルウェア関数呼び出しの要素 - -
ミドルウェア関数が適用されるHTTPメソッド。
- -
- ミドルウェア関数が適用されるパス(ルート)。 -
- -
- ミドルウェア関数。 -
- -
- 規約ごとに「next」と呼ばれるミドルウェア関数へのコールバック引数。 -
- -
- HTTP [response](/api/response) argument to the middleware function, called "res" by convention. -
- -
HTTP [request](/api/request) argument to the middleware function, called "req" by convention.
-
-
+![Elements of a middleware function call](/images/express-mw.png) Express 5以降、Promiseを返すミドルウェア関数はエラーの拒否またはスロー時に「next(value)」を呼び出します。 `next`は、拒否された値またはスローされたエラーで呼び出されます。 `next`は、拒否された値またはスローされたエラーで呼び出されます。 diff --git a/src/content/docs/ja/5x/guide/routing.mdx b/src/content/docs/ja/5x/guide/routing.mdx index 767a3e229c..446c31cdbd 100644 --- a/src/content/docs/ja/5x/guide/routing.mdx +++ b/src/content/docs/ja/5x/guide/routing.mdx @@ -10,8 +10,8 @@ For an introduction to routing, see [Basic routing](/starter/basic-routing). For an introduction to routing, see [Basic routing](/starter/basic-routing). HTTPメソッドに対応するExpress `app` オブジェクトのメソッドを使用してルーティングを定義します。 -のように、`app。 POST リクエストを処理する GET リクエストと `app.post\` を処理します。 For a full list, -see [app.METHOD](/api/application#appmethod). You can also use [app.all()](/api/application#appall) to handle all HTTP methods and [app.use()](/api/application#appuse) to +のように、`app。 POST リクエストを処理する GET リクエストと `app.post\` を処理します。 You can also use [app.all()](/api/application#appall) to handle all HTTP methods and [app.use()](/api/application#appuse) to +specify middleware as the callback function (See [Using middleware](/guide/using-middleware) for details). You can also use [app.all()](/api/application#appall) to handle all HTTP methods and [app.use()](/api/application#appuse) to specify middleware as the callback function (See [Using middleware](/guide/using-middleware) for details). 言い換えれば、アプリケーションは指定されたルートとメソッドに一致するリクエストを「リッスン」します。 マッチを検出すると、指定されたコールバック関数を呼び出します。 These routing methods specify a callback function (sometimes called a "handler function") that Express automatically runs when the application receives a request matching the specified route (endpoint) and HTTP method. @@ -87,13 +87,11 @@ app.post('/', (req: Request, res: Response) => { }); ``` -HTTPメソッドに対応するExpress `app` オブジェクトのメソッドを使用してルーティングを定義します。 -のように、`app。 POST リクエストを処理する GET リクエストと `app.post\` を処理します。 For a full list, -see [app.METHOD](/api/application#appmethod). +Expressは、`get`、`post`など、すべてのHTTPリクエストメソッドに対応するメソッドをサポートしています。 You can also use [app.all()](/api/application#appall) to handle all HTTP methods and [app.use()](/api/application#appuse) to specify middleware as the callback function (See [Using middleware](/guide/using-middleware) for details). -特別なルーティングメソッド`app.all()`があり、\_all_HTTPリクエストメソッドのパスにミドルウェア関数をロードするために使用されます。 例えば、`GET`を使用しているかどうかに関わらず、ルート`"/secret"へのリクエストに対して以下のハンドラが実行されます。 `POST`、`PUT`、`DELETE\`、または[http module](https://nodejs.org/api/http.html#http_http_methods)でサポートされている他のHTTPリクエストメソッド。 For example, the following handler is executed for requests to the route `"/secret"` whether using `GET`, `QUERY`, `POST`, `PUT`, `DELETE`, or any other HTTP request method supported in the [http module](https://nodejs.org/api/http.html#http_http_methods). For example, the following handler is executed for requests to the route `"/secret"` whether using `GET`, `QUERY`, `POST`, `PUT`, `DELETE`, or any other HTTP request method supported in the [http module](https://nodejs.org/api/http.html#httpmethods). +特別なルーティングメソッド`app.all()`は、すべてのHTTPリクエストメソッドに対して、指定したパスでミドルウェア関数を読み込むために使用されます。 For example, the following handler is executed for requests to the route `"/secret"` whether using `GET`, `QUERY`, `POST`, `PUT`, `DELETE`, or any other HTTP request method supported in the [http module](https://nodejs.org/api/http.html#httpmethods). ```js app.all('/secret', (req, res, next) => { @@ -115,7 +113,7 @@ app.all('/secret', (req: Request, res: Response, next: NextFunction) => { ルートパスはリクエストメソッドと組み合わせて、リクエストを作成できるエンドポイントを定義します。 ルートパスは文字列または正規表現にすることができます。 Route paths can be strings or regular expressions. Route paths can be strings or regular expressions. They can also capture values from the URL, as described in [Route parameters](#route-parameters) below. - + Express はルートパスに一致する [path-to-regexp](https://www.npmjs.com/package/path-to-regexp) v8 を使用します。ルートパスの定義におけるすべての可能性については、path-to-regexp ドキュメントを参照してください。 [Express Playground Router](https://bjohansebas.github.io/playground-router/)は、パターンマッチングをサポートしていませんが、基本的なExpressルートをテストするための便利なツールです。 @@ -160,7 +158,7 @@ app.get('/random.text', (req: Request, res: Response) => { }); ``` - + The characters `?`, `+`, `*`, `[]`, `()`, and `!` are reserved and cannot be used as literal characters in route paths, and braces are reserved for [optional segments](#optional-segments). Use `\` to escape them if needed. @@ -234,7 +232,7 @@ const sendParams = (req: Request<{ userId: string; bookId: string }>, res: Respo app.get('/users/:userId/books/:bookId', sendParams); ``` - + The name of route parameters must be a valid JavaScript identifier. Other names can be used by quoting them, for example `:"user-name"`. @@ -254,7 +252,7 @@ Request URL: http://localhost:3000/plantae/Prunus.persica req.params: { "genus": "Prunus", "species": "persica" } ``` - + Regexp characters are not supported inside string paths, so a parameter cannot be restricted with a suffix such as `:userId(\d+)`. Use an array of paths or a full regular expression instead. See the [path route matching syntax](/guide/migrating-5#path-route-matching-syntax) for more information. @@ -332,7 +330,7 @@ app.get('/:file{.:ext}', (req: Request, res: Response) => { The braces can also wrap a whole parameter to make it optional. Note that everything inside the braces is optional, so the position of the slash matters: ```js -app.get('/user/\{:id}', (req, res) => { +app.get('/user/{:id}', (req, res) => { // GET /user/42 => req.params = { id: '42' } // GET /user/ => req.params = {} // GET /user => 404, only the parameter is optional @@ -349,7 +347,7 @@ app.get('/order{/:id}', (req, res) => { ```ts import { type Request, type Response } from 'express'; -app.get('/user/\{:id}', (req: Request, res: Response) => { +app.get('/user/{:id}', (req: Request, res: Response) => { // GET /user/42 => req.params = { id: '42' } // GET /user/ => req.params = {} // GET /user => 404, only the parameter is optional @@ -363,7 +361,7 @@ app.get('/order{/:id}', (req: Request, res: Response) => { }); ``` -ルートパス内のスラッシュの位置を、リクエスト URL に関する[`strict routing` 設定](/api/application/#application-settings)と混同しないでください。この設定は、ルートパスが要求していないスラッシュで終わる URL が一致するかどうかを制御します。たとえば、`/order/` へのリクエストはデフォルトでは `/order{/:id}` ルートに一致しますが、strict routing が有効な場合は 404 エラーを返します。`/user/` の末尾のスラッシュは、`/user/\{:id}` ルートがそれを必須としているため影響を受けません。上記の例でコメントされているすべてのリクエストは、この設定に関係なく同じように動作します。 +ルートパス内のスラッシュの位置を、リクエスト URL に関する[`strict routing` 設定](/api/application/#application-settings)と混同しないでください。この設定は、ルートパスが要求していないスラッシュで終わる URL が一致するかどうかを制御します。たとえば、`/order/` へのリクエストはデフォルトでは `/order{/:id}` ルートに一致しますが、strict routing が有効な場合は 404 エラーを返します。`/user/` の末尾のスラッシュは、`/user/{:id}` ルートがそれを必須としているため影響を受けません。上記の例でコメントされているすべてのリクエストは、この設定に関係なく同じように動作します。 ## Route handlers diff --git a/src/content/docs/ko/4x/guide/writing-middleware.mdx b/src/content/docs/ko/4x/guide/writing-middleware.mdx index 650570479e..0e32b54698 100644 --- a/src/content/docs/ko/4x/guide/writing-middleware.mdx +++ b/src/content/docs/ko/4x/guide/writing-middleware.mdx @@ -19,36 +19,7 @@ If the current middleware function does not end the request-response cycle, it m The following figure shows the elements of a middleware function call: -
- - - - - -
-Elements of a middleware function call - -
HTTP method for which the middleware function applies.
- -
- Path (route) for which the middleware function applies. -
- -
- The middleware function. -
- -
- Callback argument to the middleware function, called "next" by convention. -
- -
- HTTP [response](/api/response) argument to the middleware function, called "res" by convention. -
- -
HTTP [request](/api/request) argument to the middleware function, called "req" by convention.
-
-
+![Elements of a middleware function call](/images/express-mw.png) Starting with Express 5, middleware functions that return a Promise will call `next(value)` when they reject or throw an error. `next` will be called with either the rejected value or the thrown Error. diff --git a/src/content/docs/ko/5x/guide/routing.mdx b/src/content/docs/ko/5x/guide/routing.mdx index 320d37e72d..080cc215e3 100644 --- a/src/content/docs/ko/5x/guide/routing.mdx +++ b/src/content/docs/ko/5x/guide/routing.mdx @@ -325,7 +325,7 @@ app.get('/:file{.:ext}', (req: Request, res: Response) => { The braces can also wrap a whole parameter to make it optional. Note that everything inside the braces is optional, so the position of the slash matters: ```js -app.get('/user/\{:id}', (req, res) => { +app.get('/user/{:id}', (req, res) => { // GET /user/42 => req.params = { id: '42' } // GET /user/ => req.params = {} // GET /user => 404, only the parameter is optional @@ -342,7 +342,7 @@ app.get('/order{/:id}', (req, res) => { ```ts import { type Request, type Response } from 'express'; -app.get('/user/\{:id}', (req: Request, res: Response) => { +app.get('/user/{:id}', (req: Request, res: Response) => { // GET /user/42 => req.params = { id: '42' } // GET /user/ => req.params = {} // GET /user => 404, only the parameter is optional @@ -356,7 +356,7 @@ app.get('/order{/:id}', (req: Request, res: Response) => { }); ``` -라우트 경로에서 슬래시의 위치를 요청 URL에 관한 [`strict routing` 설정](/api/application/#application-settings)과 혼동하지 마세요. 이 설정은 라우트 경로가 요구하지 않는 슬래시로 끝나는 URL이 여전히 일치하는지를 제어합니다. 예를 들어 `/order/` 요청은 기본적으로 `/order{/:id}` 라우트와 일치하지만, strict routing이 활성화되면 404 오류를 반환합니다. `/user/`의 끝 슬래시는 `/user/\{:id}` 라우트가 이를 요구하므로 영향을 받지 않습니다. 위 예제에서 주석으로 표시된 모든 요청은 이 설정과 관계없이 동일하게 동작합니다. +라우트 경로에서 슬래시의 위치를 요청 URL에 관한 [`strict routing` 설정](/api/application/#application-settings)과 혼동하지 마세요. 이 설정은 라우트 경로가 요구하지 않는 슬래시로 끝나는 URL이 여전히 일치하는지를 제어합니다. 예를 들어 `/order/` 요청은 기본적으로 `/order{/:id}` 라우트와 일치하지만, strict routing이 활성화되면 404 오류를 반환합니다. `/user/`의 끝 슬래시는 `/user/{:id}` 라우트가 이를 요구하므로 영향을 받지 않습니다. 위 예제에서 주석으로 표시된 모든 요청은 이 설정과 관계없이 동일하게 동작합니다. ## Route handlers @@ -556,7 +556,7 @@ The methods on the [response object](/api/response) (`res`) in the following tab You can create chainable route handlers for a route path by using `app.route()`. Because the path is specified in a single location, this helps to create modular routes and reduces redundancy and typos. For more information about routes, see the [Router() documentation](/api/router). -|## app.route()You can create chainable route handlers for a rou +\|## app.route()You can create chainable route handlers for a rou ```js app diff --git a/src/content/docs/pt-br/4x/guide/writing-middleware.mdx b/src/content/docs/pt-br/4x/guide/writing-middleware.mdx index aaf9895f9c..81621ec10c 100644 --- a/src/content/docs/pt-br/4x/guide/writing-middleware.mdx +++ b/src/content/docs/pt-br/4x/guide/writing-middleware.mdx @@ -19,36 +19,7 @@ Se a função middleware atual não encerra o ciclo de resposta de solicitação A figura a seguir mostra os elementos de uma chamada de função middleware: -
- - - - - -
-Elementos de uma chamada de função middleware - -
método HTTP para o qual se aplicam as funções middleware.
- -
- Caminho (rota) para o qual a função middleware se aplica. -
- -
- A função middleware. -
- -
- Argumento de retorno para a função middleware, chamado "próximo" por convenção. -
- -
- HTTP [response](/api/response) argument to the middleware function, called "res" by convention. -
- -
HTTP [request](/api/request) argument to the middleware function, called "req" by convention.
-
-
+![Elementos de uma chamada de função middleware](/images/express-mw.png) Começando com as funções de middleware Express, funções de intermediário que retornam uma Promessa chamarão `próximo(valor)` quando eles rejeitarem ou lançarem um erro. `next` será chamado com o valor rejeitado ou o erro lançado. diff --git a/src/content/docs/pt-br/5x/guide/routing.mdx b/src/content/docs/pt-br/5x/guide/routing.mdx index fb885aba32..9ff9231afd 100644 --- a/src/content/docs/pt-br/5x/guide/routing.mdx +++ b/src/content/docs/pt-br/5x/guide/routing.mdx @@ -110,7 +110,7 @@ app.all('/secret', (req: Request, res: Response, next: NextFunction) => { Roteamento, em combinação com um método de solicitação, defina os pontos de extremidade em que as solicitações podem ser feitas. Caminhos de rota podem ser strings ou expressões regulares. They can also capture values from the URL, as described in [Route parameters](#route-parameters) below. - + Expresso usa [path-to-regexp](https://www.npmjs.com/package/path-to-regexp) v8 para combinar com os caminhos da rota; veja a documentação pai-para-regexp para todas as possibilidades de definir caminhos da rota. [Express Playground Router](https://bjohansebas.github.io/playground-router/) é uma ferramenta útil para testar rotas Express, embora não suporte correspondência de padrões. @@ -153,7 +153,7 @@ app.get('/random.text', (req: Request, res: Response) => { }); ``` - + The characters `?`, `+`, `*`, `[]`, `()`, and `!` are reserved and cannot be used as literal characters in route paths, and braces are reserved for [optional segments](#optional-segments). Use `\` para escapar deles se necessário. @@ -227,7 +227,7 @@ const sendParams = (req: Request<{ userId: string; bookId: string }>, res: Respo app.get('/users/:userId/books/:bookId', sendParams); ``` - + The name of route parameters must be a valid JavaScript identifier. Other names can be used by quoting them, for example `:"user-name"`. @@ -247,7 +247,7 @@ Request URL: http://localhost:3000/plantae/Prunus.persica req.params: { "genus": "Prunus", "species": "persica" } ``` - + Regexp characters are not supported inside string paths, so a parameter cannot be restricted with a suffix such as `:userId(\d+)`. Use an array of paths or a full regular expression instead. Veja a [sintaxe correspondente à trajetória](/guide/migrating-5#path-syntax) para obter mais informações. @@ -325,7 +325,7 @@ app.get('/:file{.:ext}', (req: Request, res: Response) => { The braces can also wrap a whole parameter to make it optional. Note that everything inside the braces is optional, so the position of the slash matters: ```js -app.get('/user/\{:id}', (req, res) => { +app.get('/user/{:id}', (req, res) => { // GET /user/42 => req.params = { id: '42' } // GET /user/ => req.params = {} // GET /user => 404, only the parameter is optional @@ -342,7 +342,7 @@ app.get('/order{/:id}', (req, res) => { ```ts import { type Request, type Response } from 'express'; -app.get('/user/\{:id}', (req: Request, res: Response) => { +app.get('/user/{:id}', (req: Request, res: Response) => { // GET /user/42 => req.params = { id: '42' } // GET /user/ => req.params = {} // GET /user => 404, only the parameter is optional @@ -356,7 +356,7 @@ app.get('/order{/:id}', (req: Request, res: Response) => { }); ``` -Não confunda a posição da barra no caminho da rota com a [configuração `strict routing`](/api/application/#application-settings), que diz respeito à URL da requisição: ela controla se uma URL terminada em barra que o caminho da rota não exige ainda corresponde. Por exemplo, uma requisição para `/order/` corresponde à rota `/order{/:id}` por padrão, mas retorna um erro 404 quando o strict routing está habilitado; a barra final de `/user/` não é afetada porque a rota `/user/\{:id}` a exige. Todas as requisições comentadas nos exemplos acima se comportam da mesma forma independentemente dessa configuração. +Não confunda a posição da barra no caminho da rota com a [configuração `strict routing`](/api/application/#application-settings), que diz respeito à URL da requisição: ela controla se uma URL terminada em barra que o caminho da rota não exige ainda corresponde. Por exemplo, uma requisição para `/order/` corresponde à rota `/order{/:id}` por padrão, mas retorna um erro 404 quando o strict routing está habilitado; a barra final de `/user/` não é afetada porque a rota `/user/{:id}` a exige. Todas as requisições comentadas nos exemplos acima se comportam da mesma forma independentemente dessa configuração. ## Manipuladores de rota diff --git a/src/content/docs/zh-cn/4x/guide/routing.mdx b/src/content/docs/zh-cn/4x/guide/routing.mdx index d2744a7c7c..d4da79327d 100644 --- a/src/content/docs/zh-cn/4x/guide/routing.mdx +++ b/src/content/docs/zh-cn/4x/guide/routing.mdx @@ -1,5 +1,5 @@ --- -title: Routing +title: 路由 description: Learn how to define and use routes in Express.js applications, including route methods, route paths, parameters, typing requests and responses in TypeScript, and using Router for modular routing. --- @@ -10,8 +10,8 @@ For an introduction to routing, see [Basic routing](/starter/basic-routing). For an introduction to routing, see [Basic routing](/starter/basic-routing). 你可以使用 Express `app` 对象中与 HTTP 方法对应的方法来定义路由; -例如,`app.get()` 处理 GET 请求,`app.post` 处理 POST 请求。 For a full list, -see [app.METHOD](/api/application#appmethod). You can also use [app.all()](/api/application#appall) to handle all HTTP methods and [app.use()](/api/application#appuse) to +例如,`app.get()` 处理 GET 请求,`app.post` 处理 POST 请求。 You can also use [app.all()](/api/application#appall) to handle all HTTP methods and [app.use()](/api/application#appuse) to +specify middleware as the callback function (See [Using middleware](/guide/using-middleware) for details). You can also use [app.all()](/api/application#appall) to handle all HTTP methods and [app.use()](/api/application#appuse) to specify middleware as the callback function (See [Using middleware](/guide/using-middleware) for details). 换句话说,应用程序会“监听”匹配指定路由和方法的请求,当检测到匹配时,就会调用指定的回调函数。 These routing methods specify a callback function (sometimes called a "handler function") that Express automatically runs when the application receives a request matching the specified route (endpoint) and HTTP method. diff --git a/src/content/docs/zh-cn/4x/guide/writing-middleware.mdx b/src/content/docs/zh-cn/4x/guide/writing-middleware.mdx index 438ed3dc20..1a5130af04 100644 --- a/src/content/docs/zh-cn/4x/guide/writing-middleware.mdx +++ b/src/content/docs/zh-cn/4x/guide/writing-middleware.mdx @@ -19,36 +19,7 @@ _Middleware_ functions are functions that have access to the [request object](/a 下图展示了中间件函数调用的组成要素: -
- - - - - -
-Elements of a middleware function call - -
HTTP method for which the middleware function applies.
- -
- 中间件函数适用的路径(路由)。 -
- -
- The middleware function. -
- -
- 中间件函数的回调参数,按照惯例命名为 “next”。 -
- -
- HTTP [response](/api/response) argument to the middleware function, called "res" by convention. -
- -
HTTP [request](/api/request) argument to the middleware function, called "req" by convention.
-
-
+![Elements of a middleware function call](/images/express-mw.png) 从 Express 5 开始,返回 Promise 的中间件函数在被拒绝或抛出错误时,会自动调用 `next(value)`。 `next` 函数将以被拒绝的值或抛出的错误对象作为参数被调用。 diff --git a/src/content/docs/zh-cn/5x/guide/routing.mdx b/src/content/docs/zh-cn/5x/guide/routing.mdx index 1e346cf0cf..6629c09775 100644 --- a/src/content/docs/zh-cn/5x/guide/routing.mdx +++ b/src/content/docs/zh-cn/5x/guide/routing.mdx @@ -1,5 +1,5 @@ --- -title: Routing +title: 路由 description: Learn how to define and use routes in Express.js applications, including route methods, route paths, parameters, typing requests and responses in TypeScript, and using Router for modular routing. --- @@ -10,8 +10,8 @@ For an introduction to routing, see [Basic routing](/starter/basic-routing). For an introduction to routing, see [Basic routing](/starter/basic-routing). 你可以使用 Express `app` 对象中与 HTTP 方法对应的方法来定义路由; -例如,`app.get()` 处理 GET 请求,`app.post` 处理 POST 请求。 For a full list, -see [app.METHOD](/api/application#appmethod). You can also use [app.all()](/api/application#appall) to handle all HTTP methods and [app.use()](/api/application#appuse) to +例如,`app.get()` 处理 GET 请求,`app.post` 处理 POST 请求。 You can also use [app.all()](/api/application#appall) to handle all HTTP methods and [app.use()](/api/application#appuse) to +specify middleware as the callback function (See [Using middleware](/guide/using-middleware) for details). You can also use [app.all()](/api/application#appall) to handle all HTTP methods and [app.use()](/api/application#appuse) to specify middleware as the callback function (See [Using middleware](/guide/using-middleware) for details). 换句话说,应用程序会“监听”匹配指定路由和方法的请求,当检测到匹配时,就会调用指定的回调函数。 These routing methods specify a callback function (sometimes called a "handler function") that Express automatically runs when the application receives a request matching the specified route (endpoint) and HTTP method. @@ -329,7 +329,7 @@ app.get('/:file{.:ext}', (req: Request, res: Response) => { The braces can also wrap a whole parameter to make it optional. Note that everything inside the braces is optional, so the position of the slash matters: ```js -app.get('/user/\{:id}', (req, res) => { +app.get('/user/{:id}', (req, res) => { // GET /user/42 => req.params = { id: '42' } // GET /user/ => req.params = {} // GET /user => 404, only the parameter is optional @@ -346,7 +346,7 @@ app.get('/order{/:id}', (req, res) => { ```ts import { type Request, type Response } from 'express'; -app.get('/user/\{:id}', (req: Request, res: Response) => { +app.get('/user/{:id}', (req: Request, res: Response) => { // GET /user/42 => req.params = { id: '42' } // GET /user/ => req.params = {} // GET /user => 404, only the parameter is optional @@ -360,7 +360,7 @@ app.get('/order{/:id}', (req: Request, res: Response) => { }); ``` -不要将路由路径中斜杠的位置与[`strict routing` 设置](/api/application/#application-settings)混淆,后者针对的是请求 URL:它控制以路由路径未要求的斜杠结尾的 URL 是否仍然匹配。例如,对 `/order/` 的请求默认匹配 `/order{/:id}` 路由,但在启用 strict routing 时会返回 404 错误;`/user/` 的末尾斜杠不受影响,因为 `/user/\{:id}` 路由要求它。上面示例中注释的所有请求无论该设置如何,行为都相同。 +不要将路由路径中斜杠的位置与[`strict routing` 设置](/api/application/#application-settings)混淆,后者针对的是请求 URL:它控制以路由路径未要求的斜杠结尾的 URL 是否仍然匹配。例如,对 `/order/` 的请求默认匹配 `/order{/:id}` 路由,但在启用 strict routing 时会返回 404 错误;`/user/` 的末尾斜杠不受影响,因为 `/user/{:id}` 路由要求它。上面示例中注释的所有请求无论该设置如何,行为都相同。 ## Route handlers diff --git a/src/content/docs/zh-tw/4x/guide/writing-middleware.mdx b/src/content/docs/zh-tw/4x/guide/writing-middleware.mdx index 650570479e..0e32b54698 100644 --- a/src/content/docs/zh-tw/4x/guide/writing-middleware.mdx +++ b/src/content/docs/zh-tw/4x/guide/writing-middleware.mdx @@ -19,36 +19,7 @@ If the current middleware function does not end the request-response cycle, it m The following figure shows the elements of a middleware function call: -
- - - - - -
-Elements of a middleware function call - -
HTTP method for which the middleware function applies.
- -
- Path (route) for which the middleware function applies. -
- -
- The middleware function. -
- -
- Callback argument to the middleware function, called "next" by convention. -
- -
- HTTP [response](/api/response) argument to the middleware function, called "res" by convention. -
- -
HTTP [request](/api/request) argument to the middleware function, called "req" by convention.
-
-
+![Elements of a middleware function call](/images/express-mw.png) Starting with Express 5, middleware functions that return a Promise will call `next(value)` when they reject or throw an error. `next` will be called with either the rejected value or the thrown Error. diff --git a/src/content/docs/zh-tw/5x/guide/routing.mdx b/src/content/docs/zh-tw/5x/guide/routing.mdx index a613aa6f76..59347f3099 100644 --- a/src/content/docs/zh-tw/5x/guide/routing.mdx +++ b/src/content/docs/zh-tw/5x/guide/routing.mdx @@ -325,7 +325,7 @@ app.get('/:file{.:ext}', (req: Request, res: Response) => { The braces can also wrap a whole parameter to make it optional. Note that everything inside the braces is optional, so the position of the slash matters: ```js -app.get('/user/\{:id}', (req, res) => { +app.get('/user/{:id}', (req, res) => { // GET /user/42 => req.params = { id: '42' } // GET /user/ => req.params = {} // GET /user => 404, only the parameter is optional @@ -342,7 +342,7 @@ app.get('/order{/:id}', (req, res) => { ```ts import { type Request, type Response } from 'express'; -app.get('/user/\{:id}', (req: Request, res: Response) => { +app.get('/user/{:id}', (req: Request, res: Response) => { // GET /user/42 => req.params = { id: '42' } // GET /user/ => req.params = {} // GET /user => 404, only the parameter is optional @@ -356,7 +356,7 @@ app.get('/order{/:id}', (req: Request, res: Response) => { }); ``` -不要將路由路徑中斜線的位置與[`strict routing` 設定](/api/application/#application-settings)混淆,後者針對的是請求 URL:它控制以路由路徑未要求的斜線結尾的 URL 是否仍然匹配。例如,對 `/order/` 的請求預設會匹配 `/order{/:id}` 路由,但在啟用 strict routing 時會傳回 404 錯誤;`/user/` 的結尾斜線不受影響,因為 `/user/\{:id}` 路由要求它。上述範例中註解的所有請求無論該設定為何,行為都相同。 +不要將路由路徑中斜線的位置與[`strict routing` 設定](/api/application/#application-settings)混淆,後者針對的是請求 URL:它控制以路由路徑未要求的斜線結尾的 URL 是否仍然匹配。例如,對 `/order/` 的請求預設會匹配 `/order{/:id}` 路由,但在啟用 strict routing 時會傳回 404 錯誤;`/user/` 的結尾斜線不受影響,因為 `/user/{:id}` 路由要求它。上述範例中註解的所有請求無論該設定為何,行為都相同。 ## Route handlers @@ -556,7 +556,7 @@ The methods on the [response object](/api/response) (`res`) in the following tab You can create chainable route handlers for a route path by using `app.route()`. Because the path is specified in a single location, this helps to create modular routes and reduces redundancy and typos. For more information about routes, see the [Router() documentation](/api/router). -|## app.route()You can create chainable route handlers for a rou +\|## app.route()You can create chainable route handlers for a rou ```js app diff --git a/src/content/pages/de/guide/database-integration.mdx b/src/content/pages/de/guide/database-integration.mdx index fa2406e688..866bd97c2d 100644 --- a/src/content/pages/de/guide/database-integration.mdx +++ b/src/content/pages/de/guide/database-integration.mdx @@ -1,656 +1,196 @@ --- title: Datenbankintegration -description: Erfahren Sie, wie Sie verschiedene Datenbanken mit Express.js Anwendungen integrieren können, einschließlich der Einrichtung von Beispielen für MongoDB, MySQL, PostgreSQL und mehr. +description: Learn how to connect databases to Express.js applications, when to use an ORM, and best practices for connection pooling, security, and error handling. --- -import Alert from '@components/primitives/Alert/Alert.astro'; -import PackageManagerCommand from '@components/patterns/PackageManagerCommand/PackageManagerCommand.astro'; +To connect a database to an Express app simply load the desired Node.js driver or ORM in your app. The general pattern is shown below using SQLite as an example. -Hinzufügen der Möglichkeit, Datenbanken mit Express-Apps zu verbinden, ist nur eine Frage des Laden eines geeigneten Node.js Treibers für die Datenbank in Ihrer App. Dieses Dokument erklärt kurz wie Sie einige der beliebtesten Node.js-Module für Datenbanksysteme in Ihrer Express-App hinzufügen und nutzen: +There are also a few [best practices](#best-practices) to follow, regardless of the database you're using. -- [Cassandra](#cassandra) -- [Couchbase](#couchbase) -- [CouchDB](#couchdb) -- [LevelDB](#leveldb) -- [MySQL](#mysql) -- [MongoDB](#mongodb) -- [Neo4j](#neo4j) -- [Oracle](#oracle) -- [PostgreSQL](#postgresql) -- [Redis](#redis) -- [SQL Server](#sql-server) -- [SQLite](#sqlite) -- [Elasticsearch](#elasticsearch) - - - -Diese Datenbanktreiber sind unter vielen verfügbar. Für andere Optionen suchen Sie auf der -[npm](https://www.npmjs.com/) Seite. - - - -## Cassandra - -**Modul**: [cassandra-driver](https://github.com/datastax/nodejs-driver) - -### Installation +## SQLite - +Node.js includes a built-in SQLite client, [node:sqlite](https://nodejs.org/api/sqlite.html), available without a flag since Node.js 22.13.0. No installation is required. For older Node.js versions, [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) offers a similar synchronous API. ### Beispiel -```cjs title="index.cjs" -const cassandra = require('cassandra-driver'); -const client = new cassandra.Client({ contactPoints: ['localhost'] }); - -client.execute('select key from system.local', (err, result) => { - if (err) throw err; - console.log(result.rows[0]); -}); -``` +This example creates a sample user that you can retrieve at `http://localhost:3000/users/1`. It uses a synchronous SQLite API for simplicity: long-running queries block the thread handling requests. For workloads with long-running queries, consider moving database work to a worker thread or using an asynchronous database driver. -```mjs title="index.mjs" -import cassandra from 'cassandra-driver'; - -const client = new cassandra.Client({ contactPoints: ['localhost'] }); - -client.execute('select key from system.local', (err, result) => { - if (err) throw err; - console.log(result.rows[0]); -}); -``` - -## Couchbase - -**Module**: [couchnode](https://github.com/couchbase/couchnode) - -### Installation +```cjs title="index.cjs" +const express = require('express'); +const { DatabaseSync } = require('node:sqlite'); - +const app = express(); +const db = new DatabaseSync('app.db'); -### Beispiel +db.exec('CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT NOT NULL)'); +db.prepare('INSERT INTO users (id, name) VALUES (?, ?) ON CONFLICT(id) DO NOTHING').run(1, 'Alice'); +const findUser = db.prepare('SELECT id, name FROM users WHERE id = ?'); -```cjs title="index.cjs" -const couchbase = require('couchbase'); -const bucket = new couchbase.Cluster('http://localhost:8091').openBucket('bucketName'); - -// add a document to a bucket -bucket.insert('document-key', { name: 'Matt', shoeSize: 13 }, (err, result) => { - if (err) { - console.log(err); - } else { - console.log(result); +app.get('/users/:id', (req, res) => { + const user = findUser.get(req.params.id); + if (!user) { + return res.status(404).json({ error: 'User not found' }); } + res.json(user); }); -// get all documents with shoe size 13 -const n1ql = 'SELECT d.* FROM `bucketName` d WHERE shoeSize = $1'; -const query = N1qlQuery.fromString(n1ql); -bucket.query(query, [13], (err, result) => { - if (err) { - console.log(err); - } else { - console.log(result); - } -}); +app.listen(3000); ``` ```mjs title="index.mjs" -import couchbase from 'couchbase'; +import express from 'express'; +import { DatabaseSync } from 'node:sqlite'; -const bucket = new couchbase.Cluster('http://localhost:8091').openBucket('bucketName'); +const app = express(); +const db = new DatabaseSync('app.db'); -// add a document to a bucket -bucket.insert('document-key', { name: 'Matt', shoeSize: 13 }, (err, result) => { - if (err) { - console.log(err); - } else { - console.log(result); - } -}); +db.exec('CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT NOT NULL)'); +db.prepare('INSERT INTO users (id, name) VALUES (?, ?) ON CONFLICT(id) DO NOTHING').run(1, 'Alice'); +const findUser = db.prepare('SELECT id, name FROM users WHERE id = ?'); -// get all documents with shoe size 13 -const n1ql = 'SELECT d.* FROM `bucketName` d WHERE shoeSize = $1'; -const query = N1qlQuery.fromString(n1ql); -bucket.query(query, [13], (err, result) => { - if (err) { - console.log(err); - } else { - console.log(result); +app.get('/users/:id', (req, res) => { + const user = findUser.get(req.params.id); + if (!user) { + return res.status(404).json({ error: 'User not found' }); } + res.json(user); }); + +app.listen(3000); ``` -## CouchDB +```ts title="index.ts" +import express from 'express'; +import { DatabaseSync } from 'node:sqlite'; -**Module**: [nano](https://github.com/dscape/nano) +interface User { + id: number; + name: string; +} -### Installation +const app = express(); +const db = new DatabaseSync('app.db'); - +db.exec('CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT NOT NULL)'); +db.prepare('INSERT INTO users (id, name) VALUES (?, ?) ON CONFLICT(id) DO NOTHING').run(1, 'Alice'); +const findUser = db.prepare('SELECT id, name FROM users WHERE id = ?'); -### Beispiel - -```js title="index.js" -const nano = require('nano')('http://localhost:5984'); -nano.db.create('books'); -const books = nano.db.use('books'); - -// Insert a book document in the books database -books.insert({ name: 'The Art of war' }, null, (err, body) => { - if (err) { - console.log(err); - } else { - console.log(body); +app.get('/users/:id', (req, res) => { + const user = findUser.get(req.params.id) as User | undefined; + if (!user) { + return res.status(404).json({ error: 'User not found' }); } + res.json(user); }); -// Get a list of all books -books.list((err, body) => { - if (err) { - console.log(err); - } else { - console.log(body.rows); - } -}); +app.listen(3000); ``` -## LevelDB +## Other databases -**Module**: [levelup](https://github.com/rvagg/node-levelup) +Every other database follows the same pattern: install the Node.js driver from npm, create a client or connection pool once when the app starts, and use it in your route handlers. Refer to the official documentation of each driver for connection options and API details: -### Installation +- PostgreSQL: [pg](https://node-postgres.com/) or [postgres](https://github.com/porsager/postgres) +- MySQL and MariaDB: [mysql2](https://sidorares.github.io/node-mysql2/docs) or [mariadb](https://mariadb.com/docs/connectors/mariadb-connector-nodejs) +- MongoDB: [mongodb](https://www.mongodb.com/docs/drivers/node/current/) +- Redis: [redis](https://redis.io/docs/latest/develop/clients/nodejs/) or [ioredis](https://github.com/redis/ioredis) +- SQL Server: [mssql](https://github.com/tediousjs/node-mssql) +- Oracle: [oracledb](https://www.npmjs.com/package/oracledb) +- Cassandra: [cassandra-driver](https://docs.datastax.com/en/developer/nodejs-driver/latest/) +- Elasticsearch: [@elastic/elasticsearch](https://www.elastic.co/docs/reference/elasticsearch/clients/javascript) - - -### Beispiel +For any database not listed here, search for its driver on the [npm](https://www.npmjs.com/) site. -```cjs title="index.cjs" -const levelup = require('levelup'); -const db = levelup('./mydb'); +## ORMs and query builders -db.put('name', 'LevelUP', (err) => { - if (err) return console.log('Ooops!', err); +The example above uses a database driver directly, which is a good fit for small apps and for learning how things work. As an application grows, an ORM (object-relational mapper) or a query builder can take over concerns such as schema definitions, migrations, relations between tables, and input mapping, while reducing boilerplate. Many of them also provide type safety when used with TypeScript. - db.get('name', (err, value) => { - if (err) return console.log('Ooops!', err); +Popular options in the Node.js ecosystem include: - console.log(`name=${value}`); - }); -}); -``` +- [Prisma](https://www.prisma.io/): a type-safe ORM with a schema-first workflow and built-in migrations. Supports PostgreSQL, MySQL, SQLite, SQL Server, MongoDB, and CockroachDB. +- [Drizzle ORM](https://orm.drizzle.team/): a lightweight, TypeScript-first ORM with a SQL-like query API and a migrations toolkit. +- [Sequelize](https://sequelize.org/): a mature, promise-based ORM for PostgreSQL, MySQL, MariaDB, SQLite, and SQL Server. +- [TypeORM](https://typeorm.io/): an ORM built around decorators and entities, supporting many SQL databases and MongoDB. +- [Mongoose](https://mongoosejs.com/): an ODM (object document mapper) for MongoDB. +- [Knex.js](https://knexjs.org/): a flexible SQL query builder with migrations support, useful when you want control over your queries without writing raw strings. +- [Kysely](https://kysely.dev/): a type-safe SQL query builder for TypeScript. -```mjs title="index.mjs" -import levelup from 'levelup'; +Each ORM has its own API, supported databases, and approach to schemas, migrations, and queries. Features and setup requirements vary, so consult each project's documentation before choosing one. Query builders keep you close to SQL, while full ORMs abstract more of it away. Pick the level of abstraction that matches your team and project, and prefer tools that are actively maintained. -const db = levelup('./mydb'); +## Best practices -db.put('name', 'LevelUP', (err) => { - if (err) return console.log('Ooops!', err); +Adapt these practices to the database and library you choose. The snippets below illustrate separate parts of an application. The PostgreSQL snippets assume you have installed the [pg](https://node-postgres.com/) driver and created a shared pool: - db.get('name', (err, value) => { - if (err) return console.log('Ooops!', err); +```js +import { Pool } from 'pg'; - console.log(`name=${value}`); - }); -}); +const pool = new Pool({ connectionString: process.env.DATABASE_URL }); ``` -## MySQL - -**Module**: [mysql](https://github.com/felixge/node-mysql/) - -### Installation - - - -### Beispiel - -```cjs title="index.cjs" -const mysql = require('mysql'); -const connection = mysql.createConnection({ - host: 'localhost', - user: 'dbuser', - password: 's3kreee7', - database: 'my_db', -}); +### Create connections once and reuse them -connection.connect(); +Opening a database connection is expensive. Many drivers provide a connection pool that creates connections as needed and reuses them across requests. Create the pool (or client) once when the app starts. Acquire connections from that shared pool as needed in your route handlers, for example for a transaction, and always release them in a `finally` block. For individual queries, `pool.query()` in `pg` handles acquiring and releasing a connection for you. -connection.query('SELECT 1 + 1 AS solution', (err, rows, fields) => { - if (err) throw err; +### Use parameterized queries - console.log('The solution is: ', rows[0].solution); -}); +Never build SQL queries by concatenating or interpolating user input into the query string. Doing so exposes your app to SQL injection. Use your driver's parameterized queries or prepared statements to pass user input separately from the query itself. -connection.end(); +```js +// Vulnerable: user input becomes part of the query +const user = await pool.query(`SELECT * FROM users WHERE id = ${req.params.id}`); ``` -```mjs title="index.mjs" -import mysql from 'mysql'; - -const connection = mysql.createConnection({ - host: 'localhost', - user: 'dbuser', - password: 's3kreee7', - database: 'my_db', -}); - -connection.connect(); - -connection.query('SELECT 1 + 1 AS solution', (err, rows, fields) => { - if (err) throw err; - - console.log('The solution is: ', rows[0].solution); -}); - -connection.end(); +```js +// Safe: user input is passed as a parameter +const user = await pool.query('SELECT * FROM users WHERE id = $1', [req.params.id]); ``` -## MongoDB - -**Module**: [mongodb](https://github.com/mongodb/node-mongodb-native) - -### Installation - - - -### Example (v2.\*) - -```js title="index.js" -const MongoClient = require('mongodb').MongoClient; +Also validate user input before it reaches your queries, both for security and for data integrity. For NoSQL databases, construct queries using the driver's API and validate the types, fields, and operators you accept. Do not pass user-supplied query objects directly to the database. -MongoClient.connect('mongodb://localhost:27017/animals', (err, db) => { - if (err) throw err; +### Keep credentials out of your code - db.collection('mammals') - .find() - .toArray((err, result) => { - if (err) throw err; +Never hardcode connection strings, usernames, or passwords in your source code, and never commit them to version control. Read them from environment variables or a secrets manager instead: - console.log(result); - }); -}); +```js +const pool = new Pool({ connectionString: process.env.DATABASE_URL }); ``` -### Example (v3.\*) - -```js title="index.js" -const MongoClient = require('mongodb').MongoClient; - -MongoClient.connect('mongodb://localhost:27017/animals', (err, client) => { - if (err) throw err; - - const db = client.db('animals'); - - db.collection('mammals') - .find() - .toArray((err, result) => { - if (err) throw err; +During development, you can keep these values in a `.env` file and load it with Node.js's built-in support for environment files, with no extra dependencies: - console.log(result); - }); -}); +```bash +node --env-file=.env index.js ``` -If you want an object model driver for MongoDB, look at [Mongoose](https://github.com/LearnBoost/mongoose). - -## Neo4j - -**Module**: [neo4j-driver](https://github.com/neo4j/neo4j-javascript-driver) +Make sure `.env` is listed in your `.gitignore` file so it never gets committed. -### Installation - - - -### Beispiel +### Handle database errors -```cjs title="index.cjs" -const neo4j = require('neo4j-driver'); -const driver = neo4j.driver('neo4j://localhost:7687', neo4j.auth.basic('neo4j', 'letmein')); - -const session = driver.session(); - -session.readTransaction((tx) => { - return tx - .run('MATCH (n) RETURN count(n) AS count') - .then((res) => { - console.log(res.records[0].get('count')); - }) - .catch((error) => { - console.log(error); - }); -}); -``` - -```mjs title="index.mjs" -import neo4j from 'neo4j-driver'; +Database calls fail: connections drop, queries time out, constraints are violated. In Express 5, errors thrown in async route handlers are passed to your [error-handling middleware](/en/guide/error-handling) automatically, so define one and decide there what to expose to clients. Avoid leaking internal error details, such as query text or stack traces, in API responses. -const driver = neo4j.driver('neo4j://localhost:7687', neo4j.auth.basic('neo4j', 'letmein')); +### Cancel work when the client disconnects -const session = driver.session(); +Since Node.js 24.20.0, every request exposes [req.signal](https://nodejs.org/api/http.html#messagesignal), an `AbortSignal` that is aborted when the client disconnects. Pass it to any cancellation-aware API, such as a database driver that supports abort signals, so your app stops doing work nobody is waiting for: -session.readTransaction((tx) => { - return tx - .run('MATCH (n) RETURN count(n) AS count') - .then((res) => { - console.log(res.records[0].get('count')); - }) - .catch((error) => { - console.log(error); - }); +```js +app.get('/reports', async (req, res) => { + const report = await generateReport({ signal: req.signal }); + res.json(report); }); ``` -## Oracle +### Close connections on shutdown -**Module**: [oracledb](https://github.com/oracle/node-oracledb) +When your app receives a termination signal, stop accepting new requests and close your database connections so in-flight queries can finish cleanly: -### Installation +```js +const server = app.listen(3000); -NOTE: [See installation prerequisites](https://github.com/oracle/node-oracledb#-installation). - - - -### Beispiel - -```cjs title="index.cjs" -const oracledb = require('oracledb'); -const config = { - user: '', - password: '', - connectString: 'localhost:1521/orcl', -}; - -async function getEmployee(empId) { - let conn; - - try { - conn = await oracledb.getConnection(config); - - const result = await conn.execute('select * from employees where employee_id = :id', [empId]); - - console.log(result.rows[0]); - } catch (err) { - console.log('Ouch!', err); - } finally { - if (conn) { - // conn assignment worked, need to close - await conn.close(); - } - } -} - -getEmployee(101); -``` - -```mjs title="index.mjs" -import oracledb from 'oracledb'; - -const config = { - user: '', - password: '', - connectString: 'localhost:1521/orcl', -}; - -async function getEmployee(empId) { - let conn; - - try { - conn = await oracledb.getConnection(config); - - const result = await conn.execute('select * from employees where employee_id = :id', [empId]); - - console.log(result.rows[0]); - } catch (err) { - console.log('Ouch!', err); - } finally { - if (conn) { - // conn assignment worked, need to close - await conn.close(); - } - } -} - -getEmployee(101); -``` - -## PostgreSQL - -**Module**: [pg-promise](https://github.com/vitaly-t/pg-promise) - -### Installation - - - -### Beispiel - -```js title="index.js" -const pgp = require('pg-promise')(/* options */); -const db = pgp('postgres://username:password@host:port/database'); - -db.one('SELECT $1 AS value', 123) - .then((data) => { - console.log('DATA:', data.value); - }) - .catch((error) => { - console.log('ERROR:', error); +process.on('SIGTERM', () => { + server.close(async () => { + await pool.end(); + process.exit(0); }); -``` - -## Redis - -**Module**: [redis](https://github.com/mranney/node_redis) - -### Installation - - - -### Beispiel - -```cjs title="index.cjs" -const redis = require('redis'); -const client = redis.createClient(); - -client.on('error', (err) => { - console.log(`Error ${err}`); -}); - -client.set('string key', 'string val', redis.print); -client.hset('hash key', 'hashtest 1', 'some value', redis.print); -client.hset(['hash key', 'hashtest 2', 'some other value'], redis.print); - -client.hkeys('hash key', (err, replies) => { - console.log(`${replies.length} replies:`); - - replies.forEach((reply, i) => { - console.log(` ${i}: ${reply}`); - }); - - client.quit(); -}); -``` - -```mjs title="index.mjs" -import redis from 'redis'; - -const client = redis.createClient(); - -client.on('error', (err) => { - console.log(`Error ${err}`); -}); - -client.set('string key', 'string val', redis.print); -client.hset('hash key', 'hashtest 1', 'some value', redis.print); -client.hset(['hash key', 'hashtest 2', 'some other value'], redis.print); - -client.hkeys('hash key', (err, replies) => { - console.log(`${replies.length} replies:`); - - replies.forEach((reply, i) => { - console.log(` ${i}: ${reply}`); - }); - - client.quit(); }); ``` -## SQL Server - -**Module**: [tedious](https://github.com/tediousjs/tedious) - -### Installation - - - -### Beispiel - -```js title="index.js" -const Connection = require('tedious').Connection; -const Request = require('tedious').Request; - -const config = { - server: 'localhost', - authentication: { - type: 'default', - options: { - userName: 'your_username', // update me - password: 'your_password', // update me - }, - }, -}; - -const connection = new Connection(config); - -connection.on('connect', (err) => { - if (err) { - console.log(err); - } else { - executeStatement(); - } -}); - -function executeStatement() { - request = new Request("select 123, 'hello world'", (err, rowCount) => { - if (err) { - console.log(err); - } else { - console.log(`${rowCount} rows`); - } - connection.close(); - }); - - request.on('row', (columns) => { - columns.forEach((column) => { - if (column.value === null) { - console.log('NULL'); - } else { - console.log(column.value); - } - }); - }); - - connection.execSql(request); -} -``` - -## SQLite - -**Module**: [sqlite3](https://github.com/mapbox/node-sqlite3) - -### Installation - - - -### Beispiel - -```js title="index.js" -const sqlite3 = require('sqlite3').verbose(); -const db = new sqlite3.Database(':memory:'); - -db.serialize(() => { - db.run('CREATE TABLE lorem (info TEXT)'); - const stmt = db.prepare('INSERT INTO lorem VALUES (?)'); - - for (let i = 0; i < 10; i++) { - stmt.run(`Ipsum ${i}`); - } - - stmt.finalize(); - - db.each('SELECT rowid AS id, info FROM lorem', (err, row) => { - console.log(`${row.id}: ${row.info}`); - }); -}); - -db.close(); -``` - -## Elasticsearch - -**Module**: [elasticsearch](https://github.com/elastic/elasticsearch-js) - -### Installation - - - -### Beispiel - -```cjs title="index.cjs" -const elasticsearch = require('elasticsearch'); -const client = elasticsearch.Client({ - host: 'localhost:9200', -}); - -client - .search({ - index: 'books', - type: 'book', - body: { - query: { - multi_match: { - query: 'express js', - fields: ['title', 'description'], - }, - }, - }, - }) - .then( - (response) => { - const hits = response.hits.hits; - }, - (error) => { - console.trace(error.message); - } - ); -``` - -```mjs title="index.mjs" -import elasticsearch from 'elasticsearch'; - -const client = elasticsearch.Client({ - host: 'localhost:9200', -}); - -client - .search({ - index: 'books', - type: 'book', - body: { - query: { - multi_match: { - query: 'express js', - fields: ['title', 'description'], - }, - }, - }, - }) - .then( - (response) => { - const hits = response.hits.hits; - }, - (error) => { - console.trace(error.message); - } - ); -``` +If you already use an [AbortSignal](https://nodejs.org/api/globals.html#class-abortsignal) to coordinate cancellation across your app, you can pass it to `app.listen` and abort it instead of calling `server.close()` directly. diff --git a/src/content/pages/de/resources/middleware/body-parser.mdx b/src/content/pages/de/resources/middleware/body-parser.mdx index 0eb696049e..9c2831bda8 100644 --- a/src/content/pages/de/resources/middleware/body-parser.mdx +++ b/src/content/pages/de/resources/middleware/body-parser.mdx @@ -8,7 +8,7 @@ import Alert from '@components/primitives/Alert/Alert.astro'; import PackageManagerCommand from '@components/patterns/PackageManagerCommand/PackageManagerCommand.astro'; @@ -82,6 +82,12 @@ Das Objekt `bodyParser` legt verschiedene Fabriken auf, um Middleware zu erstell Middlewares werden die Eigenschaft `req.body` mit dem analysierten Körper füllen, wenn der `Content-Type` Request Header mit der Option `type` übereinstimmt. +**Note** As of v2, `req.body` is left `undefined` when no body is parsed (for +example when the request has neither `Content-Length` nor `Transfer-Encoding`, +or when `Content-Type` does not match). In v1, `req.body` was always initialized +to `{}`. Code that assumes `req.body` is always an object should use optional +chaining (for example `req.body?.username`) or set a default. + Die verschiedenen Fehler, die von diesem Modul zurückgegeben werden, werden im [Fehlerabschnitt](#errors) beschrieben. @@ -126,7 +132,7 @@ Argument direkt an `JSON.parse` übergeben. You can find more information on thi ##### strict -When set to `true`, will only accept arrays and objects; when `false` will +When set to `true`, it will only accept arrays and objects; when `false`, it will accept anything `JSON.parse` accepts. Standard ist `true`. ##### type @@ -423,7 +429,7 @@ wird auf die nicht unterstützte Kodierung gesetzt. ### The input exceeded the depth -This error occurs when using `bodyParser.urlencoded` with the `extended` property set to `true` and the input exceeds the configured `depth` option. The `status` property is set to `400`. It is recommended to review the `depth` option and evaluate if it requires a higher value. When the `depth` option is set to `32` (default value), the error will not be thrown. +This error occurs when using `bodyParser.urlencoded` with the `extended` property set to `true` and the input exceeds the configured `depth` option. The `status` property is set to `400`. It is recommended to review the `depth` option and evaluate if it requires a higher value. With the default `depth` of `32`, an input with 33 or more levels of nesting gets this error. ## Beispiele diff --git a/src/content/pages/de/resources/middleware/multer.mdx b/src/content/pages/de/resources/middleware/multer.mdx index e212c878e7..dbdc484459 100644 --- a/src/content/pages/de/resources/middleware/multer.mdx +++ b/src/content/pages/de/resources/middleware/multer.mdx @@ -8,7 +8,7 @@ import Alert from '@components/primitives/Alert/Alert.astro'; import PackageManagerCommand from '@components/patterns/PackageManagerCommand/PackageManagerCommand.astro'; diff --git a/src/content/pages/es/guide/database-integration.mdx b/src/content/pages/es/guide/database-integration.mdx index 5e1650d190..3c6902f956 100644 --- a/src/content/pages/es/guide/database-integration.mdx +++ b/src/content/pages/es/guide/database-integration.mdx @@ -1,656 +1,196 @@ --- title: Integración de la base de datos -description: Descubra cómo integrar varias bases de datos con aplicaciones Express.js, incluyendo ejemplos de configuración para MongoDB, MySQL, PostgreSQL, y más. +description: Learn how to connect databases to Express.js applications, when to use an ORM, and best practices for connection pooling, security, and error handling. --- -import Alert from '@components/primitives/Alert/Alert.astro'; -import PackageManagerCommand from '@components/patterns/PackageManagerCommand/PackageManagerCommand.astro'; +To connect a database to an Express app simply load the desired Node.js driver or ORM in your app. The general pattern is shown below using SQLite as an example. -Añadir la capacidad para conectar bases de datos a aplicaciones Express es sólo una cuestión de cargar un controlador Node.js apropiado para la base de datos en su aplicación. Este documento explica brevemente cómo agregar y utilizar algunos de los módulos Node.js más populares para sistemas de base de datos en su aplicación Express: +There are also a few [best practices](#best-practices) to follow, regardless of the database you're using. -- [Cassandra](#cassandra) -- [Couchbase](#couchbase) -- [CouchDB](#couchdb) -- [LevelDB](#leveldb) -- [MySQL](#mysql) -- [MongoDB](#mongodb) -- [Neo4j](#neo4j) -- [Oracle](#oracle) -- [PostgreSQL](#postgresql) -- [Redis](#redis) -- [Servidor SQL](#sql-server) -- [SQLite](#sqlite) -- [Elasticsearch](#elasticsearch) - - - -Estos controladores de base de datos están entre muchos que están disponibles. Para otras opciones, busca en el sitio -[npm](https://www.npmjs.com/). - - - -## Cassandra - -**Módulo**: [cassandra-driver](https://github.com/datastax/nodejs-driver) - -### Instalación +## SQLite - +Node.js includes a built-in SQLite client, [node:sqlite](https://nodejs.org/api/sqlite.html), available without a flag since Node.js 22.13.0. No installation is required. For older Node.js versions, [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) offers a similar synchronous API. ### Ejemplo -```cjs title="index.cjs" -const cassandra = require('cassandra-driver'); -const client = new cassandra.Client({ contactPoints: ['localhost'] }); - -client.execute('select key from system.local', (err, result) => { - if (err) throw err; - console.log(result.rows[0]); -}); -``` +This example creates a sample user that you can retrieve at `http://localhost:3000/users/1`. It uses a synchronous SQLite API for simplicity: long-running queries block the thread handling requests. For workloads with long-running queries, consider moving database work to a worker thread or using an asynchronous database driver. -```mjs title="index.mjs" -import cassandra from 'cassandra-driver'; - -const client = new cassandra.Client({ contactPoints: ['localhost'] }); - -client.execute('select key from system.local', (err, result) => { - if (err) throw err; - console.log(result.rows[0]); -}); -``` - -## Couchbase - -**Module**: [couchnode](https://github.com/couchbase/couchnode) - -### Instalación +```cjs title="index.cjs" +const express = require('express'); +const { DatabaseSync } = require('node:sqlite'); - +const app = express(); +const db = new DatabaseSync('app.db'); -### Ejemplo +db.exec('CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT NOT NULL)'); +db.prepare('INSERT INTO users (id, name) VALUES (?, ?) ON CONFLICT(id) DO NOTHING').run(1, 'Alice'); +const findUser = db.prepare('SELECT id, name FROM users WHERE id = ?'); -```cjs title="index.cjs" -const couchbase = require('couchbase'); -const bucket = new couchbase.Cluster('http://localhost:8091').openBucket('bucketName'); - -// add a document to a bucket -bucket.insert('document-key', { name: 'Matt', shoeSize: 13 }, (err, result) => { - if (err) { - console.log(err); - } else { - console.log(result); +app.get('/users/:id', (req, res) => { + const user = findUser.get(req.params.id); + if (!user) { + return res.status(404).json({ error: 'User not found' }); } + res.json(user); }); -// get all documents with shoe size 13 -const n1ql = 'SELECT d.* FROM `bucketName` d WHERE shoeSize = $1'; -const query = N1qlQuery.fromString(n1ql); -bucket.query(query, [13], (err, result) => { - if (err) { - console.log(err); - } else { - console.log(result); - } -}); +app.listen(3000); ``` ```mjs title="index.mjs" -import couchbase from 'couchbase'; +import express from 'express'; +import { DatabaseSync } from 'node:sqlite'; -const bucket = new couchbase.Cluster('http://localhost:8091').openBucket('bucketName'); +const app = express(); +const db = new DatabaseSync('app.db'); -// add a document to a bucket -bucket.insert('document-key', { name: 'Matt', shoeSize: 13 }, (err, result) => { - if (err) { - console.log(err); - } else { - console.log(result); - } -}); +db.exec('CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT NOT NULL)'); +db.prepare('INSERT INTO users (id, name) VALUES (?, ?) ON CONFLICT(id) DO NOTHING').run(1, 'Alice'); +const findUser = db.prepare('SELECT id, name FROM users WHERE id = ?'); -// get all documents with shoe size 13 -const n1ql = 'SELECT d.* FROM `bucketName` d WHERE shoeSize = $1'; -const query = N1qlQuery.fromString(n1ql); -bucket.query(query, [13], (err, result) => { - if (err) { - console.log(err); - } else { - console.log(result); +app.get('/users/:id', (req, res) => { + const user = findUser.get(req.params.id); + if (!user) { + return res.status(404).json({ error: 'User not found' }); } + res.json(user); }); + +app.listen(3000); ``` -## CouchDB +```ts title="index.ts" +import express from 'express'; +import { DatabaseSync } from 'node:sqlite'; -**Module**: [nano](https://github.com/dscape/nano) +interface User { + id: number; + name: string; +} -### Instalación +const app = express(); +const db = new DatabaseSync('app.db'); - +db.exec('CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT NOT NULL)'); +db.prepare('INSERT INTO users (id, name) VALUES (?, ?) ON CONFLICT(id) DO NOTHING').run(1, 'Alice'); +const findUser = db.prepare('SELECT id, name FROM users WHERE id = ?'); -### Ejemplo - -```js title="index.js" -const nano = require('nano')('http://localhost:5984'); -nano.db.create('books'); -const books = nano.db.use('books'); - -// Insert a book document in the books database -books.insert({ name: 'The Art of war' }, null, (err, body) => { - if (err) { - console.log(err); - } else { - console.log(body); +app.get('/users/:id', (req, res) => { + const user = findUser.get(req.params.id) as User | undefined; + if (!user) { + return res.status(404).json({ error: 'User not found' }); } + res.json(user); }); -// Get a list of all books -books.list((err, body) => { - if (err) { - console.log(err); - } else { - console.log(body.rows); - } -}); +app.listen(3000); ``` -## LevelDB +## Other databases -**Module**: [levelup](https://github.com/rvagg/node-levelup) +Every other database follows the same pattern: install the Node.js driver from npm, create a client or connection pool once when the app starts, and use it in your route handlers. Refer to the official documentation of each driver for connection options and API details: -### Instalación +- PostgreSQL: [pg](https://node-postgres.com/) or [postgres](https://github.com/porsager/postgres) +- MySQL and MariaDB: [mysql2](https://sidorares.github.io/node-mysql2/docs) or [mariadb](https://mariadb.com/docs/connectors/mariadb-connector-nodejs) +- MongoDB: [mongodb](https://www.mongodb.com/docs/drivers/node/current/) +- Redis: [redis](https://redis.io/docs/latest/develop/clients/nodejs/) or [ioredis](https://github.com/redis/ioredis) +- SQL Server: [mssql](https://github.com/tediousjs/node-mssql) +- Oracle: [oracledb](https://www.npmjs.com/package/oracledb) +- Cassandra: [cassandra-driver](https://docs.datastax.com/en/developer/nodejs-driver/latest/) +- Elasticsearch: [@elastic/elasticsearch](https://www.elastic.co/docs/reference/elasticsearch/clients/javascript) - - -### Ejemplo +For any database not listed here, search for its driver on the [npm](https://www.npmjs.com/) site. -```cjs title="index.cjs" -const levelup = require('levelup'); -const db = levelup('./mydb'); +## ORMs and query builders -db.put('name', 'LevelUP', (err) => { - if (err) return console.log('Ooops!', err); +The example above uses a database driver directly, which is a good fit for small apps and for learning how things work. As an application grows, an ORM (object-relational mapper) or a query builder can take over concerns such as schema definitions, migrations, relations between tables, and input mapping, while reducing boilerplate. Many of them also provide type safety when used with TypeScript. - db.get('name', (err, value) => { - if (err) return console.log('Ooops!', err); +Popular options in the Node.js ecosystem include: - console.log(`name=${value}`); - }); -}); -``` +- [Prisma](https://www.prisma.io/): a type-safe ORM with a schema-first workflow and built-in migrations. Supports PostgreSQL, MySQL, SQLite, SQL Server, MongoDB, and CockroachDB. +- [Drizzle ORM](https://orm.drizzle.team/): a lightweight, TypeScript-first ORM with a SQL-like query API and a migrations toolkit. +- [Sequelize](https://sequelize.org/): a mature, promise-based ORM for PostgreSQL, MySQL, MariaDB, SQLite, and SQL Server. +- [TypeORM](https://typeorm.io/): an ORM built around decorators and entities, supporting many SQL databases and MongoDB. +- [Mongoose](https://mongoosejs.com/): an ODM (object document mapper) for MongoDB. +- [Knex.js](https://knexjs.org/): a flexible SQL query builder with migrations support, useful when you want control over your queries without writing raw strings. +- [Kysely](https://kysely.dev/): a type-safe SQL query builder for TypeScript. -```mjs title="index.mjs" -import levelup from 'levelup'; +Each ORM has its own API, supported databases, and approach to schemas, migrations, and queries. Features and setup requirements vary, so consult each project's documentation before choosing one. Query builders keep you close to SQL, while full ORMs abstract more of it away. Pick the level of abstraction that matches your team and project, and prefer tools that are actively maintained. -const db = levelup('./mydb'); +## Best practices -db.put('name', 'LevelUP', (err) => { - if (err) return console.log('Ooops!', err); +Adapt these practices to the database and library you choose. The snippets below illustrate separate parts of an application. The PostgreSQL snippets assume you have installed the [pg](https://node-postgres.com/) driver and created a shared pool: - db.get('name', (err, value) => { - if (err) return console.log('Ooops!', err); +```js +import { Pool } from 'pg'; - console.log(`name=${value}`); - }); -}); +const pool = new Pool({ connectionString: process.env.DATABASE_URL }); ``` -## MySQL - -**Module**: [mysql](https://github.com/felixge/node-mysql/) - -### Instalación - - - -### Ejemplo - -```cjs title="index.cjs" -const mysql = require('mysql'); -const connection = mysql.createConnection({ - host: 'localhost', - user: 'dbuser', - password: 's3kreee7', - database: 'my_db', -}); +### Create connections once and reuse them -connection.connect(); +Opening a database connection is expensive. Many drivers provide a connection pool that creates connections as needed and reuses them across requests. Create the pool (or client) once when the app starts. Acquire connections from that shared pool as needed in your route handlers, for example for a transaction, and always release them in a `finally` block. For individual queries, `pool.query()` in `pg` handles acquiring and releasing a connection for you. -connection.query('SELECT 1 + 1 AS solution', (err, rows, fields) => { - if (err) throw err; +### Use parameterized queries - console.log('The solution is: ', rows[0].solution); -}); +Never build SQL queries by concatenating or interpolating user input into the query string. Doing so exposes your app to SQL injection. Use your driver's parameterized queries or prepared statements to pass user input separately from the query itself. -connection.end(); +```js +// Vulnerable: user input becomes part of the query +const user = await pool.query(`SELECT * FROM users WHERE id = ${req.params.id}`); ``` -```mjs title="index.mjs" -import mysql from 'mysql'; - -const connection = mysql.createConnection({ - host: 'localhost', - user: 'dbuser', - password: 's3kreee7', - database: 'my_db', -}); - -connection.connect(); - -connection.query('SELECT 1 + 1 AS solution', (err, rows, fields) => { - if (err) throw err; - - console.log('The solution is: ', rows[0].solution); -}); - -connection.end(); +```js +// Safe: user input is passed as a parameter +const user = await pool.query('SELECT * FROM users WHERE id = $1', [req.params.id]); ``` -## MongoDB - -**Module**: [mongodb](https://github.com/mongodb/node-mongodb-native) - -### Instalación - - - -### Example (v2.\*) - -```js title="index.js" -const MongoClient = require('mongodb').MongoClient; +Also validate user input before it reaches your queries, both for security and for data integrity. For NoSQL databases, construct queries using the driver's API and validate the types, fields, and operators you accept. Do not pass user-supplied query objects directly to the database. -MongoClient.connect('mongodb://localhost:27017/animals', (err, db) => { - if (err) throw err; +### Keep credentials out of your code - db.collection('mammals') - .find() - .toArray((err, result) => { - if (err) throw err; +Never hardcode connection strings, usernames, or passwords in your source code, and never commit them to version control. Read them from environment variables or a secrets manager instead: - console.log(result); - }); -}); +```js +const pool = new Pool({ connectionString: process.env.DATABASE_URL }); ``` -### Example (v3.\*) - -```js title="index.js" -const MongoClient = require('mongodb').MongoClient; - -MongoClient.connect('mongodb://localhost:27017/animals', (err, client) => { - if (err) throw err; - - const db = client.db('animals'); - - db.collection('mammals') - .find() - .toArray((err, result) => { - if (err) throw err; +During development, you can keep these values in a `.env` file and load it with Node.js's built-in support for environment files, with no extra dependencies: - console.log(result); - }); -}); +```bash +node --env-file=.env index.js ``` -If you want an object model driver for MongoDB, look at [Mongoose](https://github.com/LearnBoost/mongoose). - -## Neo4j - -**Module**: [neo4j-driver](https://github.com/neo4j/neo4j-javascript-driver) +Make sure `.env` is listed in your `.gitignore` file so it never gets committed. -### Instalación - - - -### Ejemplo +### Handle database errors -```cjs title="index.cjs" -const neo4j = require('neo4j-driver'); -const driver = neo4j.driver('neo4j://localhost:7687', neo4j.auth.basic('neo4j', 'letmein')); - -const session = driver.session(); - -session.readTransaction((tx) => { - return tx - .run('MATCH (n) RETURN count(n) AS count') - .then((res) => { - console.log(res.records[0].get('count')); - }) - .catch((error) => { - console.log(error); - }); -}); -``` - -```mjs title="index.mjs" -import neo4j from 'neo4j-driver'; +Database calls fail: connections drop, queries time out, constraints are violated. In Express 5, errors thrown in async route handlers are passed to your [error-handling middleware](/en/guide/error-handling) automatically, so define one and decide there what to expose to clients. Avoid leaking internal error details, such as query text or stack traces, in API responses. -const driver = neo4j.driver('neo4j://localhost:7687', neo4j.auth.basic('neo4j', 'letmein')); +### Cancel work when the client disconnects -const session = driver.session(); +Since Node.js 24.20.0, every request exposes [req.signal](https://nodejs.org/api/http.html#messagesignal), an `AbortSignal` that is aborted when the client disconnects. Pass it to any cancellation-aware API, such as a database driver that supports abort signals, so your app stops doing work nobody is waiting for: -session.readTransaction((tx) => { - return tx - .run('MATCH (n) RETURN count(n) AS count') - .then((res) => { - console.log(res.records[0].get('count')); - }) - .catch((error) => { - console.log(error); - }); +```js +app.get('/reports', async (req, res) => { + const report = await generateReport({ signal: req.signal }); + res.json(report); }); ``` -## Oracle +### Close connections on shutdown -**Module**: [oracledb](https://github.com/oracle/node-oracledb) +When your app receives a termination signal, stop accepting new requests and close your database connections so in-flight queries can finish cleanly: -### Instalación +```js +const server = app.listen(3000); -NOTE: [See installation prerequisites](https://github.com/oracle/node-oracledb#-installation). - - - -### Ejemplo - -```cjs title="index.cjs" -const oracledb = require('oracledb'); -const config = { - user: '', - password: '', - connectString: 'localhost:1521/orcl', -}; - -async function getEmployee(empId) { - let conn; - - try { - conn = await oracledb.getConnection(config); - - const result = await conn.execute('select * from employees where employee_id = :id', [empId]); - - console.log(result.rows[0]); - } catch (err) { - console.log('Ouch!', err); - } finally { - if (conn) { - // conn assignment worked, need to close - await conn.close(); - } - } -} - -getEmployee(101); -``` - -```mjs title="index.mjs" -import oracledb from 'oracledb'; - -const config = { - user: '', - password: '', - connectString: 'localhost:1521/orcl', -}; - -async function getEmployee(empId) { - let conn; - - try { - conn = await oracledb.getConnection(config); - - const result = await conn.execute('select * from employees where employee_id = :id', [empId]); - - console.log(result.rows[0]); - } catch (err) { - console.log('Ouch!', err); - } finally { - if (conn) { - // conn assignment worked, need to close - await conn.close(); - } - } -} - -getEmployee(101); -``` - -## PostgreSQL - -**Module**: [pg-promise](https://github.com/vitaly-t/pg-promise) - -### Instalación - - - -### Ejemplo - -```js title="index.js" -const pgp = require('pg-promise')(/* options */); -const db = pgp('postgres://username:password@host:port/database'); - -db.one('SELECT $1 AS value', 123) - .then((data) => { - console.log('DATA:', data.value); - }) - .catch((error) => { - console.log('ERROR:', error); +process.on('SIGTERM', () => { + server.close(async () => { + await pool.end(); + process.exit(0); }); -``` - -## Redis - -**Module**: [redis](https://github.com/mranney/node_redis) - -### Instalación - - - -### Ejemplo - -```cjs title="index.cjs" -const redis = require('redis'); -const client = redis.createClient(); - -client.on('error', (err) => { - console.log(`Error ${err}`); -}); - -client.set('string key', 'string val', redis.print); -client.hset('hash key', 'hashtest 1', 'some value', redis.print); -client.hset(['hash key', 'hashtest 2', 'some other value'], redis.print); - -client.hkeys('hash key', (err, replies) => { - console.log(`${replies.length} replies:`); - - replies.forEach((reply, i) => { - console.log(` ${i}: ${reply}`); - }); - - client.quit(); -}); -``` - -```mjs title="index.mjs" -import redis from 'redis'; - -const client = redis.createClient(); - -client.on('error', (err) => { - console.log(`Error ${err}`); -}); - -client.set('string key', 'string val', redis.print); -client.hset('hash key', 'hashtest 1', 'some value', redis.print); -client.hset(['hash key', 'hashtest 2', 'some other value'], redis.print); - -client.hkeys('hash key', (err, replies) => { - console.log(`${replies.length} replies:`); - - replies.forEach((reply, i) => { - console.log(` ${i}: ${reply}`); - }); - - client.quit(); }); ``` -## SQL Server - -**Module**: [tedious](https://github.com/tediousjs/tedious) - -### Instalación - - - -### Ejemplo - -```js title="index.js" -const Connection = require('tedious').Connection; -const Request = require('tedious').Request; - -const config = { - server: 'localhost', - authentication: { - type: 'default', - options: { - userName: 'your_username', // update me - password: 'your_password', // update me - }, - }, -}; - -const connection = new Connection(config); - -connection.on('connect', (err) => { - if (err) { - console.log(err); - } else { - executeStatement(); - } -}); - -function executeStatement() { - request = new Request("select 123, 'hello world'", (err, rowCount) => { - if (err) { - console.log(err); - } else { - console.log(`${rowCount} rows`); - } - connection.close(); - }); - - request.on('row', (columns) => { - columns.forEach((column) => { - if (column.value === null) { - console.log('NULL'); - } else { - console.log(column.value); - } - }); - }); - - connection.execSql(request); -} -``` - -## SQLite - -**Module**: [sqlite3](https://github.com/mapbox/node-sqlite3) - -### Instalación - - - -### Ejemplo - -```js title="index.js" -const sqlite3 = require('sqlite3').verbose(); -const db = new sqlite3.Database(':memory:'); - -db.serialize(() => { - db.run('CREATE TABLE lorem (info TEXT)'); - const stmt = db.prepare('INSERT INTO lorem VALUES (?)'); - - for (let i = 0; i < 10; i++) { - stmt.run(`Ipsum ${i}`); - } - - stmt.finalize(); - - db.each('SELECT rowid AS id, info FROM lorem', (err, row) => { - console.log(`${row.id}: ${row.info}`); - }); -}); - -db.close(); -``` - -## Elasticsearch - -**Module**: [elasticsearch](https://github.com/elastic/elasticsearch-js) - -### Instalación - - - -### Ejemplo - -```cjs title="index.cjs" -const elasticsearch = require('elasticsearch'); -const client = elasticsearch.Client({ - host: 'localhost:9200', -}); - -client - .search({ - index: 'books', - type: 'book', - body: { - query: { - multi_match: { - query: 'express js', - fields: ['title', 'description'], - }, - }, - }, - }) - .then( - (response) => { - const hits = response.hits.hits; - }, - (error) => { - console.trace(error.message); - } - ); -``` - -```mjs title="index.mjs" -import elasticsearch from 'elasticsearch'; - -const client = elasticsearch.Client({ - host: 'localhost:9200', -}); - -client - .search({ - index: 'books', - type: 'book', - body: { - query: { - multi_match: { - query: 'express js', - fields: ['title', 'description'], - }, - }, - }, - }) - .then( - (response) => { - const hits = response.hits.hits; - }, - (error) => { - console.trace(error.message); - } - ); -``` +If you already use an [AbortSignal](https://nodejs.org/api/globals.html#class-abortsignal) to coordinate cancellation across your app, you can pass it to `app.listen` and abort it instead of calling `server.close()` directly. diff --git a/src/content/pages/es/resources/middleware/body-parser.mdx b/src/content/pages/es/resources/middleware/body-parser.mdx index 17b638e06a..eb27994b33 100644 --- a/src/content/pages/es/resources/middleware/body-parser.mdx +++ b/src/content/pages/es/resources/middleware/body-parser.mdx @@ -8,7 +8,7 @@ import Alert from '@components/primitives/Alert/Alert.astro'; import PackageManagerCommand from '@components/patterns/PackageManagerCommand/PackageManagerCommand.astro'; @@ -82,6 +82,12 @@ El objeto `bodyParser` expone varias fábricas para crear middlewares. All middlewares will populate the `req.body` property with the parsed body when the `Content-Type` request header matches the `type` option. +**Note** As of v2, `req.body` is left `undefined` when no body is parsed (for +example when the request has neither `Content-Length` nor `Transfer-Encoding`, +or when `Content-Type` does not match). In v1, `req.body` was always initialized +to `{}`. Code that assumes `req.body` is always an object should use optional +chaining (for example `req.body?.username`) or set a default. + Los varios errores devueltos por este módulo se describen en la [sección de errores](#errors). @@ -126,7 +132,7 @@ La opción `reviver` se pasa directamente a `JSON.parse` como segundo argumento ##### estricto -When set to `true`, will only accept arrays and objects; when `false` will +When set to `true`, it will only accept arrays and objects; when `false`, it will accept anything `JSON.parse` accepts. Por defecto es `true`. ##### tipo @@ -423,7 +429,7 @@ property is set to the encoding that is unsupported. ### The input exceeded the depth -This error occurs when using `bodyParser.urlencoded` with the `extended` property set to `true` and the input exceeds the configured `depth` option. The `status` property is set to `400`. It is recommended to review the `depth` option and evaluate if it requires a higher value. When the `depth` option is set to `32` (default value), the error will not be thrown. +This error occurs when using `bodyParser.urlencoded` with the `extended` property set to `true` and the input exceeds the configured `depth` option. The `status` property is set to `400`. It is recommended to review the `depth` option and evaluate if it requires a higher value. With the default `depth` of `32`, an input with 33 or more levels of nesting gets this error. ## Ejemplos diff --git a/src/content/pages/es/resources/middleware/multer.mdx b/src/content/pages/es/resources/middleware/multer.mdx index 4f8b4fe80b..59253f6965 100644 --- a/src/content/pages/es/resources/middleware/multer.mdx +++ b/src/content/pages/es/resources/middleware/multer.mdx @@ -8,7 +8,7 @@ import Alert from '@components/primitives/Alert/Alert.astro'; import PackageManagerCommand from '@components/patterns/PackageManagerCommand/PackageManagerCommand.astro'; diff --git a/src/content/pages/fr/guide/database-integration.mdx b/src/content/pages/fr/guide/database-integration.mdx index 80cd815556..8217976248 100644 --- a/src/content/pages/fr/guide/database-integration.mdx +++ b/src/content/pages/fr/guide/database-integration.mdx @@ -1,656 +1,196 @@ --- title: Intégration de la base de données -description: Découvrez comment intégrer différentes bases de données avec les applications Express.js, y compris des exemples de configuration pour MongoDB, MySQL, PostgreSQL, et plus encore. +description: Learn how to connect databases to Express.js applications, when to use an ORM, and best practices for connection pooling, security, and error handling. --- -import Alert from '@components/primitives/Alert/Alert.astro'; -import PackageManagerCommand from '@components/patterns/PackageManagerCommand/PackageManagerCommand.astro'; +To connect a database to an Express app simply load the desired Node.js driver or ORM in your app. The general pattern is shown below using SQLite as an example. -Ajouter la possibilité de connecter des bases de données à des applications Express est juste une question de charger un pilote Node.js approprié pour la base de données dans votre application. Ce document explique brièvement comment ajouter et utiliser certains des modules Node.js les plus populaires pour les systèmes de base de données dans votre application Express : +There are also a few [best practices](#best-practices) to follow, regardless of the database you're using. -- [Cassandra](#cassandra) -- [Couchbase](#couchbase) -- [CouchDB](#couchdb) -- [LevelDB](#leveldb) -- [MySQL](#mysql) -- [MongoDB](#mongodb) -- [Neo4j](#neo4j) -- [Oracle](#oracle) -- [PostgreSQL](#postgresql) -- [Redis](#redis) -- [Serveur SQL](#sql-server) -- [SQLite](#sqlite) -- [Elasticsearch](#elasticsearch) - - - -Ces pilotes de bases de données sont parmi les nombreux qui sont disponibles. Pour d'autres options, recherchez sur le site -[npm](https://www.npmjs.com/). - - - -## Cassandra - -**Module** : [cassandra-driver](https://github.com/datastax/nodejs-driver) - -### Installation +## SQLite - +Node.js includes a built-in SQLite client, [node:sqlite](https://nodejs.org/api/sqlite.html), available without a flag since Node.js 22.13.0. No installation is required. For older Node.js versions, [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) offers a similar synchronous API. ### Exemple -```cjs title="index.cjs" -const cassandra = require('cassandra-driver'); -const client = new cassandra.Client({ contactPoints: ['localhost'] }); - -client.execute('select key from system.local', (err, result) => { - if (err) throw err; - console.log(result.rows[0]); -}); -``` +This example creates a sample user that you can retrieve at `http://localhost:3000/users/1`. It uses a synchronous SQLite API for simplicity: long-running queries block the thread handling requests. For workloads with long-running queries, consider moving database work to a worker thread or using an asynchronous database driver. -```mjs title="index.mjs" -import cassandra from 'cassandra-driver'; - -const client = new cassandra.Client({ contactPoints: ['localhost'] }); - -client.execute('select key from system.local', (err, result) => { - if (err) throw err; - console.log(result.rows[0]); -}); -``` - -## Couchbase - -**Module**: [couchnode](https://github.com/couchbase/couchnode) - -### Installation +```cjs title="index.cjs" +const express = require('express'); +const { DatabaseSync } = require('node:sqlite'); - +const app = express(); +const db = new DatabaseSync('app.db'); -### Exemple +db.exec('CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT NOT NULL)'); +db.prepare('INSERT INTO users (id, name) VALUES (?, ?) ON CONFLICT(id) DO NOTHING').run(1, 'Alice'); +const findUser = db.prepare('SELECT id, name FROM users WHERE id = ?'); -```cjs title="index.cjs" -const couchbase = require('couchbase'); -const bucket = new couchbase.Cluster('http://localhost:8091').openBucket('bucketName'); - -// add a document to a bucket -bucket.insert('document-key', { name: 'Matt', shoeSize: 13 }, (err, result) => { - if (err) { - console.log(err); - } else { - console.log(result); +app.get('/users/:id', (req, res) => { + const user = findUser.get(req.params.id); + if (!user) { + return res.status(404).json({ error: 'User not found' }); } + res.json(user); }); -// get all documents with shoe size 13 -const n1ql = 'SELECT d.* FROM `bucketName` d WHERE shoeSize = $1'; -const query = N1qlQuery.fromString(n1ql); -bucket.query(query, [13], (err, result) => { - if (err) { - console.log(err); - } else { - console.log(result); - } -}); +app.listen(3000); ``` ```mjs title="index.mjs" -import couchbase from 'couchbase'; +import express from 'express'; +import { DatabaseSync } from 'node:sqlite'; -const bucket = new couchbase.Cluster('http://localhost:8091').openBucket('bucketName'); +const app = express(); +const db = new DatabaseSync('app.db'); -// add a document to a bucket -bucket.insert('document-key', { name: 'Matt', shoeSize: 13 }, (err, result) => { - if (err) { - console.log(err); - } else { - console.log(result); - } -}); +db.exec('CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT NOT NULL)'); +db.prepare('INSERT INTO users (id, name) VALUES (?, ?) ON CONFLICT(id) DO NOTHING').run(1, 'Alice'); +const findUser = db.prepare('SELECT id, name FROM users WHERE id = ?'); -// get all documents with shoe size 13 -const n1ql = 'SELECT d.* FROM `bucketName` d WHERE shoeSize = $1'; -const query = N1qlQuery.fromString(n1ql); -bucket.query(query, [13], (err, result) => { - if (err) { - console.log(err); - } else { - console.log(result); +app.get('/users/:id', (req, res) => { + const user = findUser.get(req.params.id); + if (!user) { + return res.status(404).json({ error: 'User not found' }); } + res.json(user); }); + +app.listen(3000); ``` -## CouchDB +```ts title="index.ts" +import express from 'express'; +import { DatabaseSync } from 'node:sqlite'; -**Module**: [nano](https://github.com/dscape/nano) +interface User { + id: number; + name: string; +} -### Installation +const app = express(); +const db = new DatabaseSync('app.db'); - +db.exec('CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT NOT NULL)'); +db.prepare('INSERT INTO users (id, name) VALUES (?, ?) ON CONFLICT(id) DO NOTHING').run(1, 'Alice'); +const findUser = db.prepare('SELECT id, name FROM users WHERE id = ?'); -### Exemple - -```js title="index.js" -const nano = require('nano')('http://localhost:5984'); -nano.db.create('books'); -const books = nano.db.use('books'); - -// Insert a book document in the books database -books.insert({ name: 'The Art of war' }, null, (err, body) => { - if (err) { - console.log(err); - } else { - console.log(body); +app.get('/users/:id', (req, res) => { + const user = findUser.get(req.params.id) as User | undefined; + if (!user) { + return res.status(404).json({ error: 'User not found' }); } + res.json(user); }); -// Get a list of all books -books.list((err, body) => { - if (err) { - console.log(err); - } else { - console.log(body.rows); - } -}); +app.listen(3000); ``` -## LevelDB +## Other databases -**Module**: [levelup](https://github.com/rvagg/node-levelup) +Every other database follows the same pattern: install the Node.js driver from npm, create a client or connection pool once when the app starts, and use it in your route handlers. Refer to the official documentation of each driver for connection options and API details: -### Installation +- PostgreSQL: [pg](https://node-postgres.com/) or [postgres](https://github.com/porsager/postgres) +- MySQL and MariaDB: [mysql2](https://sidorares.github.io/node-mysql2/docs) or [mariadb](https://mariadb.com/docs/connectors/mariadb-connector-nodejs) +- MongoDB: [mongodb](https://www.mongodb.com/docs/drivers/node/current/) +- Redis: [redis](https://redis.io/docs/latest/develop/clients/nodejs/) or [ioredis](https://github.com/redis/ioredis) +- SQL Server: [mssql](https://github.com/tediousjs/node-mssql) +- Oracle: [oracledb](https://www.npmjs.com/package/oracledb) +- Cassandra: [cassandra-driver](https://docs.datastax.com/en/developer/nodejs-driver/latest/) +- Elasticsearch: [@elastic/elasticsearch](https://www.elastic.co/docs/reference/elasticsearch/clients/javascript) - - -### Exemple +For any database not listed here, search for its driver on the [npm](https://www.npmjs.com/) site. -```cjs title="index.cjs" -const levelup = require('levelup'); -const db = levelup('./mydb'); +## ORMs and query builders -db.put('name', 'LevelUP', (err) => { - if (err) return console.log('Ooops!', err); +The example above uses a database driver directly, which is a good fit for small apps and for learning how things work. As an application grows, an ORM (object-relational mapper) or a query builder can take over concerns such as schema definitions, migrations, relations between tables, and input mapping, while reducing boilerplate. Many of them also provide type safety when used with TypeScript. - db.get('name', (err, value) => { - if (err) return console.log('Ooops!', err); +Popular options in the Node.js ecosystem include: - console.log(`name=${value}`); - }); -}); -``` +- [Prisma](https://www.prisma.io/): a type-safe ORM with a schema-first workflow and built-in migrations. Supports PostgreSQL, MySQL, SQLite, SQL Server, MongoDB, and CockroachDB. +- [Drizzle ORM](https://orm.drizzle.team/): a lightweight, TypeScript-first ORM with a SQL-like query API and a migrations toolkit. +- [Sequelize](https://sequelize.org/): a mature, promise-based ORM for PostgreSQL, MySQL, MariaDB, SQLite, and SQL Server. +- [TypeORM](https://typeorm.io/): an ORM built around decorators and entities, supporting many SQL databases and MongoDB. +- [Mongoose](https://mongoosejs.com/): an ODM (object document mapper) for MongoDB. +- [Knex.js](https://knexjs.org/): a flexible SQL query builder with migrations support, useful when you want control over your queries without writing raw strings. +- [Kysely](https://kysely.dev/): a type-safe SQL query builder for TypeScript. -```mjs title="index.mjs" -import levelup from 'levelup'; +Each ORM has its own API, supported databases, and approach to schemas, migrations, and queries. Features and setup requirements vary, so consult each project's documentation before choosing one. Query builders keep you close to SQL, while full ORMs abstract more of it away. Pick the level of abstraction that matches your team and project, and prefer tools that are actively maintained. -const db = levelup('./mydb'); +## Best practices -db.put('name', 'LevelUP', (err) => { - if (err) return console.log('Ooops!', err); +Adapt these practices to the database and library you choose. The snippets below illustrate separate parts of an application. The PostgreSQL snippets assume you have installed the [pg](https://node-postgres.com/) driver and created a shared pool: - db.get('name', (err, value) => { - if (err) return console.log('Ooops!', err); +```js +import { Pool } from 'pg'; - console.log(`name=${value}`); - }); -}); +const pool = new Pool({ connectionString: process.env.DATABASE_URL }); ``` -## MySQL - -**Module**: [mysql](https://github.com/felixge/node-mysql/) - -### Installation - - - -### Exemple - -```cjs title="index.cjs" -const mysql = require('mysql'); -const connection = mysql.createConnection({ - host: 'localhost', - user: 'dbuser', - password: 's3kreee7', - database: 'my_db', -}); +### Create connections once and reuse them -connection.connect(); +Opening a database connection is expensive. Many drivers provide a connection pool that creates connections as needed and reuses them across requests. Create the pool (or client) once when the app starts. Acquire connections from that shared pool as needed in your route handlers, for example for a transaction, and always release them in a `finally` block. For individual queries, `pool.query()` in `pg` handles acquiring and releasing a connection for you. -connection.query('SELECT 1 + 1 AS solution', (err, rows, fields) => { - if (err) throw err; +### Use parameterized queries - console.log('The solution is: ', rows[0].solution); -}); +Never build SQL queries by concatenating or interpolating user input into the query string. Doing so exposes your app to SQL injection. Use your driver's parameterized queries or prepared statements to pass user input separately from the query itself. -connection.end(); +```js +// Vulnerable: user input becomes part of the query +const user = await pool.query(`SELECT * FROM users WHERE id = ${req.params.id}`); ``` -```mjs title="index.mjs" -import mysql from 'mysql'; - -const connection = mysql.createConnection({ - host: 'localhost', - user: 'dbuser', - password: 's3kreee7', - database: 'my_db', -}); - -connection.connect(); - -connection.query('SELECT 1 + 1 AS solution', (err, rows, fields) => { - if (err) throw err; - - console.log('The solution is: ', rows[0].solution); -}); - -connection.end(); +```js +// Safe: user input is passed as a parameter +const user = await pool.query('SELECT * FROM users WHERE id = $1', [req.params.id]); ``` -## MongoDB - -**Module**: [mongodb](https://github.com/mongodb/node-mongodb-native) - -### Installation - - - -### Example (v2.\*) - -```js title="index.js" -const MongoClient = require('mongodb').MongoClient; +Also validate user input before it reaches your queries, both for security and for data integrity. For NoSQL databases, construct queries using the driver's API and validate the types, fields, and operators you accept. Do not pass user-supplied query objects directly to the database. -MongoClient.connect('mongodb://localhost:27017/animals', (err, db) => { - if (err) throw err; +### Keep credentials out of your code - db.collection('mammals') - .find() - .toArray((err, result) => { - if (err) throw err; +Never hardcode connection strings, usernames, or passwords in your source code, and never commit them to version control. Read them from environment variables or a secrets manager instead: - console.log(result); - }); -}); +```js +const pool = new Pool({ connectionString: process.env.DATABASE_URL }); ``` -### Example (v3.\*) - -```js title="index.js" -const MongoClient = require('mongodb').MongoClient; - -MongoClient.connect('mongodb://localhost:27017/animals', (err, client) => { - if (err) throw err; - - const db = client.db('animals'); - - db.collection('mammals') - .find() - .toArray((err, result) => { - if (err) throw err; +During development, you can keep these values in a `.env` file and load it with Node.js's built-in support for environment files, with no extra dependencies: - console.log(result); - }); -}); +```bash +node --env-file=.env index.js ``` -If you want an object model driver for MongoDB, look at [Mongoose](https://github.com/LearnBoost/mongoose). - -## Neo4j - -**Module**: [neo4j-driver](https://github.com/neo4j/neo4j-javascript-driver) +Make sure `.env` is listed in your `.gitignore` file so it never gets committed. -### Installation - - - -### Exemple +### Handle database errors -```cjs title="index.cjs" -const neo4j = require('neo4j-driver'); -const driver = neo4j.driver('neo4j://localhost:7687', neo4j.auth.basic('neo4j', 'letmein')); - -const session = driver.session(); - -session.readTransaction((tx) => { - return tx - .run('MATCH (n) RETURN count(n) AS count') - .then((res) => { - console.log(res.records[0].get('count')); - }) - .catch((error) => { - console.log(error); - }); -}); -``` - -```mjs title="index.mjs" -import neo4j from 'neo4j-driver'; +Database calls fail: connections drop, queries time out, constraints are violated. In Express 5, errors thrown in async route handlers are passed to your [error-handling middleware](/en/guide/error-handling) automatically, so define one and decide there what to expose to clients. Avoid leaking internal error details, such as query text or stack traces, in API responses. -const driver = neo4j.driver('neo4j://localhost:7687', neo4j.auth.basic('neo4j', 'letmein')); +### Cancel work when the client disconnects -const session = driver.session(); +Since Node.js 24.20.0, every request exposes [req.signal](https://nodejs.org/api/http.html#messagesignal), an `AbortSignal` that is aborted when the client disconnects. Pass it to any cancellation-aware API, such as a database driver that supports abort signals, so your app stops doing work nobody is waiting for: -session.readTransaction((tx) => { - return tx - .run('MATCH (n) RETURN count(n) AS count') - .then((res) => { - console.log(res.records[0].get('count')); - }) - .catch((error) => { - console.log(error); - }); +```js +app.get('/reports', async (req, res) => { + const report = await generateReport({ signal: req.signal }); + res.json(report); }); ``` -## Oracle +### Close connections on shutdown -**Module**: [oracledb](https://github.com/oracle/node-oracledb) +When your app receives a termination signal, stop accepting new requests and close your database connections so in-flight queries can finish cleanly: -### Installation +```js +const server = app.listen(3000); -NOTE: [See installation prerequisites](https://github.com/oracle/node-oracledb#-installation). - - - -### Exemple - -```cjs title="index.cjs" -const oracledb = require('oracledb'); -const config = { - user: '', - password: '', - connectString: 'localhost:1521/orcl', -}; - -async function getEmployee(empId) { - let conn; - - try { - conn = await oracledb.getConnection(config); - - const result = await conn.execute('select * from employees where employee_id = :id', [empId]); - - console.log(result.rows[0]); - } catch (err) { - console.log('Ouch!', err); - } finally { - if (conn) { - // conn assignment worked, need to close - await conn.close(); - } - } -} - -getEmployee(101); -``` - -```mjs title="index.mjs" -import oracledb from 'oracledb'; - -const config = { - user: '', - password: '', - connectString: 'localhost:1521/orcl', -}; - -async function getEmployee(empId) { - let conn; - - try { - conn = await oracledb.getConnection(config); - - const result = await conn.execute('select * from employees where employee_id = :id', [empId]); - - console.log(result.rows[0]); - } catch (err) { - console.log('Ouch!', err); - } finally { - if (conn) { - // conn assignment worked, need to close - await conn.close(); - } - } -} - -getEmployee(101); -``` - -## PostgreSQL - -**Module**: [pg-promise](https://github.com/vitaly-t/pg-promise) - -### Installation - - - -### Exemple - -```js title="index.js" -const pgp = require('pg-promise')(/* options */); -const db = pgp('postgres://username:password@host:port/database'); - -db.one('SELECT $1 AS value', 123) - .then((data) => { - console.log('DATA:', data.value); - }) - .catch((error) => { - console.log('ERROR:', error); +process.on('SIGTERM', () => { + server.close(async () => { + await pool.end(); + process.exit(0); }); -``` - -## Redis - -**Module**: [redis](https://github.com/mranney/node_redis) - -### Installation - - - -### Exemple - -```cjs title="index.cjs" -const redis = require('redis'); -const client = redis.createClient(); - -client.on('error', (err) => { - console.log(`Error ${err}`); -}); - -client.set('string key', 'string val', redis.print); -client.hset('hash key', 'hashtest 1', 'some value', redis.print); -client.hset(['hash key', 'hashtest 2', 'some other value'], redis.print); - -client.hkeys('hash key', (err, replies) => { - console.log(`${replies.length} replies:`); - - replies.forEach((reply, i) => { - console.log(` ${i}: ${reply}`); - }); - - client.quit(); -}); -``` - -```mjs title="index.mjs" -import redis from 'redis'; - -const client = redis.createClient(); - -client.on('error', (err) => { - console.log(`Error ${err}`); -}); - -client.set('string key', 'string val', redis.print); -client.hset('hash key', 'hashtest 1', 'some value', redis.print); -client.hset(['hash key', 'hashtest 2', 'some other value'], redis.print); - -client.hkeys('hash key', (err, replies) => { - console.log(`${replies.length} replies:`); - - replies.forEach((reply, i) => { - console.log(` ${i}: ${reply}`); - }); - - client.quit(); }); ``` -## SQL Server - -**Module**: [tedious](https://github.com/tediousjs/tedious) - -### Installation - - - -### Exemple - -```js title="index.js" -const Connection = require('tedious').Connection; -const Request = require('tedious').Request; - -const config = { - server: 'localhost', - authentication: { - type: 'default', - options: { - userName: 'your_username', // update me - password: 'your_password', // update me - }, - }, -}; - -const connection = new Connection(config); - -connection.on('connect', (err) => { - if (err) { - console.log(err); - } else { - executeStatement(); - } -}); - -function executeStatement() { - request = new Request("select 123, 'hello world'", (err, rowCount) => { - if (err) { - console.log(err); - } else { - console.log(`${rowCount} rows`); - } - connection.close(); - }); - - request.on('row', (columns) => { - columns.forEach((column) => { - if (column.value === null) { - console.log('NULL'); - } else { - console.log(column.value); - } - }); - }); - - connection.execSql(request); -} -``` - -## SQLite - -**Module**: [sqlite3](https://github.com/mapbox/node-sqlite3) - -### Installation - - - -### Exemple - -```js title="index.js" -const sqlite3 = require('sqlite3').verbose(); -const db = new sqlite3.Database(':memory:'); - -db.serialize(() => { - db.run('CREATE TABLE lorem (info TEXT)'); - const stmt = db.prepare('INSERT INTO lorem VALUES (?)'); - - for (let i = 0; i < 10; i++) { - stmt.run(`Ipsum ${i}`); - } - - stmt.finalize(); - - db.each('SELECT rowid AS id, info FROM lorem', (err, row) => { - console.log(`${row.id}: ${row.info}`); - }); -}); - -db.close(); -``` - -## Elasticsearch - -**Module**: [elasticsearch](https://github.com/elastic/elasticsearch-js) - -### Installation - - - -### Exemple - -```cjs title="index.cjs" -const elasticsearch = require('elasticsearch'); -const client = elasticsearch.Client({ - host: 'localhost:9200', -}); - -client - .search({ - index: 'books', - type: 'book', - body: { - query: { - multi_match: { - query: 'express js', - fields: ['title', 'description'], - }, - }, - }, - }) - .then( - (response) => { - const hits = response.hits.hits; - }, - (error) => { - console.trace(error.message); - } - ); -``` - -```mjs title="index.mjs" -import elasticsearch from 'elasticsearch'; - -const client = elasticsearch.Client({ - host: 'localhost:9200', -}); - -client - .search({ - index: 'books', - type: 'book', - body: { - query: { - multi_match: { - query: 'express js', - fields: ['title', 'description'], - }, - }, - }, - }) - .then( - (response) => { - const hits = response.hits.hits; - }, - (error) => { - console.trace(error.message); - } - ); -``` +If you already use an [AbortSignal](https://nodejs.org/api/globals.html#class-abortsignal) to coordinate cancellation across your app, you can pass it to `app.listen` and abort it instead of calling `server.close()` directly. diff --git a/src/content/pages/fr/resources/middleware/body-parser.mdx b/src/content/pages/fr/resources/middleware/body-parser.mdx index 3fb1c723ef..11b5030843 100644 --- a/src/content/pages/fr/resources/middleware/body-parser.mdx +++ b/src/content/pages/fr/resources/middleware/body-parser.mdx @@ -8,7 +8,7 @@ import Alert from '@components/primitives/Alert/Alert.astro'; import PackageManagerCommand from '@components/patterns/PackageManagerCommand/PackageManagerCommand.astro'; @@ -82,6 +82,12 @@ L'objet `bodyParser` expose diverses usines pour créer des middlewares. Tous le middlewares rempliront la propriété `req.body` avec le corps analysé quand l'en-tête de requête `Content-Type` correspond à l'option `type`. +**Note** As of v2, `req.body` is left `undefined` when no body is parsed (for +example when the request has neither `Content-Length` nor `Transfer-Encoding`, +or when `Content-Type` does not match). In v1, `req.body` was always initialized +to `{}`. Code that assumes `req.body` is always an object should use optional +chaining (for example `req.body?.username`) or set a default. + Les différentes erreurs retournées par ce module sont décrites dans la section [erreurs section](#errors). @@ -126,8 +132,8 @@ L'option `reviver` est passée directement à `JSON.parse` en tant que deuxième ##### strict -Quand réglé sur `true`, n'acceptera que les tableaux et les objets; quand `false` -acceptera tout ce que `JSON.parse` accepte. `true` par défaut. +When set to `true`, it will only accept arrays and objects; when `false`, it will +accept anything `JSON.parse` accepts. `true` par défaut. ##### Type de type @@ -423,7 +429,7 @@ property is set to the encoding that is unsupported. ### The input exceeded the depth -This error occurs when using `bodyParser.urlencoded` with the `extended` property set to `true` and the input exceeds the configured `depth` option. The `status` property is set to `400`. It is recommended to review the `depth` option and evaluate if it requires a higher value. When the `depth` option is set to `32` (default value), the error will not be thrown. +This error occurs when using `bodyParser.urlencoded` with the `extended` property set to `true` and the input exceeds the configured `depth` option. The `status` property is set to `400`. It is recommended to review the `depth` option and evaluate if it requires a higher value. With the default `depth` of `32`, an input with 33 or more levels of nesting gets this error. ## Exemples diff --git a/src/content/pages/fr/resources/middleware/multer.mdx b/src/content/pages/fr/resources/middleware/multer.mdx index cd83d5159f..d3e8aac289 100644 --- a/src/content/pages/fr/resources/middleware/multer.mdx +++ b/src/content/pages/fr/resources/middleware/multer.mdx @@ -8,7 +8,7 @@ import Alert from '@components/primitives/Alert/Alert.astro'; import PackageManagerCommand from '@components/patterns/PackageManagerCommand/PackageManagerCommand.astro'; diff --git a/src/content/pages/it/guide/database-integration.mdx b/src/content/pages/it/guide/database-integration.mdx index 7139dd6704..d9df44c455 100644 --- a/src/content/pages/it/guide/database-integration.mdx +++ b/src/content/pages/it/guide/database-integration.mdx @@ -1,656 +1,196 @@ --- title: Integrazione database -description: Scopri come integrare vari database con le applicazioni Express.js, inclusi esempi di configurazione per MongoDB, MySQL, PostgreSQL e altro ancora. +description: Learn how to connect databases to Express.js applications, when to use an ORM, and best practices for connection pooling, security, and error handling. --- -import Alert from '@components/primitives/Alert/Alert.astro'; -import PackageManagerCommand from '@components/patterns/PackageManagerCommand/PackageManagerCommand.astro'; +To connect a database to an Express app simply load the desired Node.js driver or ORM in your app. The general pattern is shown below using SQLite as an example. -Aggiungere la possibilità di collegare i database alle applicazioni Express è solo una questione di caricare un driver Node.js appropriato per il database nella tua app. Questo documento spiega brevemente come aggiungere e utilizzare alcuni dei moduli Node.js più popolari per i sistemi di database nell'app Express: +There are also a few [best practices](#best-practices) to follow, regardless of the database you're using. -- [Cassandra](#cassandra) -- [Couchbase](#couchbase) -- [CouchDB](#couchdb) -- [LevelDB](#leveldb) -- [MySQL](#mysql) -- [MongoDB](#mongodb) -- [Neo4j](#neo4j) -- [Oracle](#oracle) -- [PostgreSQL](#postgresql) -- [Redis](#redis) -- [SQL Server](#sql-server) -- [SQLite](#sqlite) -- [Elasticsearch](#elasticsearch) - - - -Questi driver di database sono tra molti che sono disponibili. Per altre opzioni, cerca sul sito -[npm](https://www.npmjs.com/). - - - -## Cassandra - -**Modulo**: [cassandra-driver](https://github.com/datastax/nodejs-driver) - -### Installazione +## SQLite - +Node.js includes a built-in SQLite client, [node:sqlite](https://nodejs.org/api/sqlite.html), available without a flag since Node.js 22.13.0. No installation is required. For older Node.js versions, [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) offers a similar synchronous API. ### Esempio -```cjs title="index.cjs" -const cassandra = require('cassandra-driver'); -const client = new cassandra.Client({ contactPoints: ['localhost'] }); - -client.execute('select key from system.local', (err, result) => { - if (err) throw err; - console.log(result.rows[0]); -}); -``` +This example creates a sample user that you can retrieve at `http://localhost:3000/users/1`. It uses a synchronous SQLite API for simplicity: long-running queries block the thread handling requests. For workloads with long-running queries, consider moving database work to a worker thread or using an asynchronous database driver. -```mjs title="index.mjs" -import cassandra from 'cassandra-driver'; - -const client = new cassandra.Client({ contactPoints: ['localhost'] }); - -client.execute('select key from system.local', (err, result) => { - if (err) throw err; - console.log(result.rows[0]); -}); -``` - -## Couchbase - -**Module**: [couchnode](https://github.com/couchbase/couchnode) - -### Installazione +```cjs title="index.cjs" +const express = require('express'); +const { DatabaseSync } = require('node:sqlite'); - +const app = express(); +const db = new DatabaseSync('app.db'); -### Esempio +db.exec('CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT NOT NULL)'); +db.prepare('INSERT INTO users (id, name) VALUES (?, ?) ON CONFLICT(id) DO NOTHING').run(1, 'Alice'); +const findUser = db.prepare('SELECT id, name FROM users WHERE id = ?'); -```cjs title="index.cjs" -const couchbase = require('couchbase'); -const bucket = new couchbase.Cluster('http://localhost:8091').openBucket('bucketName'); - -// add a document to a bucket -bucket.insert('document-key', { name: 'Matt', shoeSize: 13 }, (err, result) => { - if (err) { - console.log(err); - } else { - console.log(result); +app.get('/users/:id', (req, res) => { + const user = findUser.get(req.params.id); + if (!user) { + return res.status(404).json({ error: 'User not found' }); } + res.json(user); }); -// get all documents with shoe size 13 -const n1ql = 'SELECT d.* FROM `bucketName` d WHERE shoeSize = $1'; -const query = N1qlQuery.fromString(n1ql); -bucket.query(query, [13], (err, result) => { - if (err) { - console.log(err); - } else { - console.log(result); - } -}); +app.listen(3000); ``` ```mjs title="index.mjs" -import couchbase from 'couchbase'; +import express from 'express'; +import { DatabaseSync } from 'node:sqlite'; -const bucket = new couchbase.Cluster('http://localhost:8091').openBucket('bucketName'); +const app = express(); +const db = new DatabaseSync('app.db'); -// add a document to a bucket -bucket.insert('document-key', { name: 'Matt', shoeSize: 13 }, (err, result) => { - if (err) { - console.log(err); - } else { - console.log(result); - } -}); +db.exec('CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT NOT NULL)'); +db.prepare('INSERT INTO users (id, name) VALUES (?, ?) ON CONFLICT(id) DO NOTHING').run(1, 'Alice'); +const findUser = db.prepare('SELECT id, name FROM users WHERE id = ?'); -// get all documents with shoe size 13 -const n1ql = 'SELECT d.* FROM `bucketName` d WHERE shoeSize = $1'; -const query = N1qlQuery.fromString(n1ql); -bucket.query(query, [13], (err, result) => { - if (err) { - console.log(err); - } else { - console.log(result); +app.get('/users/:id', (req, res) => { + const user = findUser.get(req.params.id); + if (!user) { + return res.status(404).json({ error: 'User not found' }); } + res.json(user); }); + +app.listen(3000); ``` -## CouchDB +```ts title="index.ts" +import express from 'express'; +import { DatabaseSync } from 'node:sqlite'; -**Module**: [nano](https://github.com/dscape/nano) +interface User { + id: number; + name: string; +} -### Installazione +const app = express(); +const db = new DatabaseSync('app.db'); - +db.exec('CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT NOT NULL)'); +db.prepare('INSERT INTO users (id, name) VALUES (?, ?) ON CONFLICT(id) DO NOTHING').run(1, 'Alice'); +const findUser = db.prepare('SELECT id, name FROM users WHERE id = ?'); -### Esempio - -```js title="index.js" -const nano = require('nano')('http://localhost:5984'); -nano.db.create('books'); -const books = nano.db.use('books'); - -// Insert a book document in the books database -books.insert({ name: 'The Art of war' }, null, (err, body) => { - if (err) { - console.log(err); - } else { - console.log(body); +app.get('/users/:id', (req, res) => { + const user = findUser.get(req.params.id) as User | undefined; + if (!user) { + return res.status(404).json({ error: 'User not found' }); } + res.json(user); }); -// Get a list of all books -books.list((err, body) => { - if (err) { - console.log(err); - } else { - console.log(body.rows); - } -}); +app.listen(3000); ``` -## LevelDB +## Other databases -**Module**: [levelup](https://github.com/rvagg/node-levelup) +Every other database follows the same pattern: install the Node.js driver from npm, create a client or connection pool once when the app starts, and use it in your route handlers. Refer to the official documentation of each driver for connection options and API details: -### Installazione +- PostgreSQL: [pg](https://node-postgres.com/) or [postgres](https://github.com/porsager/postgres) +- MySQL and MariaDB: [mysql2](https://sidorares.github.io/node-mysql2/docs) or [mariadb](https://mariadb.com/docs/connectors/mariadb-connector-nodejs) +- MongoDB: [mongodb](https://www.mongodb.com/docs/drivers/node/current/) +- Redis: [redis](https://redis.io/docs/latest/develop/clients/nodejs/) or [ioredis](https://github.com/redis/ioredis) +- SQL Server: [mssql](https://github.com/tediousjs/node-mssql) +- Oracle: [oracledb](https://www.npmjs.com/package/oracledb) +- Cassandra: [cassandra-driver](https://docs.datastax.com/en/developer/nodejs-driver/latest/) +- Elasticsearch: [@elastic/elasticsearch](https://www.elastic.co/docs/reference/elasticsearch/clients/javascript) - - -### Esempio +For any database not listed here, search for its driver on the [npm](https://www.npmjs.com/) site. -```cjs title="index.cjs" -const levelup = require('levelup'); -const db = levelup('./mydb'); +## ORMs and query builders -db.put('name', 'LevelUP', (err) => { - if (err) return console.log('Ooops!', err); +The example above uses a database driver directly, which is a good fit for small apps and for learning how things work. As an application grows, an ORM (object-relational mapper) or a query builder can take over concerns such as schema definitions, migrations, relations between tables, and input mapping, while reducing boilerplate. Many of them also provide type safety when used with TypeScript. - db.get('name', (err, value) => { - if (err) return console.log('Ooops!', err); +Popular options in the Node.js ecosystem include: - console.log(`name=${value}`); - }); -}); -``` +- [Prisma](https://www.prisma.io/): a type-safe ORM with a schema-first workflow and built-in migrations. Supports PostgreSQL, MySQL, SQLite, SQL Server, MongoDB, and CockroachDB. +- [Drizzle ORM](https://orm.drizzle.team/): a lightweight, TypeScript-first ORM with a SQL-like query API and a migrations toolkit. +- [Sequelize](https://sequelize.org/): a mature, promise-based ORM for PostgreSQL, MySQL, MariaDB, SQLite, and SQL Server. +- [TypeORM](https://typeorm.io/): an ORM built around decorators and entities, supporting many SQL databases and MongoDB. +- [Mongoose](https://mongoosejs.com/): an ODM (object document mapper) for MongoDB. +- [Knex.js](https://knexjs.org/): a flexible SQL query builder with migrations support, useful when you want control over your queries without writing raw strings. +- [Kysely](https://kysely.dev/): a type-safe SQL query builder for TypeScript. -```mjs title="index.mjs" -import levelup from 'levelup'; +Each ORM has its own API, supported databases, and approach to schemas, migrations, and queries. Features and setup requirements vary, so consult each project's documentation before choosing one. Query builders keep you close to SQL, while full ORMs abstract more of it away. Pick the level of abstraction that matches your team and project, and prefer tools that are actively maintained. -const db = levelup('./mydb'); +## Best practices -db.put('name', 'LevelUP', (err) => { - if (err) return console.log('Ooops!', err); +Adapt these practices to the database and library you choose. The snippets below illustrate separate parts of an application. The PostgreSQL snippets assume you have installed the [pg](https://node-postgres.com/) driver and created a shared pool: - db.get('name', (err, value) => { - if (err) return console.log('Ooops!', err); +```js +import { Pool } from 'pg'; - console.log(`name=${value}`); - }); -}); +const pool = new Pool({ connectionString: process.env.DATABASE_URL }); ``` -## MySQL - -**Module**: [mysql](https://github.com/felixge/node-mysql/) - -### Installazione - - - -### Esempio - -```cjs title="index.cjs" -const mysql = require('mysql'); -const connection = mysql.createConnection({ - host: 'localhost', - user: 'dbuser', - password: 's3kreee7', - database: 'my_db', -}); +### Create connections once and reuse them -connection.connect(); +Opening a database connection is expensive. Many drivers provide a connection pool that creates connections as needed and reuses them across requests. Create the pool (or client) once when the app starts. Acquire connections from that shared pool as needed in your route handlers, for example for a transaction, and always release them in a `finally` block. For individual queries, `pool.query()` in `pg` handles acquiring and releasing a connection for you. -connection.query('SELECT 1 + 1 AS solution', (err, rows, fields) => { - if (err) throw err; +### Use parameterized queries - console.log('The solution is: ', rows[0].solution); -}); +Never build SQL queries by concatenating or interpolating user input into the query string. Doing so exposes your app to SQL injection. Use your driver's parameterized queries or prepared statements to pass user input separately from the query itself. -connection.end(); +```js +// Vulnerable: user input becomes part of the query +const user = await pool.query(`SELECT * FROM users WHERE id = ${req.params.id}`); ``` -```mjs title="index.mjs" -import mysql from 'mysql'; - -const connection = mysql.createConnection({ - host: 'localhost', - user: 'dbuser', - password: 's3kreee7', - database: 'my_db', -}); - -connection.connect(); - -connection.query('SELECT 1 + 1 AS solution', (err, rows, fields) => { - if (err) throw err; - - console.log('The solution is: ', rows[0].solution); -}); - -connection.end(); +```js +// Safe: user input is passed as a parameter +const user = await pool.query('SELECT * FROM users WHERE id = $1', [req.params.id]); ``` -## MongoDB - -**Module**: [mongodb](https://github.com/mongodb/node-mongodb-native) - -### Installazione - - - -### Example (v2.\*) - -```js title="index.js" -const MongoClient = require('mongodb').MongoClient; +Also validate user input before it reaches your queries, both for security and for data integrity. For NoSQL databases, construct queries using the driver's API and validate the types, fields, and operators you accept. Do not pass user-supplied query objects directly to the database. -MongoClient.connect('mongodb://localhost:27017/animals', (err, db) => { - if (err) throw err; +### Keep credentials out of your code - db.collection('mammals') - .find() - .toArray((err, result) => { - if (err) throw err; +Never hardcode connection strings, usernames, or passwords in your source code, and never commit them to version control. Read them from environment variables or a secrets manager instead: - console.log(result); - }); -}); +```js +const pool = new Pool({ connectionString: process.env.DATABASE_URL }); ``` -### Example (v3.\*) - -```js title="index.js" -const MongoClient = require('mongodb').MongoClient; - -MongoClient.connect('mongodb://localhost:27017/animals', (err, client) => { - if (err) throw err; - - const db = client.db('animals'); - - db.collection('mammals') - .find() - .toArray((err, result) => { - if (err) throw err; +During development, you can keep these values in a `.env` file and load it with Node.js's built-in support for environment files, with no extra dependencies: - console.log(result); - }); -}); +```bash +node --env-file=.env index.js ``` -If you want an object model driver for MongoDB, look at [Mongoose](https://github.com/LearnBoost/mongoose). - -## Neo4j - -**Module**: [neo4j-driver](https://github.com/neo4j/neo4j-javascript-driver) +Make sure `.env` is listed in your `.gitignore` file so it never gets committed. -### Installazione - - - -### Esempio +### Handle database errors -```cjs title="index.cjs" -const neo4j = require('neo4j-driver'); -const driver = neo4j.driver('neo4j://localhost:7687', neo4j.auth.basic('neo4j', 'letmein')); - -const session = driver.session(); - -session.readTransaction((tx) => { - return tx - .run('MATCH (n) RETURN count(n) AS count') - .then((res) => { - console.log(res.records[0].get('count')); - }) - .catch((error) => { - console.log(error); - }); -}); -``` - -```mjs title="index.mjs" -import neo4j from 'neo4j-driver'; +Database calls fail: connections drop, queries time out, constraints are violated. In Express 5, errors thrown in async route handlers are passed to your [error-handling middleware](/en/guide/error-handling) automatically, so define one and decide there what to expose to clients. Avoid leaking internal error details, such as query text or stack traces, in API responses. -const driver = neo4j.driver('neo4j://localhost:7687', neo4j.auth.basic('neo4j', 'letmein')); +### Cancel work when the client disconnects -const session = driver.session(); +Since Node.js 24.20.0, every request exposes [req.signal](https://nodejs.org/api/http.html#messagesignal), an `AbortSignal` that is aborted when the client disconnects. Pass it to any cancellation-aware API, such as a database driver that supports abort signals, so your app stops doing work nobody is waiting for: -session.readTransaction((tx) => { - return tx - .run('MATCH (n) RETURN count(n) AS count') - .then((res) => { - console.log(res.records[0].get('count')); - }) - .catch((error) => { - console.log(error); - }); +```js +app.get('/reports', async (req, res) => { + const report = await generateReport({ signal: req.signal }); + res.json(report); }); ``` -## Oracle +### Close connections on shutdown -**Module**: [oracledb](https://github.com/oracle/node-oracledb) +When your app receives a termination signal, stop accepting new requests and close your database connections so in-flight queries can finish cleanly: -### Installazione +```js +const server = app.listen(3000); -NOTE: [See installation prerequisites](https://github.com/oracle/node-oracledb#-installation). - - - -### Esempio - -```cjs title="index.cjs" -const oracledb = require('oracledb'); -const config = { - user: '', - password: '', - connectString: 'localhost:1521/orcl', -}; - -async function getEmployee(empId) { - let conn; - - try { - conn = await oracledb.getConnection(config); - - const result = await conn.execute('select * from employees where employee_id = :id', [empId]); - - console.log(result.rows[0]); - } catch (err) { - console.log('Ouch!', err); - } finally { - if (conn) { - // conn assignment worked, need to close - await conn.close(); - } - } -} - -getEmployee(101); -``` - -```mjs title="index.mjs" -import oracledb from 'oracledb'; - -const config = { - user: '', - password: '', - connectString: 'localhost:1521/orcl', -}; - -async function getEmployee(empId) { - let conn; - - try { - conn = await oracledb.getConnection(config); - - const result = await conn.execute('select * from employees where employee_id = :id', [empId]); - - console.log(result.rows[0]); - } catch (err) { - console.log('Ouch!', err); - } finally { - if (conn) { - // conn assignment worked, need to close - await conn.close(); - } - } -} - -getEmployee(101); -``` - -## PostgreSQL - -**Module**: [pg-promise](https://github.com/vitaly-t/pg-promise) - -### Installazione - - - -### Esempio - -```js title="index.js" -const pgp = require('pg-promise')(/* options */); -const db = pgp('postgres://username:password@host:port/database'); - -db.one('SELECT $1 AS value', 123) - .then((data) => { - console.log('DATA:', data.value); - }) - .catch((error) => { - console.log('ERROR:', error); +process.on('SIGTERM', () => { + server.close(async () => { + await pool.end(); + process.exit(0); }); -``` - -## Redis - -**Module**: [redis](https://github.com/mranney/node_redis) - -### Installazione - - - -### Esempio - -```cjs title="index.cjs" -const redis = require('redis'); -const client = redis.createClient(); - -client.on('error', (err) => { - console.log(`Error ${err}`); -}); - -client.set('string key', 'string val', redis.print); -client.hset('hash key', 'hashtest 1', 'some value', redis.print); -client.hset(['hash key', 'hashtest 2', 'some other value'], redis.print); - -client.hkeys('hash key', (err, replies) => { - console.log(`${replies.length} replies:`); - - replies.forEach((reply, i) => { - console.log(` ${i}: ${reply}`); - }); - - client.quit(); -}); -``` - -```mjs title="index.mjs" -import redis from 'redis'; - -const client = redis.createClient(); - -client.on('error', (err) => { - console.log(`Error ${err}`); -}); - -client.set('string key', 'string val', redis.print); -client.hset('hash key', 'hashtest 1', 'some value', redis.print); -client.hset(['hash key', 'hashtest 2', 'some other value'], redis.print); - -client.hkeys('hash key', (err, replies) => { - console.log(`${replies.length} replies:`); - - replies.forEach((reply, i) => { - console.log(` ${i}: ${reply}`); - }); - - client.quit(); }); ``` -## SQL Server - -**Module**: [tedious](https://github.com/tediousjs/tedious) - -### Installazione - - - -### Esempio - -```js title="index.js" -const Connection = require('tedious').Connection; -const Request = require('tedious').Request; - -const config = { - server: 'localhost', - authentication: { - type: 'default', - options: { - userName: 'your_username', // update me - password: 'your_password', // update me - }, - }, -}; - -const connection = new Connection(config); - -connection.on('connect', (err) => { - if (err) { - console.log(err); - } else { - executeStatement(); - } -}); - -function executeStatement() { - request = new Request("select 123, 'hello world'", (err, rowCount) => { - if (err) { - console.log(err); - } else { - console.log(`${rowCount} rows`); - } - connection.close(); - }); - - request.on('row', (columns) => { - columns.forEach((column) => { - if (column.value === null) { - console.log('NULL'); - } else { - console.log(column.value); - } - }); - }); - - connection.execSql(request); -} -``` - -## SQLite - -**Module**: [sqlite3](https://github.com/mapbox/node-sqlite3) - -### Installazione - - - -### Esempio - -```js title="index.js" -const sqlite3 = require('sqlite3').verbose(); -const db = new sqlite3.Database(':memory:'); - -db.serialize(() => { - db.run('CREATE TABLE lorem (info TEXT)'); - const stmt = db.prepare('INSERT INTO lorem VALUES (?)'); - - for (let i = 0; i < 10; i++) { - stmt.run(`Ipsum ${i}`); - } - - stmt.finalize(); - - db.each('SELECT rowid AS id, info FROM lorem', (err, row) => { - console.log(`${row.id}: ${row.info}`); - }); -}); - -db.close(); -``` - -## Elasticsearch - -**Module**: [elasticsearch](https://github.com/elastic/elasticsearch-js) - -### Installazione - - - -### Esempio - -```cjs title="index.cjs" -const elasticsearch = require('elasticsearch'); -const client = elasticsearch.Client({ - host: 'localhost:9200', -}); - -client - .search({ - index: 'books', - type: 'book', - body: { - query: { - multi_match: { - query: 'express js', - fields: ['title', 'description'], - }, - }, - }, - }) - .then( - (response) => { - const hits = response.hits.hits; - }, - (error) => { - console.trace(error.message); - } - ); -``` - -```mjs title="index.mjs" -import elasticsearch from 'elasticsearch'; - -const client = elasticsearch.Client({ - host: 'localhost:9200', -}); - -client - .search({ - index: 'books', - type: 'book', - body: { - query: { - multi_match: { - query: 'express js', - fields: ['title', 'description'], - }, - }, - }, - }) - .then( - (response) => { - const hits = response.hits.hits; - }, - (error) => { - console.trace(error.message); - } - ); -``` +If you already use an [AbortSignal](https://nodejs.org/api/globals.html#class-abortsignal) to coordinate cancellation across your app, you can pass it to `app.listen` and abort it instead of calling `server.close()` directly. diff --git a/src/content/pages/it/resources/middleware/body-parser.mdx b/src/content/pages/it/resources/middleware/body-parser.mdx index 273691de22..99087cf061 100644 --- a/src/content/pages/it/resources/middleware/body-parser.mdx +++ b/src/content/pages/it/resources/middleware/body-parser.mdx @@ -8,7 +8,7 @@ import Alert from '@components/primitives/Alert/Alert.astro'; import PackageManagerCommand from '@components/patterns/PackageManagerCommand/PackageManagerCommand.astro'; @@ -82,6 +82,12 @@ L'oggetto `bodyParser` espone varie fabbriche per creare middlewares. Tutti i mi popoleranno la proprietà `req.body` con il corpo analizzato quando l'intestazione della richiesta `Content-Type` corrisponde all'opzione `type`. +**Note** As of v2, `req.body` is left `undefined` when no body is parsed (for +example when the request has neither `Content-Length` nor `Transfer-Encoding`, +or when `Content-Type` does not match). In v1, `req.body` was always initialized +to `{}`. Code that assumes `req.body` is always an object should use optional +chaining (for example `req.body?.username`) or set a default. + I vari errori restituiti da questo modulo sono descritti nella sezione [Errori](#errors). @@ -126,8 +132,8 @@ L'opzione `reviver` viene passata direttamente a `JSON.parse` come secondo argom ##### rigoroso -Quando impostato su `true`, accetterà solo array e oggetti; quando `false` accetterà -qualcosa `JSON.parse` accetta. Il valore predefinito è `true`. +When set to `true`, it will only accept arrays and objects; when `false`, it will +accept anything `JSON.parse` accepts. Il valore predefinito è `true`. ##### tipo @@ -423,7 +429,7 @@ property is set to the encoding that is unsupported. ### The input exceeded the depth -This error occurs when using `bodyParser.urlencoded` with the `extended` property set to `true` and the input exceeds the configured `depth` option. The `status` property is set to `400`. It is recommended to review the `depth` option and evaluate if it requires a higher value. When the `depth` option is set to `32` (default value), the error will not be thrown. +This error occurs when using `bodyParser.urlencoded` with the `extended` property set to `true` and the input exceeds the configured `depth` option. The `status` property is set to `400`. It is recommended to review the `depth` option and evaluate if it requires a higher value. With the default `depth` of `32`, an input with 33 or more levels of nesting gets this error. ## Esempi diff --git a/src/content/pages/it/resources/middleware/multer.mdx b/src/content/pages/it/resources/middleware/multer.mdx index 366cd77c69..e0fb8ebee0 100644 --- a/src/content/pages/it/resources/middleware/multer.mdx +++ b/src/content/pages/it/resources/middleware/multer.mdx @@ -8,7 +8,7 @@ import Alert from '@components/primitives/Alert/Alert.astro'; import PackageManagerCommand from '@components/patterns/PackageManagerCommand/PackageManagerCommand.astro'; diff --git a/src/content/pages/ja/guide/database-integration.mdx b/src/content/pages/ja/guide/database-integration.mdx index 41bac35365..0bd04fbd50 100644 --- a/src/content/pages/ja/guide/database-integration.mdx +++ b/src/content/pages/ja/guide/database-integration.mdx @@ -1,656 +1,196 @@ --- title: データベースの統合 -description: MongoDB、MySQL、PostgreSQLなどのセットアップ例を含む、様々なデータベースを Express.js アプリケーションに統合する方法をご覧ください。 +description: Learn how to connect databases to Express.js applications, when to use an ORM, and best practices for connection pooling, security, and error handling. --- -import Alert from '@components/primitives/Alert/Alert.astro'; -import PackageManagerCommand from '@components/patterns/PackageManagerCommand/PackageManagerCommand.astro'; +To connect a database to an Express app simply load the desired Node.js driver or ORM in your app. The general pattern is shown below using SQLite as an example. -Expressアプリケーションにデータベースを接続する機能を追加することは、アプリケーション内のデータベースに適切なNode.jsドライバをロードするだけです。このドキュメントでは、Express アプリケーションで最も人気のある Node.js モジュールのいくつかを追加して使用する方法を簡単に説明します。 +There are also a few [best practices](#best-practices) to follow, regardless of the database you're using. -- [Cassandra](#cassandra) -- [Couchbase](#couchbase) -- [CouchDB](#couchdb) -- [LevelDB](#leveldb) -- [MySQL](#mysql) -- [MongoDB](#mongodb) -- [Neo4j](#neo4j) -- [Oracle](#oracle) -- [PostgreSQL](#postgresql) -- [Redis](#redis) -- [SQL Server](#sql-server) -- [SQLite](#sqlite) -- [Elasticsearch](#elasticsearch) - - - -これらのデータベースドライバは、利用可能な多くの中にあります。その他のオプションについては、 -[npm](https://www.npmjs.com/) サイトを検索してください。 - - - -## Cassandra - -**モジュール**: [cassandra-driver](https://github.com/datastax/nodejs-driver) - -### インストール +## SQLite - +Node.js includes a built-in SQLite client, [node:sqlite](https://nodejs.org/api/sqlite.html), available without a flag since Node.js 22.13.0. No installation is required. For older Node.js versions, [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) offers a similar synchronous API. ### 例 -```cjs title="index.cjs" -const cassandra = require('cassandra-driver'); -const client = new cassandra.Client({ contactPoints: ['localhost'] }); - -client.execute('select key from system.local', (err, result) => { - if (err) throw err; - console.log(result.rows[0]); -}); -``` +This example creates a sample user that you can retrieve at `http://localhost:3000/users/1`. It uses a synchronous SQLite API for simplicity: long-running queries block the thread handling requests. For workloads with long-running queries, consider moving database work to a worker thread or using an asynchronous database driver. -```mjs title="index.mjs" -import cassandra from 'cassandra-driver'; - -const client = new cassandra.Client({ contactPoints: ['localhost'] }); - -client.execute('select key from system.local', (err, result) => { - if (err) throw err; - console.log(result.rows[0]); -}); -``` - -## Couchbase - -**Module**: [couchnode](https://github.com/couchbase/couchnode) - -### インストール +```cjs title="index.cjs" +const express = require('express'); +const { DatabaseSync } = require('node:sqlite'); - +const app = express(); +const db = new DatabaseSync('app.db'); -### 例 +db.exec('CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT NOT NULL)'); +db.prepare('INSERT INTO users (id, name) VALUES (?, ?) ON CONFLICT(id) DO NOTHING').run(1, 'Alice'); +const findUser = db.prepare('SELECT id, name FROM users WHERE id = ?'); -```cjs title="index.cjs" -const couchbase = require('couchbase'); -const bucket = new couchbase.Cluster('http://localhost:8091').openBucket('bucketName'); - -// add a document to a bucket -bucket.insert('document-key', { name: 'Matt', shoeSize: 13 }, (err, result) => { - if (err) { - console.log(err); - } else { - console.log(result); +app.get('/users/:id', (req, res) => { + const user = findUser.get(req.params.id); + if (!user) { + return res.status(404).json({ error: 'User not found' }); } + res.json(user); }); -// get all documents with shoe size 13 -const n1ql = 'SELECT d.* FROM `bucketName` d WHERE shoeSize = $1'; -const query = N1qlQuery.fromString(n1ql); -bucket.query(query, [13], (err, result) => { - if (err) { - console.log(err); - } else { - console.log(result); - } -}); +app.listen(3000); ``` ```mjs title="index.mjs" -import couchbase from 'couchbase'; +import express from 'express'; +import { DatabaseSync } from 'node:sqlite'; -const bucket = new couchbase.Cluster('http://localhost:8091').openBucket('bucketName'); +const app = express(); +const db = new DatabaseSync('app.db'); -// add a document to a bucket -bucket.insert('document-key', { name: 'Matt', shoeSize: 13 }, (err, result) => { - if (err) { - console.log(err); - } else { - console.log(result); - } -}); +db.exec('CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT NOT NULL)'); +db.prepare('INSERT INTO users (id, name) VALUES (?, ?) ON CONFLICT(id) DO NOTHING').run(1, 'Alice'); +const findUser = db.prepare('SELECT id, name FROM users WHERE id = ?'); -// get all documents with shoe size 13 -const n1ql = 'SELECT d.* FROM `bucketName` d WHERE shoeSize = $1'; -const query = N1qlQuery.fromString(n1ql); -bucket.query(query, [13], (err, result) => { - if (err) { - console.log(err); - } else { - console.log(result); +app.get('/users/:id', (req, res) => { + const user = findUser.get(req.params.id); + if (!user) { + return res.status(404).json({ error: 'User not found' }); } + res.json(user); }); + +app.listen(3000); ``` -## CouchDB +```ts title="index.ts" +import express from 'express'; +import { DatabaseSync } from 'node:sqlite'; -**Module**: [nano](https://github.com/dscape/nano) +interface User { + id: number; + name: string; +} -### インストール +const app = express(); +const db = new DatabaseSync('app.db'); - +db.exec('CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT NOT NULL)'); +db.prepare('INSERT INTO users (id, name) VALUES (?, ?) ON CONFLICT(id) DO NOTHING').run(1, 'Alice'); +const findUser = db.prepare('SELECT id, name FROM users WHERE id = ?'); -### 例 - -```js title="index.js" -const nano = require('nano')('http://localhost:5984'); -nano.db.create('books'); -const books = nano.db.use('books'); - -// Insert a book document in the books database -books.insert({ name: 'The Art of war' }, null, (err, body) => { - if (err) { - console.log(err); - } else { - console.log(body); +app.get('/users/:id', (req, res) => { + const user = findUser.get(req.params.id) as User | undefined; + if (!user) { + return res.status(404).json({ error: 'User not found' }); } + res.json(user); }); -// Get a list of all books -books.list((err, body) => { - if (err) { - console.log(err); - } else { - console.log(body.rows); - } -}); +app.listen(3000); ``` -## LevelDB +## Other databases -**Module**: [levelup](https://github.com/rvagg/node-levelup) +Every other database follows the same pattern: install the Node.js driver from npm, create a client or connection pool once when the app starts, and use it in your route handlers. Refer to the official documentation of each driver for connection options and API details: -### インストール +- PostgreSQL: [pg](https://node-postgres.com/) or [postgres](https://github.com/porsager/postgres) +- MySQL and MariaDB: [mysql2](https://sidorares.github.io/node-mysql2/docs) or [mariadb](https://mariadb.com/docs/connectors/mariadb-connector-nodejs) +- MongoDB: [mongodb](https://www.mongodb.com/docs/drivers/node/current/) +- Redis: [redis](https://redis.io/docs/latest/develop/clients/nodejs/) or [ioredis](https://github.com/redis/ioredis) +- SQL Server: [mssql](https://github.com/tediousjs/node-mssql) +- Oracle: [oracledb](https://www.npmjs.com/package/oracledb) +- Cassandra: [cassandra-driver](https://docs.datastax.com/en/developer/nodejs-driver/latest/) +- Elasticsearch: [@elastic/elasticsearch](https://www.elastic.co/docs/reference/elasticsearch/clients/javascript) - - -### 例 +For any database not listed here, search for its driver on the [npm](https://www.npmjs.com/) site. -```cjs title="index.cjs" -const levelup = require('levelup'); -const db = levelup('./mydb'); +## ORMs and query builders -db.put('name', 'LevelUP', (err) => { - if (err) return console.log('Ooops!', err); +The example above uses a database driver directly, which is a good fit for small apps and for learning how things work. As an application grows, an ORM (object-relational mapper) or a query builder can take over concerns such as schema definitions, migrations, relations between tables, and input mapping, while reducing boilerplate. Many of them also provide type safety when used with TypeScript. - db.get('name', (err, value) => { - if (err) return console.log('Ooops!', err); +Popular options in the Node.js ecosystem include: - console.log(`name=${value}`); - }); -}); -``` +- [Prisma](https://www.prisma.io/): a type-safe ORM with a schema-first workflow and built-in migrations. Supports PostgreSQL, MySQL, SQLite, SQL Server, MongoDB, and CockroachDB. +- [Drizzle ORM](https://orm.drizzle.team/): a lightweight, TypeScript-first ORM with a SQL-like query API and a migrations toolkit. +- [Sequelize](https://sequelize.org/): a mature, promise-based ORM for PostgreSQL, MySQL, MariaDB, SQLite, and SQL Server. +- [TypeORM](https://typeorm.io/): an ORM built around decorators and entities, supporting many SQL databases and MongoDB. +- [Mongoose](https://mongoosejs.com/): an ODM (object document mapper) for MongoDB. +- [Knex.js](https://knexjs.org/): a flexible SQL query builder with migrations support, useful when you want control over your queries without writing raw strings. +- [Kysely](https://kysely.dev/): a type-safe SQL query builder for TypeScript. -```mjs title="index.mjs" -import levelup from 'levelup'; +Each ORM has its own API, supported databases, and approach to schemas, migrations, and queries. Features and setup requirements vary, so consult each project's documentation before choosing one. Query builders keep you close to SQL, while full ORMs abstract more of it away. Pick the level of abstraction that matches your team and project, and prefer tools that are actively maintained. -const db = levelup('./mydb'); +## Best practices -db.put('name', 'LevelUP', (err) => { - if (err) return console.log('Ooops!', err); +Adapt these practices to the database and library you choose. The snippets below illustrate separate parts of an application. The PostgreSQL snippets assume you have installed the [pg](https://node-postgres.com/) driver and created a shared pool: - db.get('name', (err, value) => { - if (err) return console.log('Ooops!', err); +```js +import { Pool } from 'pg'; - console.log(`name=${value}`); - }); -}); +const pool = new Pool({ connectionString: process.env.DATABASE_URL }); ``` -## MySQL - -**Module**: [mysql](https://github.com/felixge/node-mysql/) - -### インストール - - - -### 例 - -```cjs title="index.cjs" -const mysql = require('mysql'); -const connection = mysql.createConnection({ - host: 'localhost', - user: 'dbuser', - password: 's3kreee7', - database: 'my_db', -}); +### Create connections once and reuse them -connection.connect(); +Opening a database connection is expensive. Many drivers provide a connection pool that creates connections as needed and reuses them across requests. Create the pool (or client) once when the app starts. Acquire connections from that shared pool as needed in your route handlers, for example for a transaction, and always release them in a `finally` block. For individual queries, `pool.query()` in `pg` handles acquiring and releasing a connection for you. -connection.query('SELECT 1 + 1 AS solution', (err, rows, fields) => { - if (err) throw err; +### Use parameterized queries - console.log('The solution is: ', rows[0].solution); -}); +Never build SQL queries by concatenating or interpolating user input into the query string. Doing so exposes your app to SQL injection. Use your driver's parameterized queries or prepared statements to pass user input separately from the query itself. -connection.end(); +```js +// Vulnerable: user input becomes part of the query +const user = await pool.query(`SELECT * FROM users WHERE id = ${req.params.id}`); ``` -```mjs title="index.mjs" -import mysql from 'mysql'; - -const connection = mysql.createConnection({ - host: 'localhost', - user: 'dbuser', - password: 's3kreee7', - database: 'my_db', -}); - -connection.connect(); - -connection.query('SELECT 1 + 1 AS solution', (err, rows, fields) => { - if (err) throw err; - - console.log('The solution is: ', rows[0].solution); -}); - -connection.end(); +```js +// Safe: user input is passed as a parameter +const user = await pool.query('SELECT * FROM users WHERE id = $1', [req.params.id]); ``` -## MongoDB - -**Module**: [mongodb](https://github.com/mongodb/node-mongodb-native) - -### インストール - - - -### Example (v2.\*) - -```js title="index.js" -const MongoClient = require('mongodb').MongoClient; +Also validate user input before it reaches your queries, both for security and for data integrity. For NoSQL databases, construct queries using the driver's API and validate the types, fields, and operators you accept. Do not pass user-supplied query objects directly to the database. -MongoClient.connect('mongodb://localhost:27017/animals', (err, db) => { - if (err) throw err; +### Keep credentials out of your code - db.collection('mammals') - .find() - .toArray((err, result) => { - if (err) throw err; +Never hardcode connection strings, usernames, or passwords in your source code, and never commit them to version control. Read them from environment variables or a secrets manager instead: - console.log(result); - }); -}); +```js +const pool = new Pool({ connectionString: process.env.DATABASE_URL }); ``` -### Example (v3.\*) - -```js title="index.js" -const MongoClient = require('mongodb').MongoClient; - -MongoClient.connect('mongodb://localhost:27017/animals', (err, client) => { - if (err) throw err; - - const db = client.db('animals'); - - db.collection('mammals') - .find() - .toArray((err, result) => { - if (err) throw err; +During development, you can keep these values in a `.env` file and load it with Node.js's built-in support for environment files, with no extra dependencies: - console.log(result); - }); -}); +```bash +node --env-file=.env index.js ``` -If you want an object model driver for MongoDB, look at [Mongoose](https://github.com/LearnBoost/mongoose). - -## Neo4j - -**Module**: [neo4j-driver](https://github.com/neo4j/neo4j-javascript-driver) +Make sure `.env` is listed in your `.gitignore` file so it never gets committed. -### インストール - - - -### 例 +### Handle database errors -```cjs title="index.cjs" -const neo4j = require('neo4j-driver'); -const driver = neo4j.driver('neo4j://localhost:7687', neo4j.auth.basic('neo4j', 'letmein')); - -const session = driver.session(); - -session.readTransaction((tx) => { - return tx - .run('MATCH (n) RETURN count(n) AS count') - .then((res) => { - console.log(res.records[0].get('count')); - }) - .catch((error) => { - console.log(error); - }); -}); -``` - -```mjs title="index.mjs" -import neo4j from 'neo4j-driver'; +Database calls fail: connections drop, queries time out, constraints are violated. In Express 5, errors thrown in async route handlers are passed to your [error-handling middleware](/en/guide/error-handling) automatically, so define one and decide there what to expose to clients. Avoid leaking internal error details, such as query text or stack traces, in API responses. -const driver = neo4j.driver('neo4j://localhost:7687', neo4j.auth.basic('neo4j', 'letmein')); +### Cancel work when the client disconnects -const session = driver.session(); +Since Node.js 24.20.0, every request exposes [req.signal](https://nodejs.org/api/http.html#messagesignal), an `AbortSignal` that is aborted when the client disconnects. Pass it to any cancellation-aware API, such as a database driver that supports abort signals, so your app stops doing work nobody is waiting for: -session.readTransaction((tx) => { - return tx - .run('MATCH (n) RETURN count(n) AS count') - .then((res) => { - console.log(res.records[0].get('count')); - }) - .catch((error) => { - console.log(error); - }); +```js +app.get('/reports', async (req, res) => { + const report = await generateReport({ signal: req.signal }); + res.json(report); }); ``` -## Oracle +### Close connections on shutdown -**Module**: [oracledb](https://github.com/oracle/node-oracledb) +When your app receives a termination signal, stop accepting new requests and close your database connections so in-flight queries can finish cleanly: -### インストール +```js +const server = app.listen(3000); -NOTE: [See installation prerequisites](https://github.com/oracle/node-oracledb#-installation). - - - -### 例 - -```cjs title="index.cjs" -const oracledb = require('oracledb'); -const config = { - user: '', - password: '', - connectString: 'localhost:1521/orcl', -}; - -async function getEmployee(empId) { - let conn; - - try { - conn = await oracledb.getConnection(config); - - const result = await conn.execute('select * from employees where employee_id = :id', [empId]); - - console.log(result.rows[0]); - } catch (err) { - console.log('Ouch!', err); - } finally { - if (conn) { - // conn assignment worked, need to close - await conn.close(); - } - } -} - -getEmployee(101); -``` - -```mjs title="index.mjs" -import oracledb from 'oracledb'; - -const config = { - user: '', - password: '', - connectString: 'localhost:1521/orcl', -}; - -async function getEmployee(empId) { - let conn; - - try { - conn = await oracledb.getConnection(config); - - const result = await conn.execute('select * from employees where employee_id = :id', [empId]); - - console.log(result.rows[0]); - } catch (err) { - console.log('Ouch!', err); - } finally { - if (conn) { - // conn assignment worked, need to close - await conn.close(); - } - } -} - -getEmployee(101); -``` - -## PostgreSQL - -**Module**: [pg-promise](https://github.com/vitaly-t/pg-promise) - -### インストール - - - -### 例 - -```js title="index.js" -const pgp = require('pg-promise')(/* options */); -const db = pgp('postgres://username:password@host:port/database'); - -db.one('SELECT $1 AS value', 123) - .then((data) => { - console.log('DATA:', data.value); - }) - .catch((error) => { - console.log('ERROR:', error); +process.on('SIGTERM', () => { + server.close(async () => { + await pool.end(); + process.exit(0); }); -``` - -## Redis - -**Module**: [redis](https://github.com/mranney/node_redis) - -### インストール - - - -### 例 - -```cjs title="index.cjs" -const redis = require('redis'); -const client = redis.createClient(); - -client.on('error', (err) => { - console.log(`Error ${err}`); -}); - -client.set('string key', 'string val', redis.print); -client.hset('hash key', 'hashtest 1', 'some value', redis.print); -client.hset(['hash key', 'hashtest 2', 'some other value'], redis.print); - -client.hkeys('hash key', (err, replies) => { - console.log(`${replies.length} replies:`); - - replies.forEach((reply, i) => { - console.log(` ${i}: ${reply}`); - }); - - client.quit(); -}); -``` - -```mjs title="index.mjs" -import redis from 'redis'; - -const client = redis.createClient(); - -client.on('error', (err) => { - console.log(`Error ${err}`); -}); - -client.set('string key', 'string val', redis.print); -client.hset('hash key', 'hashtest 1', 'some value', redis.print); -client.hset(['hash key', 'hashtest 2', 'some other value'], redis.print); - -client.hkeys('hash key', (err, replies) => { - console.log(`${replies.length} replies:`); - - replies.forEach((reply, i) => { - console.log(` ${i}: ${reply}`); - }); - - client.quit(); }); ``` -## SQL Server - -**Module**: [tedious](https://github.com/tediousjs/tedious) - -### インストール - - - -### 例 - -```js title="index.js" -const Connection = require('tedious').Connection; -const Request = require('tedious').Request; - -const config = { - server: 'localhost', - authentication: { - type: 'default', - options: { - userName: 'your_username', // update me - password: 'your_password', // update me - }, - }, -}; - -const connection = new Connection(config); - -connection.on('connect', (err) => { - if (err) { - console.log(err); - } else { - executeStatement(); - } -}); - -function executeStatement() { - request = new Request("select 123, 'hello world'", (err, rowCount) => { - if (err) { - console.log(err); - } else { - console.log(`${rowCount} rows`); - } - connection.close(); - }); - - request.on('row', (columns) => { - columns.forEach((column) => { - if (column.value === null) { - console.log('NULL'); - } else { - console.log(column.value); - } - }); - }); - - connection.execSql(request); -} -``` - -## SQLite - -**Module**: [sqlite3](https://github.com/mapbox/node-sqlite3) - -### インストール - - - -### 例 - -```js title="index.js" -const sqlite3 = require('sqlite3').verbose(); -const db = new sqlite3.Database(':memory:'); - -db.serialize(() => { - db.run('CREATE TABLE lorem (info TEXT)'); - const stmt = db.prepare('INSERT INTO lorem VALUES (?)'); - - for (let i = 0; i < 10; i++) { - stmt.run(`Ipsum ${i}`); - } - - stmt.finalize(); - - db.each('SELECT rowid AS id, info FROM lorem', (err, row) => { - console.log(`${row.id}: ${row.info}`); - }); -}); - -db.close(); -``` - -## Elasticsearch - -**Module**: [elasticsearch](https://github.com/elastic/elasticsearch-js) - -### インストール - - - -### 例 - -```cjs title="index.cjs" -const elasticsearch = require('elasticsearch'); -const client = elasticsearch.Client({ - host: 'localhost:9200', -}); - -client - .search({ - index: 'books', - type: 'book', - body: { - query: { - multi_match: { - query: 'express js', - fields: ['title', 'description'], - }, - }, - }, - }) - .then( - (response) => { - const hits = response.hits.hits; - }, - (error) => { - console.trace(error.message); - } - ); -``` - -```mjs title="index.mjs" -import elasticsearch from 'elasticsearch'; - -const client = elasticsearch.Client({ - host: 'localhost:9200', -}); - -client - .search({ - index: 'books', - type: 'book', - body: { - query: { - multi_match: { - query: 'express js', - fields: ['title', 'description'], - }, - }, - }, - }) - .then( - (response) => { - const hits = response.hits.hits; - }, - (error) => { - console.trace(error.message); - } - ); -``` +If you already use an [AbortSignal](https://nodejs.org/api/globals.html#class-abortsignal) to coordinate cancellation across your app, you can pass it to `app.listen` and abort it instead of calling `server.close()` directly. diff --git a/src/content/pages/ja/resources/middleware/body-parser.mdx b/src/content/pages/ja/resources/middleware/body-parser.mdx index cc54496fd7..acb935fec2 100644 --- a/src/content/pages/ja/resources/middleware/body-parser.mdx +++ b/src/content/pages/ja/resources/middleware/body-parser.mdx @@ -8,7 +8,7 @@ import Alert from '@components/primitives/Alert/Alert.astro'; import PackageManagerCommand from '@components/patterns/PackageManagerCommand/PackageManagerCommand.astro'; @@ -82,6 +82,12 @@ const text = require('body-parser/text'); middlewares will populate the `req.body` property with the parsed body when the `Content-Type` request header matches the `type` option. +**Note** As of v2, `req.body` is left `undefined` when no body is parsed (for +example when the request has neither `Content-Length` nor `Transfer-Encoding`, +or when `Content-Type` does not match). In v1, `req.body` was always initialized +to `{}`. Code that assumes `req.body` is always an object should use optional +chaining (for example `req.body?.username`) or set a default. + このモジュールによって返される様々なエラーは、 [errors section](#errors)で説明されています。 @@ -125,7 +131,7 @@ the following keys: ##### 厳格な -When set to `true`, will only accept arrays and objects; when `false` will +When set to `true`, it will only accept arrays and objects; when `false`, it will accept anything `JSON.parse` accepts. デフォルトは `true` です。 ##### タイプ @@ -419,7 +425,7 @@ property is set to the encoding that is unsupported. ### The input exceeded the depth -This error occurs when using `bodyParser.urlencoded` with the `extended` property set to `true` and the input exceeds the configured `depth` option. The `status` property is set to `400`. It is recommended to review the `depth` option and evaluate if it requires a higher value. When the `depth` option is set to `32` (default value), the error will not be thrown. +This error occurs when using `bodyParser.urlencoded` with the `extended` property set to `true` and the input exceeds the configured `depth` option. The `status` property is set to `400`. It is recommended to review the `depth` option and evaluate if it requires a higher value. With the default `depth` of `32`, an input with 33 or more levels of nesting gets this error. ## 例 diff --git a/src/content/pages/ja/resources/middleware/multer.mdx b/src/content/pages/ja/resources/middleware/multer.mdx index b7e9f96a26..c5c52daba6 100644 --- a/src/content/pages/ja/resources/middleware/multer.mdx +++ b/src/content/pages/ja/resources/middleware/multer.mdx @@ -8,7 +8,7 @@ import Alert from '@components/primitives/Alert/Alert.astro'; import PackageManagerCommand from '@components/patterns/PackageManagerCommand/PackageManagerCommand.astro'; diff --git a/src/content/pages/ko/guide/database-integration.mdx b/src/content/pages/ko/guide/database-integration.mdx index 5c3aeab058..b80ed4d966 100644 --- a/src/content/pages/ko/guide/database-integration.mdx +++ b/src/content/pages/ko/guide/database-integration.mdx @@ -1,656 +1,196 @@ --- title: Database integration -description: Discover how to integrate various databases with Express.js applications, including setup examples for MongoDB, MySQL, PostgreSQL, and more. +description: Learn how to connect databases to Express.js applications, when to use an ORM, and best practices for connection pooling, security, and error handling. --- -import Alert from '@components/primitives/Alert/Alert.astro'; -import PackageManagerCommand from '@components/patterns/PackageManagerCommand/PackageManagerCommand.astro'; +To connect a database to an Express app simply load the desired Node.js driver or ORM in your app. The general pattern is shown below using SQLite as an example. -Adding the capability to connect databases to Express apps is just a matter of loading an appropriate Node.js driver for the database in your app. This document briefly explains how to add and use some of the most popular Node.js modules for database systems in your Express app: +There are also a few [best practices](#best-practices) to follow, regardless of the database you're using. -- [Cassandra](#cassandra) -- [Couchbase](#couchbase) -- [CouchDB](#couchdb) -- [LevelDB](#leveldb) -- [MySQL](#mysql) -- [MongoDB](#mongodb) -- [Neo4j](#neo4j) -- [Oracle](#oracle) -- [PostgreSQL](#postgresql) -- [Redis](#redis) -- [SQL Server](#sql-server) -- [SQLite](#sqlite) -- [Elasticsearch](#elasticsearch) - - - -These database drivers are among many that are available. For other options, search on the -[npm](https://www.npmjs.com/) site. - - - -## Cassandra - -**Module**: [cassandra-driver](https://github.com/datastax/nodejs-driver) - -### Installation +## SQLite - +Node.js includes a built-in SQLite client, [node:sqlite](https://nodejs.org/api/sqlite.html), available without a flag since Node.js 22.13.0. No installation is required. For older Node.js versions, [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) offers a similar synchronous API. ### Example -```cjs title="index.cjs" -const cassandra = require('cassandra-driver'); -const client = new cassandra.Client({ contactPoints: ['localhost'] }); - -client.execute('select key from system.local', (err, result) => { - if (err) throw err; - console.log(result.rows[0]); -}); -``` +This example creates a sample user that you can retrieve at `http://localhost:3000/users/1`. It uses a synchronous SQLite API for simplicity: long-running queries block the thread handling requests. For workloads with long-running queries, consider moving database work to a worker thread or using an asynchronous database driver. -```mjs title="index.mjs" -import cassandra from 'cassandra-driver'; - -const client = new cassandra.Client({ contactPoints: ['localhost'] }); - -client.execute('select key from system.local', (err, result) => { - if (err) throw err; - console.log(result.rows[0]); -}); -``` - -## Couchbase - -**Module**: [couchnode](https://github.com/couchbase/couchnode) - -### Installation +```cjs title="index.cjs" +const express = require('express'); +const { DatabaseSync } = require('node:sqlite'); - +const app = express(); +const db = new DatabaseSync('app.db'); -### Example +db.exec('CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT NOT NULL)'); +db.prepare('INSERT INTO users (id, name) VALUES (?, ?) ON CONFLICT(id) DO NOTHING').run(1, 'Alice'); +const findUser = db.prepare('SELECT id, name FROM users WHERE id = ?'); -```cjs title="index.cjs" -const couchbase = require('couchbase'); -const bucket = new couchbase.Cluster('http://localhost:8091').openBucket('bucketName'); - -// add a document to a bucket -bucket.insert('document-key', { name: 'Matt', shoeSize: 13 }, (err, result) => { - if (err) { - console.log(err); - } else { - console.log(result); +app.get('/users/:id', (req, res) => { + const user = findUser.get(req.params.id); + if (!user) { + return res.status(404).json({ error: 'User not found' }); } + res.json(user); }); -// get all documents with shoe size 13 -const n1ql = 'SELECT d.* FROM `bucketName` d WHERE shoeSize = $1'; -const query = N1qlQuery.fromString(n1ql); -bucket.query(query, [13], (err, result) => { - if (err) { - console.log(err); - } else { - console.log(result); - } -}); +app.listen(3000); ``` ```mjs title="index.mjs" -import couchbase from 'couchbase'; +import express from 'express'; +import { DatabaseSync } from 'node:sqlite'; -const bucket = new couchbase.Cluster('http://localhost:8091').openBucket('bucketName'); +const app = express(); +const db = new DatabaseSync('app.db'); -// add a document to a bucket -bucket.insert('document-key', { name: 'Matt', shoeSize: 13 }, (err, result) => { - if (err) { - console.log(err); - } else { - console.log(result); - } -}); +db.exec('CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT NOT NULL)'); +db.prepare('INSERT INTO users (id, name) VALUES (?, ?) ON CONFLICT(id) DO NOTHING').run(1, 'Alice'); +const findUser = db.prepare('SELECT id, name FROM users WHERE id = ?'); -// get all documents with shoe size 13 -const n1ql = 'SELECT d.* FROM `bucketName` d WHERE shoeSize = $1'; -const query = N1qlQuery.fromString(n1ql); -bucket.query(query, [13], (err, result) => { - if (err) { - console.log(err); - } else { - console.log(result); +app.get('/users/:id', (req, res) => { + const user = findUser.get(req.params.id); + if (!user) { + return res.status(404).json({ error: 'User not found' }); } + res.json(user); }); + +app.listen(3000); ``` -## CouchDB +```ts title="index.ts" +import express from 'express'; +import { DatabaseSync } from 'node:sqlite'; -**Module**: [nano](https://github.com/dscape/nano) +interface User { + id: number; + name: string; +} -### Installation +const app = express(); +const db = new DatabaseSync('app.db'); - +db.exec('CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT NOT NULL)'); +db.prepare('INSERT INTO users (id, name) VALUES (?, ?) ON CONFLICT(id) DO NOTHING').run(1, 'Alice'); +const findUser = db.prepare('SELECT id, name FROM users WHERE id = ?'); -### Example - -```js title="index.js" -const nano = require('nano')('http://localhost:5984'); -nano.db.create('books'); -const books = nano.db.use('books'); - -// Insert a book document in the books database -books.insert({ name: 'The Art of war' }, null, (err, body) => { - if (err) { - console.log(err); - } else { - console.log(body); +app.get('/users/:id', (req, res) => { + const user = findUser.get(req.params.id) as User | undefined; + if (!user) { + return res.status(404).json({ error: 'User not found' }); } + res.json(user); }); -// Get a list of all books -books.list((err, body) => { - if (err) { - console.log(err); - } else { - console.log(body.rows); - } -}); +app.listen(3000); ``` -## LevelDB +## Other databases -**Module**: [levelup](https://github.com/rvagg/node-levelup) +Every other database follows the same pattern: install the Node.js driver from npm, create a client or connection pool once when the app starts, and use it in your route handlers. Refer to the official documentation of each driver for connection options and API details: -### Installation +- PostgreSQL: [pg](https://node-postgres.com/) or [postgres](https://github.com/porsager/postgres) +- MySQL and MariaDB: [mysql2](https://sidorares.github.io/node-mysql2/docs) or [mariadb](https://mariadb.com/docs/connectors/mariadb-connector-nodejs) +- MongoDB: [mongodb](https://www.mongodb.com/docs/drivers/node/current/) +- Redis: [redis](https://redis.io/docs/latest/develop/clients/nodejs/) or [ioredis](https://github.com/redis/ioredis) +- SQL Server: [mssql](https://github.com/tediousjs/node-mssql) +- Oracle: [oracledb](https://www.npmjs.com/package/oracledb) +- Cassandra: [cassandra-driver](https://docs.datastax.com/en/developer/nodejs-driver/latest/) +- Elasticsearch: [@elastic/elasticsearch](https://www.elastic.co/docs/reference/elasticsearch/clients/javascript) - - -### Example +For any database not listed here, search for its driver on the [npm](https://www.npmjs.com/) site. -```cjs title="index.cjs" -const levelup = require('levelup'); -const db = levelup('./mydb'); +## ORMs and query builders -db.put('name', 'LevelUP', (err) => { - if (err) return console.log('Ooops!', err); +The example above uses a database driver directly, which is a good fit for small apps and for learning how things work. As an application grows, an ORM (object-relational mapper) or a query builder can take over concerns such as schema definitions, migrations, relations between tables, and input mapping, while reducing boilerplate. Many of them also provide type safety when used with TypeScript. - db.get('name', (err, value) => { - if (err) return console.log('Ooops!', err); +Popular options in the Node.js ecosystem include: - console.log(`name=${value}`); - }); -}); -``` +- [Prisma](https://www.prisma.io/): a type-safe ORM with a schema-first workflow and built-in migrations. Supports PostgreSQL, MySQL, SQLite, SQL Server, MongoDB, and CockroachDB. +- [Drizzle ORM](https://orm.drizzle.team/): a lightweight, TypeScript-first ORM with a SQL-like query API and a migrations toolkit. +- [Sequelize](https://sequelize.org/): a mature, promise-based ORM for PostgreSQL, MySQL, MariaDB, SQLite, and SQL Server. +- [TypeORM](https://typeorm.io/): an ORM built around decorators and entities, supporting many SQL databases and MongoDB. +- [Mongoose](https://mongoosejs.com/): an ODM (object document mapper) for MongoDB. +- [Knex.js](https://knexjs.org/): a flexible SQL query builder with migrations support, useful when you want control over your queries without writing raw strings. +- [Kysely](https://kysely.dev/): a type-safe SQL query builder for TypeScript. -```mjs title="index.mjs" -import levelup from 'levelup'; +Each ORM has its own API, supported databases, and approach to schemas, migrations, and queries. Features and setup requirements vary, so consult each project's documentation before choosing one. Query builders keep you close to SQL, while full ORMs abstract more of it away. Pick the level of abstraction that matches your team and project, and prefer tools that are actively maintained. -const db = levelup('./mydb'); +## Best practices -db.put('name', 'LevelUP', (err) => { - if (err) return console.log('Ooops!', err); +Adapt these practices to the database and library you choose. The snippets below illustrate separate parts of an application. The PostgreSQL snippets assume you have installed the [pg](https://node-postgres.com/) driver and created a shared pool: - db.get('name', (err, value) => { - if (err) return console.log('Ooops!', err); +```js +import { Pool } from 'pg'; - console.log(`name=${value}`); - }); -}); +const pool = new Pool({ connectionString: process.env.DATABASE_URL }); ``` -## MySQL - -**Module**: [mysql](https://github.com/felixge/node-mysql/) - -### Installation - - - -### Example - -```cjs title="index.cjs" -const mysql = require('mysql'); -const connection = mysql.createConnection({ - host: 'localhost', - user: 'dbuser', - password: 's3kreee7', - database: 'my_db', -}); +### Create connections once and reuse them -connection.connect(); +Opening a database connection is expensive. Many drivers provide a connection pool that creates connections as needed and reuses them across requests. Create the pool (or client) once when the app starts. Acquire connections from that shared pool as needed in your route handlers, for example for a transaction, and always release them in a `finally` block. For individual queries, `pool.query()` in `pg` handles acquiring and releasing a connection for you. -connection.query('SELECT 1 + 1 AS solution', (err, rows, fields) => { - if (err) throw err; +### Use parameterized queries - console.log('The solution is: ', rows[0].solution); -}); +Never build SQL queries by concatenating or interpolating user input into the query string. Doing so exposes your app to SQL injection. Use your driver's parameterized queries or prepared statements to pass user input separately from the query itself. -connection.end(); +```js +// Vulnerable: user input becomes part of the query +const user = await pool.query(`SELECT * FROM users WHERE id = ${req.params.id}`); ``` -```mjs title="index.mjs" -import mysql from 'mysql'; - -const connection = mysql.createConnection({ - host: 'localhost', - user: 'dbuser', - password: 's3kreee7', - database: 'my_db', -}); - -connection.connect(); - -connection.query('SELECT 1 + 1 AS solution', (err, rows, fields) => { - if (err) throw err; - - console.log('The solution is: ', rows[0].solution); -}); - -connection.end(); +```js +// Safe: user input is passed as a parameter +const user = await pool.query('SELECT * FROM users WHERE id = $1', [req.params.id]); ``` -## MongoDB - -**Module**: [mongodb](https://github.com/mongodb/node-mongodb-native) - -### Installation - - - -### Example (v2.\*) - -```js title="index.js" -const MongoClient = require('mongodb').MongoClient; +Also validate user input before it reaches your queries, both for security and for data integrity. For NoSQL databases, construct queries using the driver's API and validate the types, fields, and operators you accept. Do not pass user-supplied query objects directly to the database. -MongoClient.connect('mongodb://localhost:27017/animals', (err, db) => { - if (err) throw err; +### Keep credentials out of your code - db.collection('mammals') - .find() - .toArray((err, result) => { - if (err) throw err; +Never hardcode connection strings, usernames, or passwords in your source code, and never commit them to version control. Read them from environment variables or a secrets manager instead: - console.log(result); - }); -}); +```js +const pool = new Pool({ connectionString: process.env.DATABASE_URL }); ``` -### Example (v3.\*) - -```js title="index.js" -const MongoClient = require('mongodb').MongoClient; - -MongoClient.connect('mongodb://localhost:27017/animals', (err, client) => { - if (err) throw err; - - const db = client.db('animals'); - - db.collection('mammals') - .find() - .toArray((err, result) => { - if (err) throw err; +During development, you can keep these values in a `.env` file and load it with Node.js's built-in support for environment files, with no extra dependencies: - console.log(result); - }); -}); +```bash +node --env-file=.env index.js ``` -If you want an object model driver for MongoDB, look at [Mongoose](https://github.com/LearnBoost/mongoose). - -## Neo4j - -**Module**: [neo4j-driver](https://github.com/neo4j/neo4j-javascript-driver) +Make sure `.env` is listed in your `.gitignore` file so it never gets committed. -### Installation - - - -### Example +### Handle database errors -```cjs title="index.cjs" -const neo4j = require('neo4j-driver'); -const driver = neo4j.driver('neo4j://localhost:7687', neo4j.auth.basic('neo4j', 'letmein')); - -const session = driver.session(); - -session.readTransaction((tx) => { - return tx - .run('MATCH (n) RETURN count(n) AS count') - .then((res) => { - console.log(res.records[0].get('count')); - }) - .catch((error) => { - console.log(error); - }); -}); -``` - -```mjs title="index.mjs" -import neo4j from 'neo4j-driver'; +Database calls fail: connections drop, queries time out, constraints are violated. In Express 5, errors thrown in async route handlers are passed to your [error-handling middleware](/en/guide/error-handling) automatically, so define one and decide there what to expose to clients. Avoid leaking internal error details, such as query text or stack traces, in API responses. -const driver = neo4j.driver('neo4j://localhost:7687', neo4j.auth.basic('neo4j', 'letmein')); +### Cancel work when the client disconnects -const session = driver.session(); +Since Node.js 24.20.0, every request exposes [req.signal](https://nodejs.org/api/http.html#messagesignal), an `AbortSignal` that is aborted when the client disconnects. Pass it to any cancellation-aware API, such as a database driver that supports abort signals, so your app stops doing work nobody is waiting for: -session.readTransaction((tx) => { - return tx - .run('MATCH (n) RETURN count(n) AS count') - .then((res) => { - console.log(res.records[0].get('count')); - }) - .catch((error) => { - console.log(error); - }); +```js +app.get('/reports', async (req, res) => { + const report = await generateReport({ signal: req.signal }); + res.json(report); }); ``` -## Oracle +### Close connections on shutdown -**Module**: [oracledb](https://github.com/oracle/node-oracledb) +When your app receives a termination signal, stop accepting new requests and close your database connections so in-flight queries can finish cleanly: -### Installation +```js +const server = app.listen(3000); -NOTE: [See installation prerequisites](https://github.com/oracle/node-oracledb#-installation). - - - -### Example - -```cjs title="index.cjs" -const oracledb = require('oracledb'); -const config = { - user: '', - password: '', - connectString: 'localhost:1521/orcl', -}; - -async function getEmployee(empId) { - let conn; - - try { - conn = await oracledb.getConnection(config); - - const result = await conn.execute('select * from employees where employee_id = :id', [empId]); - - console.log(result.rows[0]); - } catch (err) { - console.log('Ouch!', err); - } finally { - if (conn) { - // conn assignment worked, need to close - await conn.close(); - } - } -} - -getEmployee(101); -``` - -```mjs title="index.mjs" -import oracledb from 'oracledb'; - -const config = { - user: '', - password: '', - connectString: 'localhost:1521/orcl', -}; - -async function getEmployee(empId) { - let conn; - - try { - conn = await oracledb.getConnection(config); - - const result = await conn.execute('select * from employees where employee_id = :id', [empId]); - - console.log(result.rows[0]); - } catch (err) { - console.log('Ouch!', err); - } finally { - if (conn) { - // conn assignment worked, need to close - await conn.close(); - } - } -} - -getEmployee(101); -``` - -## PostgreSQL - -**Module**: [pg-promise](https://github.com/vitaly-t/pg-promise) - -### Installation - - - -### Example - -```js title="index.js" -const pgp = require('pg-promise')(/* options */); -const db = pgp('postgres://username:password@host:port/database'); - -db.one('SELECT $1 AS value', 123) - .then((data) => { - console.log('DATA:', data.value); - }) - .catch((error) => { - console.log('ERROR:', error); +process.on('SIGTERM', () => { + server.close(async () => { + await pool.end(); + process.exit(0); }); -``` - -## Redis - -**Module**: [redis](https://github.com/mranney/node_redis) - -### Installation - - - -### Example - -```cjs title="index.cjs" -const redis = require('redis'); -const client = redis.createClient(); - -client.on('error', (err) => { - console.log(`Error ${err}`); -}); - -client.set('string key', 'string val', redis.print); -client.hset('hash key', 'hashtest 1', 'some value', redis.print); -client.hset(['hash key', 'hashtest 2', 'some other value'], redis.print); - -client.hkeys('hash key', (err, replies) => { - console.log(`${replies.length} replies:`); - - replies.forEach((reply, i) => { - console.log(` ${i}: ${reply}`); - }); - - client.quit(); -}); -``` - -```mjs title="index.mjs" -import redis from 'redis'; - -const client = redis.createClient(); - -client.on('error', (err) => { - console.log(`Error ${err}`); -}); - -client.set('string key', 'string val', redis.print); -client.hset('hash key', 'hashtest 1', 'some value', redis.print); -client.hset(['hash key', 'hashtest 2', 'some other value'], redis.print); - -client.hkeys('hash key', (err, replies) => { - console.log(`${replies.length} replies:`); - - replies.forEach((reply, i) => { - console.log(` ${i}: ${reply}`); - }); - - client.quit(); }); ``` -## SQL Server - -**Module**: [tedious](https://github.com/tediousjs/tedious) - -### Installation - - - -### Example - -```js title="index.js" -const Connection = require('tedious').Connection; -const Request = require('tedious').Request; - -const config = { - server: 'localhost', - authentication: { - type: 'default', - options: { - userName: 'your_username', // update me - password: 'your_password', // update me - }, - }, -}; - -const connection = new Connection(config); - -connection.on('connect', (err) => { - if (err) { - console.log(err); - } else { - executeStatement(); - } -}); - -function executeStatement() { - request = new Request("select 123, 'hello world'", (err, rowCount) => { - if (err) { - console.log(err); - } else { - console.log(`${rowCount} rows`); - } - connection.close(); - }); - - request.on('row', (columns) => { - columns.forEach((column) => { - if (column.value === null) { - console.log('NULL'); - } else { - console.log(column.value); - } - }); - }); - - connection.execSql(request); -} -``` - -## SQLite - -**Module**: [sqlite3](https://github.com/mapbox/node-sqlite3) - -### Installation - - - -### Example - -```js title="index.js" -const sqlite3 = require('sqlite3').verbose(); -const db = new sqlite3.Database(':memory:'); - -db.serialize(() => { - db.run('CREATE TABLE lorem (info TEXT)'); - const stmt = db.prepare('INSERT INTO lorem VALUES (?)'); - - for (let i = 0; i < 10; i++) { - stmt.run(`Ipsum ${i}`); - } - - stmt.finalize(); - - db.each('SELECT rowid AS id, info FROM lorem', (err, row) => { - console.log(`${row.id}: ${row.info}`); - }); -}); - -db.close(); -``` - -## Elasticsearch - -**Module**: [elasticsearch](https://github.com/elastic/elasticsearch-js) - -### Installation - - - -### Example - -```cjs title="index.cjs" -const elasticsearch = require('elasticsearch'); -const client = elasticsearch.Client({ - host: 'localhost:9200', -}); - -client - .search({ - index: 'books', - type: 'book', - body: { - query: { - multi_match: { - query: 'express js', - fields: ['title', 'description'], - }, - }, - }, - }) - .then( - (response) => { - const hits = response.hits.hits; - }, - (error) => { - console.trace(error.message); - } - ); -``` - -```mjs title="index.mjs" -import elasticsearch from 'elasticsearch'; - -const client = elasticsearch.Client({ - host: 'localhost:9200', -}); - -client - .search({ - index: 'books', - type: 'book', - body: { - query: { - multi_match: { - query: 'express js', - fields: ['title', 'description'], - }, - }, - }, - }) - .then( - (response) => { - const hits = response.hits.hits; - }, - (error) => { - console.trace(error.message); - } - ); -``` +If you already use an [AbortSignal](https://nodejs.org/api/globals.html#class-abortsignal) to coordinate cancellation across your app, you can pass it to `app.listen` and abort it instead of calling `server.close()` directly. diff --git a/src/content/pages/ko/resources/middleware/body-parser.mdx b/src/content/pages/ko/resources/middleware/body-parser.mdx index 3547c52f26..0b9148b175 100644 --- a/src/content/pages/ko/resources/middleware/body-parser.mdx +++ b/src/content/pages/ko/resources/middleware/body-parser.mdx @@ -8,7 +8,7 @@ import Alert from '@components/primitives/Alert/Alert.astro'; import PackageManagerCommand from '@components/patterns/PackageManagerCommand/PackageManagerCommand.astro'; @@ -82,6 +82,12 @@ The `bodyParser` object exposes various factories to create middlewares. All middlewares will populate the `req.body` property with the parsed body when the `Content-Type` request header matches the `type` option. +**Note** As of v2, `req.body` is left `undefined` when no body is parsed (for +example when the request has neither `Content-Length` nor `Transfer-Encoding`, +or when `Content-Type` does not match). In v1, `req.body` was always initialized +to `{}`. Code that assumes `req.body` is always an object should use optional +chaining (for example `req.body?.username`) or set a default. + The various errors returned by this module are described in the [errors section](#errors). @@ -126,7 +132,7 @@ argument. You can find more information on this argument ##### strict -When set to `true`, will only accept arrays and objects; when `false` will +When set to `true`, it will only accept arrays and objects; when `false`, it will accept anything `JSON.parse` accepts. Defaults to `true`. ##### type @@ -423,7 +429,7 @@ property is set to the encoding that is unsupported. ### The input exceeded the depth -This error occurs when using `bodyParser.urlencoded` with the `extended` property set to `true` and the input exceeds the configured `depth` option. The `status` property is set to `400`. It is recommended to review the `depth` option and evaluate if it requires a higher value. When the `depth` option is set to `32` (default value), the error will not be thrown. +This error occurs when using `bodyParser.urlencoded` with the `extended` property set to `true` and the input exceeds the configured `depth` option. The `status` property is set to `400`. It is recommended to review the `depth` option and evaluate if it requires a higher value. With the default `depth` of `32`, an input with 33 or more levels of nesting gets this error. ## Examples diff --git a/src/content/pages/ko/resources/middleware/multer.mdx b/src/content/pages/ko/resources/middleware/multer.mdx index d32246802f..7e8a4718ab 100644 --- a/src/content/pages/ko/resources/middleware/multer.mdx +++ b/src/content/pages/ko/resources/middleware/multer.mdx @@ -8,7 +8,7 @@ import Alert from '@components/primitives/Alert/Alert.astro'; import PackageManagerCommand from '@components/patterns/PackageManagerCommand/PackageManagerCommand.astro'; diff --git a/src/content/pages/pt-br/guide/database-integration.mdx b/src/content/pages/pt-br/guide/database-integration.mdx index f2e1510e6e..460d8718cb 100644 --- a/src/content/pages/pt-br/guide/database-integration.mdx +++ b/src/content/pages/pt-br/guide/database-integration.mdx @@ -1,656 +1,196 @@ --- title: Integração com base de dados -description: Descubra como integrar vários bancos de dados com aplicações do Express.js, incluindo exemplos de configuração para MongoDB, MySQL, PostgreSQL e muito mais. +description: Learn how to connect databases to Express.js applications, when to use an ORM, and best practices for connection pooling, security, and error handling. --- -import Alert from '@components/primitives/Alert/Alert.astro'; -import PackageManagerCommand from '@components/patterns/PackageManagerCommand/PackageManagerCommand.astro'; +To connect a database to an Express app simply load the desired Node.js driver or ORM in your app. The general pattern is shown below using SQLite as an example. -Adicionar a capacidade de conectar bancos de dados aos aplicativos Express é apenas uma questão de carregar um driver Node.js apropriado para o banco de dados em seu aplicativo. Este documento explica brevemente como adicionar e usar alguns dos mais populares módulos Node.js para os sistemas de banco de dados no seu aplicativo Express: +There are also a few [best practices](#best-practices) to follow, regardless of the database you're using. -- [Cassandra](#cassandra) -- [Couchbase](#couchbase) -- [CouchDB](#couchdb) -- [LevelDB](#leveldb) -- [MySQL](#mysql) -- [MongoDB](#mongodb) -- [Neo4j](#neo4j) -- [Oracle](#oracle) -- [PostgreSQL](#postgresql) -- [Redis](#redis) -- [SQL Server](#sql-server) -- [SQLite](#sqlite) -- [Elasticsearch](#elasticsearch) - - - -Estes motoristas da base de dados estão entre muitos que estão disponíveis. Para outras opções, pesquise no site -[npm](https://www.npmjs.com/). - - - -## Cassandra - -**Módulo**: [cassandra-driver](https://github.com/datastax/nodejs-driver) - -### Instalação +## SQLite - +Node.js includes a built-in SQLite client, [node:sqlite](https://nodejs.org/api/sqlite.html), available without a flag since Node.js 22.13.0. No installation is required. For older Node.js versions, [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) offers a similar synchronous API. ### Exemplo -```cjs title="index.cjs" -const cassandra = require('cassandra-driver'); -const client = new cassandra.Client({ contactPoints: ['localhost'] }); - -client.execute('select key from system.local', (err, result) => { - if (err) throw err; - console.log(result.rows[0]); -}); -``` +This example creates a sample user that you can retrieve at `http://localhost:3000/users/1`. It uses a synchronous SQLite API for simplicity: long-running queries block the thread handling requests. For workloads with long-running queries, consider moving database work to a worker thread or using an asynchronous database driver. -```mjs title="index.mjs" -import cassandra from 'cassandra-driver'; - -const client = new cassandra.Client({ contactPoints: ['localhost'] }); - -client.execute('select key from system.local', (err, result) => { - if (err) throw err; - console.log(result.rows[0]); -}); -``` - -## Couchbase - -**Module**: [couchnode](https://github.com/couchbase/couchnode) - -### Instalação +```cjs title="index.cjs" +const express = require('express'); +const { DatabaseSync } = require('node:sqlite'); - +const app = express(); +const db = new DatabaseSync('app.db'); -### Exemplo +db.exec('CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT NOT NULL)'); +db.prepare('INSERT INTO users (id, name) VALUES (?, ?) ON CONFLICT(id) DO NOTHING').run(1, 'Alice'); +const findUser = db.prepare('SELECT id, name FROM users WHERE id = ?'); -```cjs title="index.cjs" -const couchbase = require('couchbase'); -const bucket = new couchbase.Cluster('http://localhost:8091').openBucket('bucketName'); - -// add a document to a bucket -bucket.insert('document-key', { name: 'Matt', shoeSize: 13 }, (err, result) => { - if (err) { - console.log(err); - } else { - console.log(result); +app.get('/users/:id', (req, res) => { + const user = findUser.get(req.params.id); + if (!user) { + return res.status(404).json({ error: 'User not found' }); } + res.json(user); }); -// get all documents with shoe size 13 -const n1ql = 'SELECT d.* FROM `bucketName` d WHERE shoeSize = $1'; -const query = N1qlQuery.fromString(n1ql); -bucket.query(query, [13], (err, result) => { - if (err) { - console.log(err); - } else { - console.log(result); - } -}); +app.listen(3000); ``` ```mjs title="index.mjs" -import couchbase from 'couchbase'; +import express from 'express'; +import { DatabaseSync } from 'node:sqlite'; -const bucket = new couchbase.Cluster('http://localhost:8091').openBucket('bucketName'); +const app = express(); +const db = new DatabaseSync('app.db'); -// add a document to a bucket -bucket.insert('document-key', { name: 'Matt', shoeSize: 13 }, (err, result) => { - if (err) { - console.log(err); - } else { - console.log(result); - } -}); +db.exec('CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT NOT NULL)'); +db.prepare('INSERT INTO users (id, name) VALUES (?, ?) ON CONFLICT(id) DO NOTHING').run(1, 'Alice'); +const findUser = db.prepare('SELECT id, name FROM users WHERE id = ?'); -// get all documents with shoe size 13 -const n1ql = 'SELECT d.* FROM `bucketName` d WHERE shoeSize = $1'; -const query = N1qlQuery.fromString(n1ql); -bucket.query(query, [13], (err, result) => { - if (err) { - console.log(err); - } else { - console.log(result); +app.get('/users/:id', (req, res) => { + const user = findUser.get(req.params.id); + if (!user) { + return res.status(404).json({ error: 'User not found' }); } + res.json(user); }); + +app.listen(3000); ``` -## CouchDB +```ts title="index.ts" +import express from 'express'; +import { DatabaseSync } from 'node:sqlite'; -**Module**: [nano](https://github.com/dscape/nano) +interface User { + id: number; + name: string; +} -### Instalação +const app = express(); +const db = new DatabaseSync('app.db'); - +db.exec('CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT NOT NULL)'); +db.prepare('INSERT INTO users (id, name) VALUES (?, ?) ON CONFLICT(id) DO NOTHING').run(1, 'Alice'); +const findUser = db.prepare('SELECT id, name FROM users WHERE id = ?'); -### Exemplo - -```js title="index.js" -const nano = require('nano')('http://localhost:5984'); -nano.db.create('books'); -const books = nano.db.use('books'); - -// Insert a book document in the books database -books.insert({ name: 'The Art of war' }, null, (err, body) => { - if (err) { - console.log(err); - } else { - console.log(body); +app.get('/users/:id', (req, res) => { + const user = findUser.get(req.params.id) as User | undefined; + if (!user) { + return res.status(404).json({ error: 'User not found' }); } + res.json(user); }); -// Get a list of all books -books.list((err, body) => { - if (err) { - console.log(err); - } else { - console.log(body.rows); - } -}); +app.listen(3000); ``` -## LevelDB +## Other databases -**Module**: [levelup](https://github.com/rvagg/node-levelup) +Every other database follows the same pattern: install the Node.js driver from npm, create a client or connection pool once when the app starts, and use it in your route handlers. Refer to the official documentation of each driver for connection options and API details: -### Instalação +- PostgreSQL: [pg](https://node-postgres.com/) or [postgres](https://github.com/porsager/postgres) +- MySQL and MariaDB: [mysql2](https://sidorares.github.io/node-mysql2/docs) or [mariadb](https://mariadb.com/docs/connectors/mariadb-connector-nodejs) +- MongoDB: [mongodb](https://www.mongodb.com/docs/drivers/node/current/) +- Redis: [redis](https://redis.io/docs/latest/develop/clients/nodejs/) or [ioredis](https://github.com/redis/ioredis) +- SQL Server: [mssql](https://github.com/tediousjs/node-mssql) +- Oracle: [oracledb](https://www.npmjs.com/package/oracledb) +- Cassandra: [cassandra-driver](https://docs.datastax.com/en/developer/nodejs-driver/latest/) +- Elasticsearch: [@elastic/elasticsearch](https://www.elastic.co/docs/reference/elasticsearch/clients/javascript) - - -### Exemplo +For any database not listed here, search for its driver on the [npm](https://www.npmjs.com/) site. -```cjs title="index.cjs" -const levelup = require('levelup'); -const db = levelup('./mydb'); +## ORMs and query builders -db.put('name', 'LevelUP', (err) => { - if (err) return console.log('Ooops!', err); +The example above uses a database driver directly, which is a good fit for small apps and for learning how things work. As an application grows, an ORM (object-relational mapper) or a query builder can take over concerns such as schema definitions, migrations, relations between tables, and input mapping, while reducing boilerplate. Many of them also provide type safety when used with TypeScript. - db.get('name', (err, value) => { - if (err) return console.log('Ooops!', err); +Popular options in the Node.js ecosystem include: - console.log(`name=${value}`); - }); -}); -``` +- [Prisma](https://www.prisma.io/): a type-safe ORM with a schema-first workflow and built-in migrations. Supports PostgreSQL, MySQL, SQLite, SQL Server, MongoDB, and CockroachDB. +- [Drizzle ORM](https://orm.drizzle.team/): a lightweight, TypeScript-first ORM with a SQL-like query API and a migrations toolkit. +- [Sequelize](https://sequelize.org/): a mature, promise-based ORM for PostgreSQL, MySQL, MariaDB, SQLite, and SQL Server. +- [TypeORM](https://typeorm.io/): an ORM built around decorators and entities, supporting many SQL databases and MongoDB. +- [Mongoose](https://mongoosejs.com/): an ODM (object document mapper) for MongoDB. +- [Knex.js](https://knexjs.org/): a flexible SQL query builder with migrations support, useful when you want control over your queries without writing raw strings. +- [Kysely](https://kysely.dev/): a type-safe SQL query builder for TypeScript. -```mjs title="index.mjs" -import levelup from 'levelup'; +Each ORM has its own API, supported databases, and approach to schemas, migrations, and queries. Features and setup requirements vary, so consult each project's documentation before choosing one. Query builders keep you close to SQL, while full ORMs abstract more of it away. Pick the level of abstraction that matches your team and project, and prefer tools that are actively maintained. -const db = levelup('./mydb'); +## Best practices -db.put('name', 'LevelUP', (err) => { - if (err) return console.log('Ooops!', err); +Adapt these practices to the database and library you choose. The snippets below illustrate separate parts of an application. The PostgreSQL snippets assume you have installed the [pg](https://node-postgres.com/) driver and created a shared pool: - db.get('name', (err, value) => { - if (err) return console.log('Ooops!', err); +```js +import { Pool } from 'pg'; - console.log(`name=${value}`); - }); -}); +const pool = new Pool({ connectionString: process.env.DATABASE_URL }); ``` -## MySQL - -**Module**: [mysql](https://github.com/felixge/node-mysql/) - -### Instalação - - - -### Exemplo - -```cjs title="index.cjs" -const mysql = require('mysql'); -const connection = mysql.createConnection({ - host: 'localhost', - user: 'dbuser', - password: 's3kreee7', - database: 'my_db', -}); +### Create connections once and reuse them -connection.connect(); +Opening a database connection is expensive. Many drivers provide a connection pool that creates connections as needed and reuses them across requests. Create the pool (or client) once when the app starts. Acquire connections from that shared pool as needed in your route handlers, for example for a transaction, and always release them in a `finally` block. For individual queries, `pool.query()` in `pg` handles acquiring and releasing a connection for you. -connection.query('SELECT 1 + 1 AS solution', (err, rows, fields) => { - if (err) throw err; +### Use parameterized queries - console.log('The solution is: ', rows[0].solution); -}); +Never build SQL queries by concatenating or interpolating user input into the query string. Doing so exposes your app to SQL injection. Use your driver's parameterized queries or prepared statements to pass user input separately from the query itself. -connection.end(); +```js +// Vulnerable: user input becomes part of the query +const user = await pool.query(`SELECT * FROM users WHERE id = ${req.params.id}`); ``` -```mjs title="index.mjs" -import mysql from 'mysql'; - -const connection = mysql.createConnection({ - host: 'localhost', - user: 'dbuser', - password: 's3kreee7', - database: 'my_db', -}); - -connection.connect(); - -connection.query('SELECT 1 + 1 AS solution', (err, rows, fields) => { - if (err) throw err; - - console.log('The solution is: ', rows[0].solution); -}); - -connection.end(); +```js +// Safe: user input is passed as a parameter +const user = await pool.query('SELECT * FROM users WHERE id = $1', [req.params.id]); ``` -## MongoDB - -**Module**: [mongodb](https://github.com/mongodb/node-mongodb-native) - -### Instalação - - - -### Example (v2.\*) - -```js title="index.js" -const MongoClient = require('mongodb').MongoClient; +Also validate user input before it reaches your queries, both for security and for data integrity. For NoSQL databases, construct queries using the driver's API and validate the types, fields, and operators you accept. Do not pass user-supplied query objects directly to the database. -MongoClient.connect('mongodb://localhost:27017/animals', (err, db) => { - if (err) throw err; +### Keep credentials out of your code - db.collection('mammals') - .find() - .toArray((err, result) => { - if (err) throw err; +Never hardcode connection strings, usernames, or passwords in your source code, and never commit them to version control. Read them from environment variables or a secrets manager instead: - console.log(result); - }); -}); +```js +const pool = new Pool({ connectionString: process.env.DATABASE_URL }); ``` -### Example (v3.\*) - -```js title="index.js" -const MongoClient = require('mongodb').MongoClient; - -MongoClient.connect('mongodb://localhost:27017/animals', (err, client) => { - if (err) throw err; - - const db = client.db('animals'); - - db.collection('mammals') - .find() - .toArray((err, result) => { - if (err) throw err; +During development, you can keep these values in a `.env` file and load it with Node.js's built-in support for environment files, with no extra dependencies: - console.log(result); - }); -}); +```bash +node --env-file=.env index.js ``` -If you want an object model driver for MongoDB, look at [Mongoose](https://github.com/LearnBoost/mongoose). - -## Neo4j - -**Module**: [neo4j-driver](https://github.com/neo4j/neo4j-javascript-driver) +Make sure `.env` is listed in your `.gitignore` file so it never gets committed. -### Instalação - - - -### Exemplo +### Handle database errors -```cjs title="index.cjs" -const neo4j = require('neo4j-driver'); -const driver = neo4j.driver('neo4j://localhost:7687', neo4j.auth.basic('neo4j', 'letmein')); - -const session = driver.session(); - -session.readTransaction((tx) => { - return tx - .run('MATCH (n) RETURN count(n) AS count') - .then((res) => { - console.log(res.records[0].get('count')); - }) - .catch((error) => { - console.log(error); - }); -}); -``` - -```mjs title="index.mjs" -import neo4j from 'neo4j-driver'; +Database calls fail: connections drop, queries time out, constraints are violated. In Express 5, errors thrown in async route handlers are passed to your [error-handling middleware](/en/guide/error-handling) automatically, so define one and decide there what to expose to clients. Avoid leaking internal error details, such as query text or stack traces, in API responses. -const driver = neo4j.driver('neo4j://localhost:7687', neo4j.auth.basic('neo4j', 'letmein')); +### Cancel work when the client disconnects -const session = driver.session(); +Since Node.js 24.20.0, every request exposes [req.signal](https://nodejs.org/api/http.html#messagesignal), an `AbortSignal` that is aborted when the client disconnects. Pass it to any cancellation-aware API, such as a database driver that supports abort signals, so your app stops doing work nobody is waiting for: -session.readTransaction((tx) => { - return tx - .run('MATCH (n) RETURN count(n) AS count') - .then((res) => { - console.log(res.records[0].get('count')); - }) - .catch((error) => { - console.log(error); - }); +```js +app.get('/reports', async (req, res) => { + const report = await generateReport({ signal: req.signal }); + res.json(report); }); ``` -## Oracle +### Close connections on shutdown -**Module**: [oracledb](https://github.com/oracle/node-oracledb) +When your app receives a termination signal, stop accepting new requests and close your database connections so in-flight queries can finish cleanly: -### Instalação +```js +const server = app.listen(3000); -NOTE: [See installation prerequisites](https://github.com/oracle/node-oracledb#-installation). - - - -### Exemplo - -```cjs title="index.cjs" -const oracledb = require('oracledb'); -const config = { - user: '', - password: '', - connectString: 'localhost:1521/orcl', -}; - -async function getEmployee(empId) { - let conn; - - try { - conn = await oracledb.getConnection(config); - - const result = await conn.execute('select * from employees where employee_id = :id', [empId]); - - console.log(result.rows[0]); - } catch (err) { - console.log('Ouch!', err); - } finally { - if (conn) { - // conn assignment worked, need to close - await conn.close(); - } - } -} - -getEmployee(101); -``` - -```mjs title="index.mjs" -import oracledb from 'oracledb'; - -const config = { - user: '', - password: '', - connectString: 'localhost:1521/orcl', -}; - -async function getEmployee(empId) { - let conn; - - try { - conn = await oracledb.getConnection(config); - - const result = await conn.execute('select * from employees where employee_id = :id', [empId]); - - console.log(result.rows[0]); - } catch (err) { - console.log('Ouch!', err); - } finally { - if (conn) { - // conn assignment worked, need to close - await conn.close(); - } - } -} - -getEmployee(101); -``` - -## PostgreSQL - -**Module**: [pg-promise](https://github.com/vitaly-t/pg-promise) - -### Instalação - - - -### Exemplo - -```js title="index.js" -const pgp = require('pg-promise')(/* options */); -const db = pgp('postgres://username:password@host:port/database'); - -db.one('SELECT $1 AS value', 123) - .then((data) => { - console.log('DATA:', data.value); - }) - .catch((error) => { - console.log('ERROR:', error); +process.on('SIGTERM', () => { + server.close(async () => { + await pool.end(); + process.exit(0); }); -``` - -## Redis - -**Module**: [redis](https://github.com/mranney/node_redis) - -### Instalação - - - -### Exemplo - -```cjs title="index.cjs" -const redis = require('redis'); -const client = redis.createClient(); - -client.on('error', (err) => { - console.log(`Error ${err}`); -}); - -client.set('string key', 'string val', redis.print); -client.hset('hash key', 'hashtest 1', 'some value', redis.print); -client.hset(['hash key', 'hashtest 2', 'some other value'], redis.print); - -client.hkeys('hash key', (err, replies) => { - console.log(`${replies.length} replies:`); - - replies.forEach((reply, i) => { - console.log(` ${i}: ${reply}`); - }); - - client.quit(); -}); -``` - -```mjs title="index.mjs" -import redis from 'redis'; - -const client = redis.createClient(); - -client.on('error', (err) => { - console.log(`Error ${err}`); -}); - -client.set('string key', 'string val', redis.print); -client.hset('hash key', 'hashtest 1', 'some value', redis.print); -client.hset(['hash key', 'hashtest 2', 'some other value'], redis.print); - -client.hkeys('hash key', (err, replies) => { - console.log(`${replies.length} replies:`); - - replies.forEach((reply, i) => { - console.log(` ${i}: ${reply}`); - }); - - client.quit(); }); ``` -## SQL Server - -**Module**: [tedious](https://github.com/tediousjs/tedious) - -### Instalação - - - -### Exemplo - -```js title="index.js" -const Connection = require('tedious').Connection; -const Request = require('tedious').Request; - -const config = { - server: 'localhost', - authentication: { - type: 'default', - options: { - userName: 'your_username', // update me - password: 'your_password', // update me - }, - }, -}; - -const connection = new Connection(config); - -connection.on('connect', (err) => { - if (err) { - console.log(err); - } else { - executeStatement(); - } -}); - -function executeStatement() { - request = new Request("select 123, 'hello world'", (err, rowCount) => { - if (err) { - console.log(err); - } else { - console.log(`${rowCount} rows`); - } - connection.close(); - }); - - request.on('row', (columns) => { - columns.forEach((column) => { - if (column.value === null) { - console.log('NULL'); - } else { - console.log(column.value); - } - }); - }); - - connection.execSql(request); -} -``` - -## SQLite - -**Module**: [sqlite3](https://github.com/mapbox/node-sqlite3) - -### Instalação - - - -### Exemplo - -```js title="index.js" -const sqlite3 = require('sqlite3').verbose(); -const db = new sqlite3.Database(':memory:'); - -db.serialize(() => { - db.run('CREATE TABLE lorem (info TEXT)'); - const stmt = db.prepare('INSERT INTO lorem VALUES (?)'); - - for (let i = 0; i < 10; i++) { - stmt.run(`Ipsum ${i}`); - } - - stmt.finalize(); - - db.each('SELECT rowid AS id, info FROM lorem', (err, row) => { - console.log(`${row.id}: ${row.info}`); - }); -}); - -db.close(); -``` - -## Elasticsearch - -**Module**: [elasticsearch](https://github.com/elastic/elasticsearch-js) - -### Instalação - - - -### Exemplo - -```cjs title="index.cjs" -const elasticsearch = require('elasticsearch'); -const client = elasticsearch.Client({ - host: 'localhost:9200', -}); - -client - .search({ - index: 'books', - type: 'book', - body: { - query: { - multi_match: { - query: 'express js', - fields: ['title', 'description'], - }, - }, - }, - }) - .then( - (response) => { - const hits = response.hits.hits; - }, - (error) => { - console.trace(error.message); - } - ); -``` - -```mjs title="index.mjs" -import elasticsearch from 'elasticsearch'; - -const client = elasticsearch.Client({ - host: 'localhost:9200', -}); - -client - .search({ - index: 'books', - type: 'book', - body: { - query: { - multi_match: { - query: 'express js', - fields: ['title', 'description'], - }, - }, - }, - }) - .then( - (response) => { - const hits = response.hits.hits; - }, - (error) => { - console.trace(error.message); - } - ); -``` +If you already use an [AbortSignal](https://nodejs.org/api/globals.html#class-abortsignal) to coordinate cancellation across your app, you can pass it to `app.listen` and abort it instead of calling `server.close()` directly. diff --git a/src/content/pages/pt-br/resources/middleware/body-parser.mdx b/src/content/pages/pt-br/resources/middleware/body-parser.mdx index 8cb3469679..aee2c85477 100644 --- a/src/content/pages/pt-br/resources/middleware/body-parser.mdx +++ b/src/content/pages/pt-br/resources/middleware/body-parser.mdx @@ -8,7 +8,7 @@ import Alert from '@components/primitives/Alert/Alert.astro'; import PackageManagerCommand from '@components/patterns/PackageManagerCommand/PackageManagerCommand.astro'; @@ -82,6 +82,12 @@ O objeto `bodyParser` expõe várias fábricas para criar intermediários. Todos middlewares irão preencher a propriedade `req.body` com o corpo analisado quando o cabeçalho de solicitação `Content-Type` corresponde à opção `type`. +**Note** As of v2, `req.body` is left `undefined` when no body is parsed (for +example when the request has neither `Content-Length` nor `Transfer-Encoding`, +or when `Content-Type` does not match). In v1, `req.body` was always initialized +to `{}`. Code that assumes `req.body` is always an object should use optional +chaining (for example `req.body?.username`) or set a default. + Os vários erros retornados por este módulo estão descritos na [seção de erro](#errors). @@ -126,8 +132,8 @@ A opção `reviver` é passada diretamente para `JSON.parse` como o segundo argu ##### estrito -Quando definido como `true`, só vai aceitar matrizes e objetos; quando `false` -vai aceitar qualquer coisa que `JSON.parse` aceita. O padrão é 'true'. +When set to `true`, it will only accept arrays and objects; when `false`, it will +accept anything `JSON.parse` accepts. O padrão é 'true'. ##### Tipo @@ -423,7 +429,7 @@ property is set to the encoding that is unsupported. ### The input exceeded the depth -This error occurs when using `bodyParser.urlencoded` with the `extended` property set to `true` and the input exceeds the configured `depth` option. The `status` property is set to `400`. It is recommended to review the `depth` option and evaluate if it requires a higher value. When the `depth` option is set to `32` (default value), the error will not be thrown. +This error occurs when using `bodyParser.urlencoded` with the `extended` property set to `true` and the input exceeds the configured `depth` option. The `status` property is set to `400`. It is recommended to review the `depth` option and evaluate if it requires a higher value. With the default `depth` of `32`, an input with 33 or more levels of nesting gets this error. ## Exemplos diff --git a/src/content/pages/pt-br/resources/middleware/multer.mdx b/src/content/pages/pt-br/resources/middleware/multer.mdx index f678c0d137..9e976fa8c3 100644 --- a/src/content/pages/pt-br/resources/middleware/multer.mdx +++ b/src/content/pages/pt-br/resources/middleware/multer.mdx @@ -8,7 +8,7 @@ import Alert from '@components/primitives/Alert/Alert.astro'; import PackageManagerCommand from '@components/patterns/PackageManagerCommand/PackageManagerCommand.astro'; diff --git a/src/content/pages/zh-cn/guide/database-integration.mdx b/src/content/pages/zh-cn/guide/database-integration.mdx index d5822072a4..faec0b3327 100644 --- a/src/content/pages/zh-cn/guide/database-integration.mdx +++ b/src/content/pages/zh-cn/guide/database-integration.mdx @@ -1,655 +1,196 @@ --- title: 数据库集成 -description: 了解如何将各种数据库与 Express.js 应用程序集成,包括 MongoDB、MySQL、PostgreSQL 等的配置示例。 +description: Learn how to connect databases to Express.js applications, when to use an ORM, and best practices for connection pooling, security, and error handling. --- -import Alert from '@components/primitives/Alert/Alert.astro'; -import PackageManagerCommand from '@components/patterns/PackageManagerCommand/PackageManagerCommand.astro'; +To connect a database to an Express app simply load the desired Node.js driver or ORM in your app. The general pattern is shown below using SQLite as an example. -为 Express 应用添加连接数据库的功能,只需在应用中加载对应数据库的合适 Node.js 驱动程序即可。本文简要说明如何在 Express 应用中添加并使用一些最流行的数据库系统 Node.js 模块: +There are also a few [best practices](#best-practices) to follow, regardless of the database you're using. -- [Cassandra](#cassandra) -- [Couchbase](#couchbase) -- [CouchDB](#couchdb) -- [LevelDB](#leveldb) -- [MySQL](#mysql) -- [MongoDB](#mongodb) -- [Neo4j](#neo4j) -- [Oracle](#oracle) -- [PostgreSQL](#postgresql) -- [Redis](#redis) -- [SQL Server](#sql-server) -- [SQLite](#sqlite) -- [Elasticsearch](#elasticsearch) - - - -这些数据库驱动只是众多可选驱动中的一部分。如需其他方案,可前往[npm](https://www.npmjs.com/)官网检索。 - - - -## Cassandra - -**Module**: [cassandra-driver](https://github.com/datastax/nodejs-driver) - -### 安装 +## SQLite - +Node.js includes a built-in SQLite client, [node:sqlite](https://nodejs.org/api/sqlite.html), available without a flag since Node.js 22.13.0. No installation is required. For older Node.js versions, [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) offers a similar synchronous API. ### 示例 -```cjs title="index.cjs" -const cassandra = require('cassandra-driver'); -const client = new cassandra.Client({ contactPoints: ['localhost'] }); - -client.execute('select key from system.local', (err, result) => { - if (err) throw err; - console.log(result.rows[0]); -}); -``` +This example creates a sample user that you can retrieve at `http://localhost:3000/users/1`. It uses a synchronous SQLite API for simplicity: long-running queries block the thread handling requests. For workloads with long-running queries, consider moving database work to a worker thread or using an asynchronous database driver. -```mjs title="index.mjs" -import cassandra from 'cassandra-driver'; - -const client = new cassandra.Client({ contactPoints: ['localhost'] }); - -client.execute('select key from system.local', (err, result) => { - if (err) throw err; - console.log(result.rows[0]); -}); -``` - -## Couchbase - -**Module**: [couchnode](https://github.com/couchbase/couchnode) - -### 安装 +```cjs title="index.cjs" +const express = require('express'); +const { DatabaseSync } = require('node:sqlite'); - +const app = express(); +const db = new DatabaseSync('app.db'); -### 示例 +db.exec('CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT NOT NULL)'); +db.prepare('INSERT INTO users (id, name) VALUES (?, ?) ON CONFLICT(id) DO NOTHING').run(1, 'Alice'); +const findUser = db.prepare('SELECT id, name FROM users WHERE id = ?'); -```cjs title="index.cjs" -const couchbase = require('couchbase'); -const bucket = new couchbase.Cluster('http://localhost:8091').openBucket('bucketName'); - -// add a document to a bucket -bucket.insert('document-key', { name: 'Matt', shoeSize: 13 }, (err, result) => { - if (err) { - console.log(err); - } else { - console.log(result); +app.get('/users/:id', (req, res) => { + const user = findUser.get(req.params.id); + if (!user) { + return res.status(404).json({ error: 'User not found' }); } + res.json(user); }); -// get all documents with shoe size 13 -const n1ql = 'SELECT d.* FROM `bucketName` d WHERE shoeSize = $1'; -const query = N1qlQuery.fromString(n1ql); -bucket.query(query, [13], (err, result) => { - if (err) { - console.log(err); - } else { - console.log(result); - } -}); +app.listen(3000); ``` ```mjs title="index.mjs" -import couchbase from 'couchbase'; +import express from 'express'; +import { DatabaseSync } from 'node:sqlite'; -const bucket = new couchbase.Cluster('http://localhost:8091').openBucket('bucketName'); +const app = express(); +const db = new DatabaseSync('app.db'); -// add a document to a bucket -bucket.insert('document-key', { name: 'Matt', shoeSize: 13 }, (err, result) => { - if (err) { - console.log(err); - } else { - console.log(result); - } -}); +db.exec('CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT NOT NULL)'); +db.prepare('INSERT INTO users (id, name) VALUES (?, ?) ON CONFLICT(id) DO NOTHING').run(1, 'Alice'); +const findUser = db.prepare('SELECT id, name FROM users WHERE id = ?'); -// get all documents with shoe size 13 -const n1ql = 'SELECT d.* FROM `bucketName` d WHERE shoeSize = $1'; -const query = N1qlQuery.fromString(n1ql); -bucket.query(query, [13], (err, result) => { - if (err) { - console.log(err); - } else { - console.log(result); +app.get('/users/:id', (req, res) => { + const user = findUser.get(req.params.id); + if (!user) { + return res.status(404).json({ error: 'User not found' }); } + res.json(user); }); + +app.listen(3000); ``` -## CouchDB +```ts title="index.ts" +import express from 'express'; +import { DatabaseSync } from 'node:sqlite'; -**Module**: [nano](https://github.com/dscape/nano) +interface User { + id: number; + name: string; +} -### 安装 +const app = express(); +const db = new DatabaseSync('app.db'); - +db.exec('CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT NOT NULL)'); +db.prepare('INSERT INTO users (id, name) VALUES (?, ?) ON CONFLICT(id) DO NOTHING').run(1, 'Alice'); +const findUser = db.prepare('SELECT id, name FROM users WHERE id = ?'); -### 示例 - -```js title="index.js" -const nano = require('nano')('http://localhost:5984'); -nano.db.create('books'); -const books = nano.db.use('books'); - -// Insert a book document in the books database -books.insert({ name: 'The Art of war' }, null, (err, body) => { - if (err) { - console.log(err); - } else { - console.log(body); +app.get('/users/:id', (req, res) => { + const user = findUser.get(req.params.id) as User | undefined; + if (!user) { + return res.status(404).json({ error: 'User not found' }); } + res.json(user); }); -// Get a list of all books -books.list((err, body) => { - if (err) { - console.log(err); - } else { - console.log(body.rows); - } -}); +app.listen(3000); ``` -## LevelDB +## Other databases -**Module**: [levelup](https://github.com/rvagg/node-levelup) +Every other database follows the same pattern: install the Node.js driver from npm, create a client or connection pool once when the app starts, and use it in your route handlers. Refer to the official documentation of each driver for connection options and API details: -### 安装 +- PostgreSQL: [pg](https://node-postgres.com/) or [postgres](https://github.com/porsager/postgres) +- MySQL and MariaDB: [mysql2](https://sidorares.github.io/node-mysql2/docs) or [mariadb](https://mariadb.com/docs/connectors/mariadb-connector-nodejs) +- MongoDB: [mongodb](https://www.mongodb.com/docs/drivers/node/current/) +- Redis: [redis](https://redis.io/docs/latest/develop/clients/nodejs/) or [ioredis](https://github.com/redis/ioredis) +- SQL Server: [mssql](https://github.com/tediousjs/node-mssql) +- Oracle: [oracledb](https://www.npmjs.com/package/oracledb) +- Cassandra: [cassandra-driver](https://docs.datastax.com/en/developer/nodejs-driver/latest/) +- Elasticsearch: [@elastic/elasticsearch](https://www.elastic.co/docs/reference/elasticsearch/clients/javascript) - - -### 示例 +For any database not listed here, search for its driver on the [npm](https://www.npmjs.com/) site. -```cjs title="index.cjs" -const levelup = require('levelup'); -const db = levelup('./mydb'); +## ORMs and query builders -db.put('name', 'LevelUP', (err) => { - if (err) return console.log('Ooops!', err); +The example above uses a database driver directly, which is a good fit for small apps and for learning how things work. As an application grows, an ORM (object-relational mapper) or a query builder can take over concerns such as schema definitions, migrations, relations between tables, and input mapping, while reducing boilerplate. Many of them also provide type safety when used with TypeScript. - db.get('name', (err, value) => { - if (err) return console.log('Ooops!', err); +Popular options in the Node.js ecosystem include: - console.log(`name=${value}`); - }); -}); -``` +- [Prisma](https://www.prisma.io/): a type-safe ORM with a schema-first workflow and built-in migrations. Supports PostgreSQL, MySQL, SQLite, SQL Server, MongoDB, and CockroachDB. +- [Drizzle ORM](https://orm.drizzle.team/): a lightweight, TypeScript-first ORM with a SQL-like query API and a migrations toolkit. +- [Sequelize](https://sequelize.org/): a mature, promise-based ORM for PostgreSQL, MySQL, MariaDB, SQLite, and SQL Server. +- [TypeORM](https://typeorm.io/): an ORM built around decorators and entities, supporting many SQL databases and MongoDB. +- [Mongoose](https://mongoosejs.com/): an ODM (object document mapper) for MongoDB. +- [Knex.js](https://knexjs.org/): a flexible SQL query builder with migrations support, useful when you want control over your queries without writing raw strings. +- [Kysely](https://kysely.dev/): a type-safe SQL query builder for TypeScript. -```mjs title="index.mjs" -import levelup from 'levelup'; +Each ORM has its own API, supported databases, and approach to schemas, migrations, and queries. Features and setup requirements vary, so consult each project's documentation before choosing one. Query builders keep you close to SQL, while full ORMs abstract more of it away. Pick the level of abstraction that matches your team and project, and prefer tools that are actively maintained. -const db = levelup('./mydb'); +## Best practices -db.put('name', 'LevelUP', (err) => { - if (err) return console.log('Ooops!', err); +Adapt these practices to the database and library you choose. The snippets below illustrate separate parts of an application. The PostgreSQL snippets assume you have installed the [pg](https://node-postgres.com/) driver and created a shared pool: - db.get('name', (err, value) => { - if (err) return console.log('Ooops!', err); +```js +import { Pool } from 'pg'; - console.log(`name=${value}`); - }); -}); +const pool = new Pool({ connectionString: process.env.DATABASE_URL }); ``` -## MySQL - -**Module**: [mysql](https://github.com/felixge/node-mysql/) - -### 安装 - - - -### 示例 - -```cjs title="index.cjs" -const mysql = require('mysql'); -const connection = mysql.createConnection({ - host: 'localhost', - user: 'dbuser', - password: 's3kreee7', - database: 'my_db', -}); +### Create connections once and reuse them -connection.connect(); +Opening a database connection is expensive. Many drivers provide a connection pool that creates connections as needed and reuses them across requests. Create the pool (or client) once when the app starts. Acquire connections from that shared pool as needed in your route handlers, for example for a transaction, and always release them in a `finally` block. For individual queries, `pool.query()` in `pg` handles acquiring and releasing a connection for you. -connection.query('SELECT 1 + 1 AS solution', (err, rows, fields) => { - if (err) throw err; +### Use parameterized queries - console.log('The solution is: ', rows[0].solution); -}); +Never build SQL queries by concatenating or interpolating user input into the query string. Doing so exposes your app to SQL injection. Use your driver's parameterized queries or prepared statements to pass user input separately from the query itself. -connection.end(); +```js +// Vulnerable: user input becomes part of the query +const user = await pool.query(`SELECT * FROM users WHERE id = ${req.params.id}`); ``` -```mjs title="index.mjs" -import mysql from 'mysql'; - -const connection = mysql.createConnection({ - host: 'localhost', - user: 'dbuser', - password: 's3kreee7', - database: 'my_db', -}); - -connection.connect(); - -connection.query('SELECT 1 + 1 AS solution', (err, rows, fields) => { - if (err) throw err; - - console.log('The solution is: ', rows[0].solution); -}); - -connection.end(); +```js +// Safe: user input is passed as a parameter +const user = await pool.query('SELECT * FROM users WHERE id = $1', [req.params.id]); ``` -## MongoDB - -**Module**: [mongodb](https://github.com/mongodb/node-mongodb-native) - -### 安装 - - - -### Example (v2.\*) - -```js title="index.js" -const MongoClient = require('mongodb').MongoClient; +Also validate user input before it reaches your queries, both for security and for data integrity. For NoSQL databases, construct queries using the driver's API and validate the types, fields, and operators you accept. Do not pass user-supplied query objects directly to the database. -MongoClient.connect('mongodb://localhost:27017/animals', (err, db) => { - if (err) throw err; +### Keep credentials out of your code - db.collection('mammals') - .find() - .toArray((err, result) => { - if (err) throw err; +Never hardcode connection strings, usernames, or passwords in your source code, and never commit them to version control. Read them from environment variables or a secrets manager instead: - console.log(result); - }); -}); +```js +const pool = new Pool({ connectionString: process.env.DATABASE_URL }); ``` -### Example (v3.\*) - -```js title="index.js" -const MongoClient = require('mongodb').MongoClient; - -MongoClient.connect('mongodb://localhost:27017/animals', (err, client) => { - if (err) throw err; - - const db = client.db('animals'); - - db.collection('mammals') - .find() - .toArray((err, result) => { - if (err) throw err; +During development, you can keep these values in a `.env` file and load it with Node.js's built-in support for environment files, with no extra dependencies: - console.log(result); - }); -}); +```bash +node --env-file=.env index.js ``` -If you want an object model driver for MongoDB, look at [Mongoose](https://github.com/LearnBoost/mongoose). - -## Neo4j - -**Module**: [neo4j-driver](https://github.com/neo4j/neo4j-javascript-driver) +Make sure `.env` is listed in your `.gitignore` file so it never gets committed. -### 安装 - - - -### 示例 +### Handle database errors -```cjs title="index.cjs" -const neo4j = require('neo4j-driver'); -const driver = neo4j.driver('neo4j://localhost:7687', neo4j.auth.basic('neo4j', 'letmein')); - -const session = driver.session(); - -session.readTransaction((tx) => { - return tx - .run('MATCH (n) RETURN count(n) AS count') - .then((res) => { - console.log(res.records[0].get('count')); - }) - .catch((error) => { - console.log(error); - }); -}); -``` - -```mjs title="index.mjs" -import neo4j from 'neo4j-driver'; +Database calls fail: connections drop, queries time out, constraints are violated. In Express 5, errors thrown in async route handlers are passed to your [error-handling middleware](/en/guide/error-handling) automatically, so define one and decide there what to expose to clients. Avoid leaking internal error details, such as query text or stack traces, in API responses. -const driver = neo4j.driver('neo4j://localhost:7687', neo4j.auth.basic('neo4j', 'letmein')); +### Cancel work when the client disconnects -const session = driver.session(); +Since Node.js 24.20.0, every request exposes [req.signal](https://nodejs.org/api/http.html#messagesignal), an `AbortSignal` that is aborted when the client disconnects. Pass it to any cancellation-aware API, such as a database driver that supports abort signals, so your app stops doing work nobody is waiting for: -session.readTransaction((tx) => { - return tx - .run('MATCH (n) RETURN count(n) AS count') - .then((res) => { - console.log(res.records[0].get('count')); - }) - .catch((error) => { - console.log(error); - }); +```js +app.get('/reports', async (req, res) => { + const report = await generateReport({ signal: req.signal }); + res.json(report); }); ``` -## Oracle +### Close connections on shutdown -**Module**: [oracledb](https://github.com/oracle/node-oracledb) +When your app receives a termination signal, stop accepting new requests and close your database connections so in-flight queries can finish cleanly: -### 安装 +```js +const server = app.listen(3000); -NOTE: [See installation prerequisites](https://github.com/oracle/node-oracledb#-installation). - - - -### 示例 - -```cjs title="index.cjs" -const oracledb = require('oracledb'); -const config = { - user: '', - password: '', - connectString: 'localhost:1521/orcl', -}; - -async function getEmployee(empId) { - let conn; - - try { - conn = await oracledb.getConnection(config); - - const result = await conn.execute('select * from employees where employee_id = :id', [empId]); - - console.log(result.rows[0]); - } catch (err) { - console.log('Ouch!', err); - } finally { - if (conn) { - // conn assignment worked, need to close - await conn.close(); - } - } -} - -getEmployee(101); -``` - -```mjs title="index.mjs" -import oracledb from 'oracledb'; - -const config = { - user: '', - password: '', - connectString: 'localhost:1521/orcl', -}; - -async function getEmployee(empId) { - let conn; - - try { - conn = await oracledb.getConnection(config); - - const result = await conn.execute('select * from employees where employee_id = :id', [empId]); - - console.log(result.rows[0]); - } catch (err) { - console.log('Ouch!', err); - } finally { - if (conn) { - // conn assignment worked, need to close - await conn.close(); - } - } -} - -getEmployee(101); -``` - -## PostgreSQL - -**Module**: [pg-promise](https://github.com/vitaly-t/pg-promise) - -### 安装 - - - -### 示例 - -```js title="index.js" -const pgp = require('pg-promise')(/* options */); -const db = pgp('postgres://username:password@host:port/database'); - -db.one('SELECT $1 AS value', 123) - .then((data) => { - console.log('DATA:', data.value); - }) - .catch((error) => { - console.log('ERROR:', error); +process.on('SIGTERM', () => { + server.close(async () => { + await pool.end(); + process.exit(0); }); -``` - -## Redis - -**Module**: [redis](https://github.com/mranney/node_redis) - -### 安装 - - - -### 示例 - -```cjs title="index.cjs" -const redis = require('redis'); -const client = redis.createClient(); - -client.on('error', (err) => { - console.log(`Error ${err}`); -}); - -client.set('string key', 'string val', redis.print); -client.hset('hash key', 'hashtest 1', 'some value', redis.print); -client.hset(['hash key', 'hashtest 2', 'some other value'], redis.print); - -client.hkeys('hash key', (err, replies) => { - console.log(`${replies.length} replies:`); - - replies.forEach((reply, i) => { - console.log(` ${i}: ${reply}`); - }); - - client.quit(); -}); -``` - -```mjs title="index.mjs" -import redis from 'redis'; - -const client = redis.createClient(); - -client.on('error', (err) => { - console.log(`Error ${err}`); -}); - -client.set('string key', 'string val', redis.print); -client.hset('hash key', 'hashtest 1', 'some value', redis.print); -client.hset(['hash key', 'hashtest 2', 'some other value'], redis.print); - -client.hkeys('hash key', (err, replies) => { - console.log(`${replies.length} replies:`); - - replies.forEach((reply, i) => { - console.log(` ${i}: ${reply}`); - }); - - client.quit(); }); ``` -## SQL Server - -**Module**: [tedious](https://github.com/tediousjs/tedious) - -### 安装 - - - -### 示例 - -```js title="index.js" -const Connection = require('tedious').Connection; -const Request = require('tedious').Request; - -const config = { - server: 'localhost', - authentication: { - type: 'default', - options: { - userName: 'your_username', // update me - password: 'your_password', // update me - }, - }, -}; - -const connection = new Connection(config); - -connection.on('connect', (err) => { - if (err) { - console.log(err); - } else { - executeStatement(); - } -}); - -function executeStatement() { - request = new Request("select 123, 'hello world'", (err, rowCount) => { - if (err) { - console.log(err); - } else { - console.log(`${rowCount} rows`); - } - connection.close(); - }); - - request.on('row', (columns) => { - columns.forEach((column) => { - if (column.value === null) { - console.log('NULL'); - } else { - console.log(column.value); - } - }); - }); - - connection.execSql(request); -} -``` - -## SQLite - -**Module**: [sqlite3](https://github.com/mapbox/node-sqlite3) - -### 安装 - - - -### 示例 - -```js title="index.js" -const sqlite3 = require('sqlite3').verbose(); -const db = new sqlite3.Database(':memory:'); - -db.serialize(() => { - db.run('CREATE TABLE lorem (info TEXT)'); - const stmt = db.prepare('INSERT INTO lorem VALUES (?)'); - - for (let i = 0; i < 10; i++) { - stmt.run(`Ipsum ${i}`); - } - - stmt.finalize(); - - db.each('SELECT rowid AS id, info FROM lorem', (err, row) => { - console.log(`${row.id}: ${row.info}`); - }); -}); - -db.close(); -``` - -## Elasticsearch - -**Module**: [elasticsearch](https://github.com/elastic/elasticsearch-js) - -### 安装 - - - -### 示例 - -```cjs title="index.cjs" -const elasticsearch = require('elasticsearch'); -const client = elasticsearch.Client({ - host: 'localhost:9200', -}); - -client - .search({ - index: 'books', - type: 'book', - body: { - query: { - multi_match: { - query: 'express js', - fields: ['title', 'description'], - }, - }, - }, - }) - .then( - (response) => { - const hits = response.hits.hits; - }, - (error) => { - console.trace(error.message); - } - ); -``` - -```mjs title="index.mjs" -import elasticsearch from 'elasticsearch'; - -const client = elasticsearch.Client({ - host: 'localhost:9200', -}); - -client - .search({ - index: 'books', - type: 'book', - body: { - query: { - multi_match: { - query: 'express js', - fields: ['title', 'description'], - }, - }, - }, - }) - .then( - (response) => { - const hits = response.hits.hits; - }, - (error) => { - console.trace(error.message); - } - ); -``` +If you already use an [AbortSignal](https://nodejs.org/api/globals.html#class-abortsignal) to coordinate cancellation across your app, you can pass it to `app.listen` and abort it instead of calling `server.close()` directly. diff --git a/src/content/pages/zh-cn/resources/middleware/body-parser.mdx b/src/content/pages/zh-cn/resources/middleware/body-parser.mdx index 5e53dce90b..29326a4cdd 100644 --- a/src/content/pages/zh-cn/resources/middleware/body-parser.mdx +++ b/src/content/pages/zh-cn/resources/middleware/body-parser.mdx @@ -8,7 +8,7 @@ import Alert from '@components/primitives/Alert/Alert.astro'; import PackageManagerCommand from '@components/patterns/PackageManagerCommand/PackageManagerCommand.astro'; @@ -73,6 +73,12 @@ const text = require('body-parser/text'); `bodyParser` 对象提供了多种工厂方法来创建中间件。所有中间件在请求头`Content-Type`与`type`选项匹配时,都会将解析后的请求体存入`req.body`属性。 +**Note** As of v2, `req.body` is left `undefined` when no body is parsed (for +example when the request has neither `Content-Length` nor `Transfer-Encoding`, +or when `Content-Type` does not match). In v1, `req.body` was always initialized +to `{}`. Code that assumes `req.body` is always an object should use optional +chaining (for example `req.body?.username`) or set a default. + 该模块抛出的各类错误在[错误章节](#errors)中有相关说明。 ### bodyParser.json([options]) @@ -107,7 +113,8 @@ Unicode encoding of the body. ##### strict -设为 `true` 时,仅接受数组与对象;设为 `false` 时,可接受所有 `JSON.parse` 能够解析的内容。默认值为 `true`。 +When set to `true`, it will only accept arrays and objects; when `false`, it will +accept anything `JSON.parse` accepts. 默认值为 `true`。 ##### type @@ -292,7 +299,7 @@ verified by the defined `verify` option. `status` 属性值为 `403`,`type` ### 输入内容超出解析深度限制 -当使用 `bodyParser.urlencoded` 并将 `extended` 配置为 `true` 时,若传入参数嵌套层级超过 `depth` 配置上限,就会触发该错误。 `status` 属性值为 `400`。建议检查 `depth` 配置项,评估是否需要调大该数值。当 `depth` 配置项设为默认值 32 时,不会抛出该错误。 +当使用 `bodyParser.urlencoded` 并将 `extended` 配置为 `true` 时,若传入参数嵌套层级超过 `depth` 配置上限,就会触发该错误。 `status` 属性值为 `400`。建议检查 `depth` 配置项,评估是否需要调大该数值。 With the default `depth` of `32`, an input with 33 or more levels of nesting gets this error. ## Examples diff --git a/src/content/pages/zh-cn/resources/middleware/multer.mdx b/src/content/pages/zh-cn/resources/middleware/multer.mdx index ad9365d875..4adea9dd9e 100644 --- a/src/content/pages/zh-cn/resources/middleware/multer.mdx +++ b/src/content/pages/zh-cn/resources/middleware/multer.mdx @@ -8,7 +8,7 @@ import Alert from '@components/primitives/Alert/Alert.astro'; import PackageManagerCommand from '@components/patterns/PackageManagerCommand/PackageManagerCommand.astro'; diff --git a/src/content/pages/zh-tw/guide/database-integration.mdx b/src/content/pages/zh-tw/guide/database-integration.mdx index 5c3aeab058..b80ed4d966 100644 --- a/src/content/pages/zh-tw/guide/database-integration.mdx +++ b/src/content/pages/zh-tw/guide/database-integration.mdx @@ -1,656 +1,196 @@ --- title: Database integration -description: Discover how to integrate various databases with Express.js applications, including setup examples for MongoDB, MySQL, PostgreSQL, and more. +description: Learn how to connect databases to Express.js applications, when to use an ORM, and best practices for connection pooling, security, and error handling. --- -import Alert from '@components/primitives/Alert/Alert.astro'; -import PackageManagerCommand from '@components/patterns/PackageManagerCommand/PackageManagerCommand.astro'; +To connect a database to an Express app simply load the desired Node.js driver or ORM in your app. The general pattern is shown below using SQLite as an example. -Adding the capability to connect databases to Express apps is just a matter of loading an appropriate Node.js driver for the database in your app. This document briefly explains how to add and use some of the most popular Node.js modules for database systems in your Express app: +There are also a few [best practices](#best-practices) to follow, regardless of the database you're using. -- [Cassandra](#cassandra) -- [Couchbase](#couchbase) -- [CouchDB](#couchdb) -- [LevelDB](#leveldb) -- [MySQL](#mysql) -- [MongoDB](#mongodb) -- [Neo4j](#neo4j) -- [Oracle](#oracle) -- [PostgreSQL](#postgresql) -- [Redis](#redis) -- [SQL Server](#sql-server) -- [SQLite](#sqlite) -- [Elasticsearch](#elasticsearch) - - - -These database drivers are among many that are available. For other options, search on the -[npm](https://www.npmjs.com/) site. - - - -## Cassandra - -**Module**: [cassandra-driver](https://github.com/datastax/nodejs-driver) - -### Installation +## SQLite - +Node.js includes a built-in SQLite client, [node:sqlite](https://nodejs.org/api/sqlite.html), available without a flag since Node.js 22.13.0. No installation is required. For older Node.js versions, [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) offers a similar synchronous API. ### Example -```cjs title="index.cjs" -const cassandra = require('cassandra-driver'); -const client = new cassandra.Client({ contactPoints: ['localhost'] }); - -client.execute('select key from system.local', (err, result) => { - if (err) throw err; - console.log(result.rows[0]); -}); -``` +This example creates a sample user that you can retrieve at `http://localhost:3000/users/1`. It uses a synchronous SQLite API for simplicity: long-running queries block the thread handling requests. For workloads with long-running queries, consider moving database work to a worker thread or using an asynchronous database driver. -```mjs title="index.mjs" -import cassandra from 'cassandra-driver'; - -const client = new cassandra.Client({ contactPoints: ['localhost'] }); - -client.execute('select key from system.local', (err, result) => { - if (err) throw err; - console.log(result.rows[0]); -}); -``` - -## Couchbase - -**Module**: [couchnode](https://github.com/couchbase/couchnode) - -### Installation +```cjs title="index.cjs" +const express = require('express'); +const { DatabaseSync } = require('node:sqlite'); - +const app = express(); +const db = new DatabaseSync('app.db'); -### Example +db.exec('CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT NOT NULL)'); +db.prepare('INSERT INTO users (id, name) VALUES (?, ?) ON CONFLICT(id) DO NOTHING').run(1, 'Alice'); +const findUser = db.prepare('SELECT id, name FROM users WHERE id = ?'); -```cjs title="index.cjs" -const couchbase = require('couchbase'); -const bucket = new couchbase.Cluster('http://localhost:8091').openBucket('bucketName'); - -// add a document to a bucket -bucket.insert('document-key', { name: 'Matt', shoeSize: 13 }, (err, result) => { - if (err) { - console.log(err); - } else { - console.log(result); +app.get('/users/:id', (req, res) => { + const user = findUser.get(req.params.id); + if (!user) { + return res.status(404).json({ error: 'User not found' }); } + res.json(user); }); -// get all documents with shoe size 13 -const n1ql = 'SELECT d.* FROM `bucketName` d WHERE shoeSize = $1'; -const query = N1qlQuery.fromString(n1ql); -bucket.query(query, [13], (err, result) => { - if (err) { - console.log(err); - } else { - console.log(result); - } -}); +app.listen(3000); ``` ```mjs title="index.mjs" -import couchbase from 'couchbase'; +import express from 'express'; +import { DatabaseSync } from 'node:sqlite'; -const bucket = new couchbase.Cluster('http://localhost:8091').openBucket('bucketName'); +const app = express(); +const db = new DatabaseSync('app.db'); -// add a document to a bucket -bucket.insert('document-key', { name: 'Matt', shoeSize: 13 }, (err, result) => { - if (err) { - console.log(err); - } else { - console.log(result); - } -}); +db.exec('CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT NOT NULL)'); +db.prepare('INSERT INTO users (id, name) VALUES (?, ?) ON CONFLICT(id) DO NOTHING').run(1, 'Alice'); +const findUser = db.prepare('SELECT id, name FROM users WHERE id = ?'); -// get all documents with shoe size 13 -const n1ql = 'SELECT d.* FROM `bucketName` d WHERE shoeSize = $1'; -const query = N1qlQuery.fromString(n1ql); -bucket.query(query, [13], (err, result) => { - if (err) { - console.log(err); - } else { - console.log(result); +app.get('/users/:id', (req, res) => { + const user = findUser.get(req.params.id); + if (!user) { + return res.status(404).json({ error: 'User not found' }); } + res.json(user); }); + +app.listen(3000); ``` -## CouchDB +```ts title="index.ts" +import express from 'express'; +import { DatabaseSync } from 'node:sqlite'; -**Module**: [nano](https://github.com/dscape/nano) +interface User { + id: number; + name: string; +} -### Installation +const app = express(); +const db = new DatabaseSync('app.db'); - +db.exec('CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT NOT NULL)'); +db.prepare('INSERT INTO users (id, name) VALUES (?, ?) ON CONFLICT(id) DO NOTHING').run(1, 'Alice'); +const findUser = db.prepare('SELECT id, name FROM users WHERE id = ?'); -### Example - -```js title="index.js" -const nano = require('nano')('http://localhost:5984'); -nano.db.create('books'); -const books = nano.db.use('books'); - -// Insert a book document in the books database -books.insert({ name: 'The Art of war' }, null, (err, body) => { - if (err) { - console.log(err); - } else { - console.log(body); +app.get('/users/:id', (req, res) => { + const user = findUser.get(req.params.id) as User | undefined; + if (!user) { + return res.status(404).json({ error: 'User not found' }); } + res.json(user); }); -// Get a list of all books -books.list((err, body) => { - if (err) { - console.log(err); - } else { - console.log(body.rows); - } -}); +app.listen(3000); ``` -## LevelDB +## Other databases -**Module**: [levelup](https://github.com/rvagg/node-levelup) +Every other database follows the same pattern: install the Node.js driver from npm, create a client or connection pool once when the app starts, and use it in your route handlers. Refer to the official documentation of each driver for connection options and API details: -### Installation +- PostgreSQL: [pg](https://node-postgres.com/) or [postgres](https://github.com/porsager/postgres) +- MySQL and MariaDB: [mysql2](https://sidorares.github.io/node-mysql2/docs) or [mariadb](https://mariadb.com/docs/connectors/mariadb-connector-nodejs) +- MongoDB: [mongodb](https://www.mongodb.com/docs/drivers/node/current/) +- Redis: [redis](https://redis.io/docs/latest/develop/clients/nodejs/) or [ioredis](https://github.com/redis/ioredis) +- SQL Server: [mssql](https://github.com/tediousjs/node-mssql) +- Oracle: [oracledb](https://www.npmjs.com/package/oracledb) +- Cassandra: [cassandra-driver](https://docs.datastax.com/en/developer/nodejs-driver/latest/) +- Elasticsearch: [@elastic/elasticsearch](https://www.elastic.co/docs/reference/elasticsearch/clients/javascript) - - -### Example +For any database not listed here, search for its driver on the [npm](https://www.npmjs.com/) site. -```cjs title="index.cjs" -const levelup = require('levelup'); -const db = levelup('./mydb'); +## ORMs and query builders -db.put('name', 'LevelUP', (err) => { - if (err) return console.log('Ooops!', err); +The example above uses a database driver directly, which is a good fit for small apps and for learning how things work. As an application grows, an ORM (object-relational mapper) or a query builder can take over concerns such as schema definitions, migrations, relations between tables, and input mapping, while reducing boilerplate. Many of them also provide type safety when used with TypeScript. - db.get('name', (err, value) => { - if (err) return console.log('Ooops!', err); +Popular options in the Node.js ecosystem include: - console.log(`name=${value}`); - }); -}); -``` +- [Prisma](https://www.prisma.io/): a type-safe ORM with a schema-first workflow and built-in migrations. Supports PostgreSQL, MySQL, SQLite, SQL Server, MongoDB, and CockroachDB. +- [Drizzle ORM](https://orm.drizzle.team/): a lightweight, TypeScript-first ORM with a SQL-like query API and a migrations toolkit. +- [Sequelize](https://sequelize.org/): a mature, promise-based ORM for PostgreSQL, MySQL, MariaDB, SQLite, and SQL Server. +- [TypeORM](https://typeorm.io/): an ORM built around decorators and entities, supporting many SQL databases and MongoDB. +- [Mongoose](https://mongoosejs.com/): an ODM (object document mapper) for MongoDB. +- [Knex.js](https://knexjs.org/): a flexible SQL query builder with migrations support, useful when you want control over your queries without writing raw strings. +- [Kysely](https://kysely.dev/): a type-safe SQL query builder for TypeScript. -```mjs title="index.mjs" -import levelup from 'levelup'; +Each ORM has its own API, supported databases, and approach to schemas, migrations, and queries. Features and setup requirements vary, so consult each project's documentation before choosing one. Query builders keep you close to SQL, while full ORMs abstract more of it away. Pick the level of abstraction that matches your team and project, and prefer tools that are actively maintained. -const db = levelup('./mydb'); +## Best practices -db.put('name', 'LevelUP', (err) => { - if (err) return console.log('Ooops!', err); +Adapt these practices to the database and library you choose. The snippets below illustrate separate parts of an application. The PostgreSQL snippets assume you have installed the [pg](https://node-postgres.com/) driver and created a shared pool: - db.get('name', (err, value) => { - if (err) return console.log('Ooops!', err); +```js +import { Pool } from 'pg'; - console.log(`name=${value}`); - }); -}); +const pool = new Pool({ connectionString: process.env.DATABASE_URL }); ``` -## MySQL - -**Module**: [mysql](https://github.com/felixge/node-mysql/) - -### Installation - - - -### Example - -```cjs title="index.cjs" -const mysql = require('mysql'); -const connection = mysql.createConnection({ - host: 'localhost', - user: 'dbuser', - password: 's3kreee7', - database: 'my_db', -}); +### Create connections once and reuse them -connection.connect(); +Opening a database connection is expensive. Many drivers provide a connection pool that creates connections as needed and reuses them across requests. Create the pool (or client) once when the app starts. Acquire connections from that shared pool as needed in your route handlers, for example for a transaction, and always release them in a `finally` block. For individual queries, `pool.query()` in `pg` handles acquiring and releasing a connection for you. -connection.query('SELECT 1 + 1 AS solution', (err, rows, fields) => { - if (err) throw err; +### Use parameterized queries - console.log('The solution is: ', rows[0].solution); -}); +Never build SQL queries by concatenating or interpolating user input into the query string. Doing so exposes your app to SQL injection. Use your driver's parameterized queries or prepared statements to pass user input separately from the query itself. -connection.end(); +```js +// Vulnerable: user input becomes part of the query +const user = await pool.query(`SELECT * FROM users WHERE id = ${req.params.id}`); ``` -```mjs title="index.mjs" -import mysql from 'mysql'; - -const connection = mysql.createConnection({ - host: 'localhost', - user: 'dbuser', - password: 's3kreee7', - database: 'my_db', -}); - -connection.connect(); - -connection.query('SELECT 1 + 1 AS solution', (err, rows, fields) => { - if (err) throw err; - - console.log('The solution is: ', rows[0].solution); -}); - -connection.end(); +```js +// Safe: user input is passed as a parameter +const user = await pool.query('SELECT * FROM users WHERE id = $1', [req.params.id]); ``` -## MongoDB - -**Module**: [mongodb](https://github.com/mongodb/node-mongodb-native) - -### Installation - - - -### Example (v2.\*) - -```js title="index.js" -const MongoClient = require('mongodb').MongoClient; +Also validate user input before it reaches your queries, both for security and for data integrity. For NoSQL databases, construct queries using the driver's API and validate the types, fields, and operators you accept. Do not pass user-supplied query objects directly to the database. -MongoClient.connect('mongodb://localhost:27017/animals', (err, db) => { - if (err) throw err; +### Keep credentials out of your code - db.collection('mammals') - .find() - .toArray((err, result) => { - if (err) throw err; +Never hardcode connection strings, usernames, or passwords in your source code, and never commit them to version control. Read them from environment variables or a secrets manager instead: - console.log(result); - }); -}); +```js +const pool = new Pool({ connectionString: process.env.DATABASE_URL }); ``` -### Example (v3.\*) - -```js title="index.js" -const MongoClient = require('mongodb').MongoClient; - -MongoClient.connect('mongodb://localhost:27017/animals', (err, client) => { - if (err) throw err; - - const db = client.db('animals'); - - db.collection('mammals') - .find() - .toArray((err, result) => { - if (err) throw err; +During development, you can keep these values in a `.env` file and load it with Node.js's built-in support for environment files, with no extra dependencies: - console.log(result); - }); -}); +```bash +node --env-file=.env index.js ``` -If you want an object model driver for MongoDB, look at [Mongoose](https://github.com/LearnBoost/mongoose). - -## Neo4j - -**Module**: [neo4j-driver](https://github.com/neo4j/neo4j-javascript-driver) +Make sure `.env` is listed in your `.gitignore` file so it never gets committed. -### Installation - - - -### Example +### Handle database errors -```cjs title="index.cjs" -const neo4j = require('neo4j-driver'); -const driver = neo4j.driver('neo4j://localhost:7687', neo4j.auth.basic('neo4j', 'letmein')); - -const session = driver.session(); - -session.readTransaction((tx) => { - return tx - .run('MATCH (n) RETURN count(n) AS count') - .then((res) => { - console.log(res.records[0].get('count')); - }) - .catch((error) => { - console.log(error); - }); -}); -``` - -```mjs title="index.mjs" -import neo4j from 'neo4j-driver'; +Database calls fail: connections drop, queries time out, constraints are violated. In Express 5, errors thrown in async route handlers are passed to your [error-handling middleware](/en/guide/error-handling) automatically, so define one and decide there what to expose to clients. Avoid leaking internal error details, such as query text or stack traces, in API responses. -const driver = neo4j.driver('neo4j://localhost:7687', neo4j.auth.basic('neo4j', 'letmein')); +### Cancel work when the client disconnects -const session = driver.session(); +Since Node.js 24.20.0, every request exposes [req.signal](https://nodejs.org/api/http.html#messagesignal), an `AbortSignal` that is aborted when the client disconnects. Pass it to any cancellation-aware API, such as a database driver that supports abort signals, so your app stops doing work nobody is waiting for: -session.readTransaction((tx) => { - return tx - .run('MATCH (n) RETURN count(n) AS count') - .then((res) => { - console.log(res.records[0].get('count')); - }) - .catch((error) => { - console.log(error); - }); +```js +app.get('/reports', async (req, res) => { + const report = await generateReport({ signal: req.signal }); + res.json(report); }); ``` -## Oracle +### Close connections on shutdown -**Module**: [oracledb](https://github.com/oracle/node-oracledb) +When your app receives a termination signal, stop accepting new requests and close your database connections so in-flight queries can finish cleanly: -### Installation +```js +const server = app.listen(3000); -NOTE: [See installation prerequisites](https://github.com/oracle/node-oracledb#-installation). - - - -### Example - -```cjs title="index.cjs" -const oracledb = require('oracledb'); -const config = { - user: '', - password: '', - connectString: 'localhost:1521/orcl', -}; - -async function getEmployee(empId) { - let conn; - - try { - conn = await oracledb.getConnection(config); - - const result = await conn.execute('select * from employees where employee_id = :id', [empId]); - - console.log(result.rows[0]); - } catch (err) { - console.log('Ouch!', err); - } finally { - if (conn) { - // conn assignment worked, need to close - await conn.close(); - } - } -} - -getEmployee(101); -``` - -```mjs title="index.mjs" -import oracledb from 'oracledb'; - -const config = { - user: '', - password: '', - connectString: 'localhost:1521/orcl', -}; - -async function getEmployee(empId) { - let conn; - - try { - conn = await oracledb.getConnection(config); - - const result = await conn.execute('select * from employees where employee_id = :id', [empId]); - - console.log(result.rows[0]); - } catch (err) { - console.log('Ouch!', err); - } finally { - if (conn) { - // conn assignment worked, need to close - await conn.close(); - } - } -} - -getEmployee(101); -``` - -## PostgreSQL - -**Module**: [pg-promise](https://github.com/vitaly-t/pg-promise) - -### Installation - - - -### Example - -```js title="index.js" -const pgp = require('pg-promise')(/* options */); -const db = pgp('postgres://username:password@host:port/database'); - -db.one('SELECT $1 AS value', 123) - .then((data) => { - console.log('DATA:', data.value); - }) - .catch((error) => { - console.log('ERROR:', error); +process.on('SIGTERM', () => { + server.close(async () => { + await pool.end(); + process.exit(0); }); -``` - -## Redis - -**Module**: [redis](https://github.com/mranney/node_redis) - -### Installation - - - -### Example - -```cjs title="index.cjs" -const redis = require('redis'); -const client = redis.createClient(); - -client.on('error', (err) => { - console.log(`Error ${err}`); -}); - -client.set('string key', 'string val', redis.print); -client.hset('hash key', 'hashtest 1', 'some value', redis.print); -client.hset(['hash key', 'hashtest 2', 'some other value'], redis.print); - -client.hkeys('hash key', (err, replies) => { - console.log(`${replies.length} replies:`); - - replies.forEach((reply, i) => { - console.log(` ${i}: ${reply}`); - }); - - client.quit(); -}); -``` - -```mjs title="index.mjs" -import redis from 'redis'; - -const client = redis.createClient(); - -client.on('error', (err) => { - console.log(`Error ${err}`); -}); - -client.set('string key', 'string val', redis.print); -client.hset('hash key', 'hashtest 1', 'some value', redis.print); -client.hset(['hash key', 'hashtest 2', 'some other value'], redis.print); - -client.hkeys('hash key', (err, replies) => { - console.log(`${replies.length} replies:`); - - replies.forEach((reply, i) => { - console.log(` ${i}: ${reply}`); - }); - - client.quit(); }); ``` -## SQL Server - -**Module**: [tedious](https://github.com/tediousjs/tedious) - -### Installation - - - -### Example - -```js title="index.js" -const Connection = require('tedious').Connection; -const Request = require('tedious').Request; - -const config = { - server: 'localhost', - authentication: { - type: 'default', - options: { - userName: 'your_username', // update me - password: 'your_password', // update me - }, - }, -}; - -const connection = new Connection(config); - -connection.on('connect', (err) => { - if (err) { - console.log(err); - } else { - executeStatement(); - } -}); - -function executeStatement() { - request = new Request("select 123, 'hello world'", (err, rowCount) => { - if (err) { - console.log(err); - } else { - console.log(`${rowCount} rows`); - } - connection.close(); - }); - - request.on('row', (columns) => { - columns.forEach((column) => { - if (column.value === null) { - console.log('NULL'); - } else { - console.log(column.value); - } - }); - }); - - connection.execSql(request); -} -``` - -## SQLite - -**Module**: [sqlite3](https://github.com/mapbox/node-sqlite3) - -### Installation - - - -### Example - -```js title="index.js" -const sqlite3 = require('sqlite3').verbose(); -const db = new sqlite3.Database(':memory:'); - -db.serialize(() => { - db.run('CREATE TABLE lorem (info TEXT)'); - const stmt = db.prepare('INSERT INTO lorem VALUES (?)'); - - for (let i = 0; i < 10; i++) { - stmt.run(`Ipsum ${i}`); - } - - stmt.finalize(); - - db.each('SELECT rowid AS id, info FROM lorem', (err, row) => { - console.log(`${row.id}: ${row.info}`); - }); -}); - -db.close(); -``` - -## Elasticsearch - -**Module**: [elasticsearch](https://github.com/elastic/elasticsearch-js) - -### Installation - - - -### Example - -```cjs title="index.cjs" -const elasticsearch = require('elasticsearch'); -const client = elasticsearch.Client({ - host: 'localhost:9200', -}); - -client - .search({ - index: 'books', - type: 'book', - body: { - query: { - multi_match: { - query: 'express js', - fields: ['title', 'description'], - }, - }, - }, - }) - .then( - (response) => { - const hits = response.hits.hits; - }, - (error) => { - console.trace(error.message); - } - ); -``` - -```mjs title="index.mjs" -import elasticsearch from 'elasticsearch'; - -const client = elasticsearch.Client({ - host: 'localhost:9200', -}); - -client - .search({ - index: 'books', - type: 'book', - body: { - query: { - multi_match: { - query: 'express js', - fields: ['title', 'description'], - }, - }, - }, - }) - .then( - (response) => { - const hits = response.hits.hits; - }, - (error) => { - console.trace(error.message); - } - ); -``` +If you already use an [AbortSignal](https://nodejs.org/api/globals.html#class-abortsignal) to coordinate cancellation across your app, you can pass it to `app.listen` and abort it instead of calling `server.close()` directly. diff --git a/src/content/pages/zh-tw/resources/middleware/body-parser.mdx b/src/content/pages/zh-tw/resources/middleware/body-parser.mdx index 3547c52f26..0b9148b175 100644 --- a/src/content/pages/zh-tw/resources/middleware/body-parser.mdx +++ b/src/content/pages/zh-tw/resources/middleware/body-parser.mdx @@ -8,7 +8,7 @@ import Alert from '@components/primitives/Alert/Alert.astro'; import PackageManagerCommand from '@components/patterns/PackageManagerCommand/PackageManagerCommand.astro'; @@ -82,6 +82,12 @@ The `bodyParser` object exposes various factories to create middlewares. All middlewares will populate the `req.body` property with the parsed body when the `Content-Type` request header matches the `type` option. +**Note** As of v2, `req.body` is left `undefined` when no body is parsed (for +example when the request has neither `Content-Length` nor `Transfer-Encoding`, +or when `Content-Type` does not match). In v1, `req.body` was always initialized +to `{}`. Code that assumes `req.body` is always an object should use optional +chaining (for example `req.body?.username`) or set a default. + The various errors returned by this module are described in the [errors section](#errors). @@ -126,7 +132,7 @@ argument. You can find more information on this argument ##### strict -When set to `true`, will only accept arrays and objects; when `false` will +When set to `true`, it will only accept arrays and objects; when `false`, it will accept anything `JSON.parse` accepts. Defaults to `true`. ##### type @@ -423,7 +429,7 @@ property is set to the encoding that is unsupported. ### The input exceeded the depth -This error occurs when using `bodyParser.urlencoded` with the `extended` property set to `true` and the input exceeds the configured `depth` option. The `status` property is set to `400`. It is recommended to review the `depth` option and evaluate if it requires a higher value. When the `depth` option is set to `32` (default value), the error will not be thrown. +This error occurs when using `bodyParser.urlencoded` with the `extended` property set to `true` and the input exceeds the configured `depth` option. The `status` property is set to `400`. It is recommended to review the `depth` option and evaluate if it requires a higher value. With the default `depth` of `32`, an input with 33 or more levels of nesting gets this error. ## Examples diff --git a/src/content/pages/zh-tw/resources/middleware/multer.mdx b/src/content/pages/zh-tw/resources/middleware/multer.mdx index d32246802f..7e8a4718ab 100644 --- a/src/content/pages/zh-tw/resources/middleware/multer.mdx +++ b/src/content/pages/zh-tw/resources/middleware/multer.mdx @@ -8,7 +8,7 @@ import Alert from '@components/primitives/Alert/Alert.astro'; import PackageManagerCommand from '@components/patterns/PackageManagerCommand/PackageManagerCommand.astro';