From 855d89e4aae5564660dff4f9a4b56acec218e27e Mon Sep 17 00:00:00 2001 From: Marshall Thompson Date: Fri, 4 Nov 2022 21:45:43 -0600 Subject: [PATCH 01/25] docs for initial cli guide --- docs/guides/cli/config.md | 9 + docs/guides/cli/generate-app.md | 341 +++++++++++++++++++++++ docs/guides/cli/index.md | 82 ++++++ docs/guides/cli/schemas-and-resolvers.md | 62 +++++ docs/guides/cli/service-classes.md | 33 +++ docs/guides/cli/service-hooks.md | 53 ++++ docs/guides/cli/validators.md | 14 + 7 files changed, 594 insertions(+) create mode 100644 docs/guides/cli/config.md create mode 100644 docs/guides/cli/generate-app.md create mode 100644 docs/guides/cli/index.md create mode 100644 docs/guides/cli/schemas-and-resolvers.md create mode 100644 docs/guides/cli/service-classes.md create mode 100644 docs/guides/cli/service-hooks.md create mode 100644 docs/guides/cli/validators.md diff --git a/docs/guides/cli/config.md b/docs/guides/cli/config.md new file mode 100644 index 0000000000..95e73284f2 --- /dev/null +++ b/docs/guides/cli/config.md @@ -0,0 +1,9 @@ +--- +outline: deep +--- + +# Generated Configuration + +## The Config Package + +## Config Schemas diff --git a/docs/guides/cli/generate-app.md b/docs/guides/cli/generate-app.md new file mode 100644 index 0000000000..95fe30137b --- /dev/null +++ b/docs/guides/cli/generate-app.md @@ -0,0 +1,341 @@ +--- +outline: deep +--- + +# Generate an App + +You can generate a new app with `feathers generate app`. Before running the generator, create a directory for your project. Then open that project in a terminal and run the command: + +```bash +feathers generate app +``` + +## Generator Questions + +The app generator will ask you a series of questions. The following sections provide an overview of those questions. + +### TypeScript or JavaScript + +```bash +? Do you want to use JavaScript or TypeScript? (Use arrow keys) +❯ TypeScript + JavaScript +``` + +You can select a TypeScript project or a JavaScript project. The apps produced by each option are exactly equivalent in functionality. When you select "JavaScript" we generate a TypeScript app, compile it to JavaScript, then write the `.js` files to disk. The benefits of this approach are that you + +### App Name + +```bash +? What is the name of your application? (my-app) +``` + +The default name (in parentheses) will be the name of the folder that you created. Pay attention that it's the correct folder and that you don't accidentally generate in the parent folder. The name you select will become the `name` property in the `package.json`. + +### App Description + +```bash +? Write a short description +``` + +The text you specify here will become the `description` in the `package.json`. + +### HTTP Framework + +```bash +? Which HTTP framework do you want to use? (Use arrow keys) +❯ KoaJS (recommended) + Express +``` + +The generator allows you to build on top of either of the big names in Node HTTP Frameworks: KoaJS or Express. For most Feathers applications, you only ever lightly touch the underlying framework for tasks like setting up CORS. + +If you don't absolutely need some Express API or middleware package, we recommend using the more-modern KoaJS framework. We've selected it as the new default in Feathers v5. + +### Transport Layer APIs + +```bash +? What APIs do you want to offer? (Press to select, to toggle all, to invert selection, and to proceed) +❯◉ HTTP (REST) + ◉ Real-time +``` + +Feathers is unique in that it can use the same API for either HTTP or Real-time requests. You can even use both at the same time. When you use Feathers Hooks for authorization and access control, you secure requests on both transports at the same time! + +If you select `HTTP (REST)`, then the REST adapter for your chosen HTTP framework will be installed. + +If you select `Real-time`, the `@feathersjs/socketio` adapter will be installed. + +### Package Manager + +```bash +? Which package manager are you using? (Use arrow keys) +❯ npm + Yarn + pnpm +``` + +You answer here will determine which package manager is used to install modules. If you haven't installed Yarn or pnpm, select `npm`. + +### Schema Format + +```bash +? What is your preferred schema (model) definition format? (Use arrow keys) +❯ TypeBox (recommended) + JSON schema +``` + +The new Feathers v5 Dove has built-in support for TypeBox and JSON Schema schema definition formats. Both options are type friendly. Just define your schema and your get automatic TypeScript types for free. TypeBox is fully JSON schema compatible but in a format that is more concise and easier to read. + +### Database + +```bash +? Which database are you connecting to? Other databases can be added at any time (Use arrow keys) +❯ SQLite + MongoDB + PostgreSQL + MySQL/MariaDB + Microsoft SQL +``` + +Feathers will setup a database connection to whichever database you select in this step. The Feathers v5 Dove generator supports the most popular databases using two database adapter that have been brought into core: + +- If you select `MongoDB`, the `@feathersjs/mongodb` package will be installed and configured. +- If you select `SQLite`, `PostgreSQL`, `MySQL/MariaDB`, or `Microsoft SQL` the `@feathersjs/knex` adapter will be installed. + +#### Why are there fewer options? + +Feathers v5 Dove is still 100% compatible with database adapters for previous versions, so even though the generator doesn't directly set them up for you, you can manually add them using the instructions in each project. You can find additional Feathers-supported databases and other plugins by searching the [feathers-plugin tag on npm](https://www.npmjs.com/search?q=keywords:feathers-plugin). + +If Feathers still supports the other database adapters, why are they not listed? Since we now have built-in support for Feathers Schemas, we chose to support the most popular databases by bringing low-level adapters into Feathers core. By "low-level" adapters, we mean the ones that run closest to the database, itself, without their own ORM or schema layer. For example, since KnexJS is a query builder for the popular SQL databases, we chose to bring it into core. ObjectionJS and Sequelize adapters can still be setup manually. Similarly, we brought the MongoDB adapter into core and no longer generate feathers-mongoose services, automatically. + +Our ultimate plan is to use the new generator (which we built from the ground up, ourselves) to bring back pluggable support for custom generators. In the future, each database adapter's repo will also host its generator code, along with custom generators for your own projects. + +One more thing worth noting: we have also brought `feathers-memory` into the core and renamed it to `@feathersjs/memory`. It's not currently available in the generator. + +Now, back to the generator. Once you've selected a database adapter you can move on to the next step. + +### Connection String + +```bash +? Enter your database connection string (mongodb://localhost:27017/my-app) +``` + +Generally, you'll want to enter the connection string for the development database and not the production database. Production database connection strings most likely are safest in an environment variable. A development database usually doesn't require the same level of security. Depending on which database you selected, a default connection string will be presented. Press `enter` to accept it, or enter your own connection string. + +### Authentication Method + +```bash +? Which authentication methods do you want to use? Other methods and providers can be added at any time. (Press to select, to toggle all, +to invert selection, and to proceed) +❯◉ Email + Password + ◯ Google + ◯ Facebook + ◯ Twitter + ◯ GitHub + ◯ Auth0 +``` + +Depending on which option you choose, Feathers will install it alongside the JWT authentication strategy package. After authentication with the chosen method, Feathers gives you a JWT which can be used to authenticate with the API. + +Choosing `Email + Password` will install the "@feathersjs/authentication-local" package to handle password auth. + +Choosing any of the other options will install the `@feathersjs/authentication-oauth` package to handle logging in with the selected login provider. + +### Install dependencies + +Once you've selected the final question, the generator will create the necessary files and then install packages. The output will look something like the below, but will vary a bit if you selected a package manager other than `npm`. + +```bash + Wrote file test/app.test.ts + Wrote file src/app.ts + Wrote file src/channels.ts + Wrote file src/client.ts + Wrote file config/default.json + Wrote file config/test.json + Wrote file config/custom-environment-variables.json + Wrote file src/declarations.ts + Wrote file public/index.html + Wrote file src/index.ts + Wrote file src/logger.ts + Wrote file package.json + Wrote file .prettierrc + Wrote file readme.md + Wrote file src/schemas/configuration.ts + Wrote file src/schemas/validators.ts + Wrote file src/services/index.ts + Wrote file tsconfig.json + Wrote file src/mongodb.ts + Updated src/app.ts + Running npm install --save + +added 197 packages, and audited 198 packages in 19s + +24 packages are looking for funding + run `npm fund` for details + +found 0 vulnerabilities + Running npm install --save-dev + +added 168 packages, and audited 366 packages in 20s + +57 packages are looking for funding + run `npm fund` for details + +found 0 vulnerabilities +``` + + + +## The generated files + +Let's have a brief look at the files that have been generated: + + + + + +
+ +* `config/` contains the configuration files for the app + * `default.json` contains the basic application configuration + * `production.json` files override `default.json` when in production mode by setting `NODE_ENV=production`. For details, see the [configuration API documentation](../../api/configuration.md) +* `node_modules/` our installed dependencies which are also added in the `package.json` +* `public/` contains static files to be served. A sample favicon and `index.html` (which will show up when going directly to the server URL) are already included. +* `src/` contains the Feathers server code. + * `hooks/` contains our custom [hooks](../basics/hooks.md) + * `services/` contains our [services](../basics/services.md) + * `users/` is a service that has been generated automatically to allow registering and authenticating users + * `users.class.ts` is the service class + * `users.hooks.ts` initializes Feathers hooks for this service + * `users.service.ts` registers this service on our Feathers application + * `middleware/` contains any [Express middleware](http://expressjs.com/en/guide/writing-middleware.html) + * `models/` contains database model files + * `users.model.ts` sets up our user collection for NeDB + * `app.ts` configures our Feathers application like we did in the [getting started chapter](../basics/starting.md) + * `app.hooks.ts` registers hooks that apply to every service + * `authentication.ts` sets up Feathers authentication system + * `channels.ts` sets up Feathers [event channels](../../api/channels.md) + * `declarations.ts` contains TypeScript declarations for our app + * `index.ts` loads and starts the application +* `test/` contains test files for the app, hooks and services + * `services/` has our service tests + * `users.test.ts` contains some basic tests for the `users` service + * `app.test.ts` tests that the index page appears, as well as 404 errors for HTML pages and JSON + * `authentication.test.ts` includes some tests that basic authentication works +* `.editorconfig` is an [EditorConfig](http://editorconfig.org/) setting to help developers define and maintain consistent coding styles among different editors and IDEs. +* `.gitignore` specifies [intentionally untracked files](https://git-scm.com/docs/gitignore) which [git](https://git-scm.com/), [GitHub](https://github.com/) and other similar projects ignore. +* `tsconfig.json` the TypeScript [compiler configuration](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html) +* `package.json` contains [information](https://docs.npmjs.com/files/package.json) about our NodeJS project like its name or dependencies. +* `README.md` has installation and usage instructions + + + + + +* `config/` contains the configuration files for the app + * `default.json` contains the basic application configuration + * `production.json` files override `default.json` when in production mode by setting `NODE_ENV=production`. For details, see the [configuration API documentation](../../api/configuration.md) +* `node_modules/` our installed dependencies which are also added in the `package.json` +* `public/` contains static files to be served. A sample favicon and `index.html` (which will show up when going directly to the server URL) are already included. +* `src/` contains the Feathers server code. + * `hooks/` contains our custom [hooks](../basics/hooks.md) + * `services/` contains our [services](../basics/services.md) + * `users/` is a service that has been generated automatically to allow registering and authenticating users + * `users.class.js` is the service class + * `users.hooks.js` initializes Feathers hooks for this service + * `users.service.js` registers this service on our Feathers application + * `middleware/` contains any [Express middleware](http://expressjs.com/en/guide/writing-middleware.html) + * `models/` contains database model files + * `users.model.js` sets up our user collection for NeDB + * `app.js` configures our Feathers application like we did in the [getting started chapter](../basics/starting.md) + * `app.hooks.js` registers hooks that apply to every service + * `authentication.js` sets up Feathers authentication system + * `channels.js` sets up Feathers [event channels](../../api/channels.md) + * `index.js` loads and starts the application +* `test/` contains test files for the app, hooks and services + * `services/` has our service tests + * `users.test.js` contains some basic tests for the `users` service + * `app.test.js` tests that the index page appears, as well as 404 errors for HTML pages and JSON + * `authentication.test.js` includes some tests that basic authentication works +* `.editorconfig` is an [EditorConfig](http://editorconfig.org/) setting to help developers define and maintain consistent coding styles among different editors and IDEs. +* `.eslintrc.json` contains defaults for linting your code with [ESLint](http://eslint.org/docs/user-guide/getting-started). +* `.gitignore` specifies [intentionally untracked files](https://git-scm.com/docs/gitignore) which [git](https://git-scm.com/), [GitHub](https://github.com/) and other similar projects ignore. +* `package.json` contains [information](https://docs.npmjs.com/files/package.json) about our NodeJS project like its name or dependencies. +* `README.md` has installation and usage instructions + + + + + +## Configure functions + +The most important pattern used in the generated application to split things up into individual files are _configure functions_ which are functions that are exported from a file and take the Feathers [app object](../../api/application.md) and then use it to e.g. register services. Those functions are then passed to [app.configure](../../api/application.md#configurecallback). + +For example, have a look at the following files: + + + + + +
+ +`src/services/index.ts` looks like this: + +```ts +import { Application } from '../declarations'; +import users from './users/users.service'; +// Don't remove this comment. It's needed to format import lines nicely. + +export default function (app: Application) { + app.configure(users); +} +``` + +It uses another configure function exported from `src/services/users/users.service.ts`. The export from `src/services/index.js` is in turn used in `src/app.ts` as: + +```ts +// ... +import services from './services'; + +// ... +app.configure(authentication); +// Set up our services (see `services/index.js`) +app.configure(services); +// ... +``` + + + + + +`src/services/index.js` looks like this: + +```js +const users = require('./users/users.service.js'); +// eslint-disable-next-line no-unused-vars +module.exports = function (app) { + app.configure(users); +}; +``` + +It uses another configure function exported from `src/services/users/users.service.js`. The export from `src/services/index.js` is in turn used in `src/app.js` as: + +```js +// ... +const services = require('./services'); + +// ... +app.configure(authentication); +// Set up our services (see `services/index.js`) +app.configure(services); +// ... +``` + + + + + +This is how the generator splits things up into separate files and any documentation example that uses the `app` object can be used in a configure function. You can create your own files that export a configure function and `require`/`import` and `app.configure` them in `app.js`. + +> __Note:__ Keep in mind that the order in which configure functions are called might matter, e.g. if it is using a service, that service has to be registered first. diff --git a/docs/guides/cli/index.md b/docs/guides/cli/index.md new file mode 100644 index 0000000000..a497864886 --- /dev/null +++ b/docs/guides/cli/index.md @@ -0,0 +1,82 @@ +--- +outline: deep +--- + +# The Feathers CLI + +The Feathers generator allows you to quickly scaffold a Feathers app with the latest standardized file structure. + +## Install the CLI + +You can install the `@feathersjs/cli@pre` package as a global node module or run it directly with `npx`. We recommend installing the package locally unless you are in an enviroment that specifically prevents global modules. + +### Global Install (Preferred) + +Install the cli globally by running the following command: + +```bash +npm i -g @feathersjs/cli@pre +``` + +Now you will be able to run the generator by using the `feathers` command. + +### Run Directly With npx + +The `npx` command that comes bundled with `npm` allows you to run the Feathers CLI directly, avoiding the need to install a global module. + +```bash +npx @feathersjs/cli@pre generate app +``` + + + +## CLI Commands + +Once you've installed the CLI, you should be able to run the `generate` command with no arguments: + +```bash +feathers generate +``` + +You'll see the following output: + +```bash +Usage: feathers generate|g [options] [command] + +Run a generator. Currently available: + app: Generate a new application + service: Generate a new service + hook: Generate a hook + connection: Add a new database connection + authentication: Add authentication to the application + +Options: + -h, --help display help for command + +Commands: + app [options] Generate a new application + service [options] Generate a new service + hook [options] Generate a hook + connection Add a new database connection + authentication Add authentication to the application + help [command] display help for command +``` + +### Generate an App + +Learn about the app generator on the [Generate an App](./generate-app.md) page. + +### View the Help Output + +You can see the generator's help output by running a command followed by `-h`, like `feathers generate app -h`. Here's what it looks like: + +```bash +Usage: feathers generate app [options] + +Generate a new application + +Options: + --name The name of the application + -h, --help display help for command +``` + diff --git a/docs/guides/cli/schemas-and-resolvers.md b/docs/guides/cli/schemas-and-resolvers.md new file mode 100644 index 0000000000..f4d9227b4c --- /dev/null +++ b/docs/guides/cli/schemas-and-resolvers.md @@ -0,0 +1,62 @@ +--- +outline: deep +--- + +# Service Schemas and Resolvers + +Give a tour of the generated schemas and resolvers for a service. + +## Main Schemas and Resolvers + +```ts +// Main data model schema https://dove.feathersjs.com/guides/cli/schemas-and-resolvers.html#main-schemas-and-resolvers +export const messagesSchema = Type.Object( + { + _id: objectId, + text: Type.String(), + }, + { $id: 'Messages', additionalProperties: false }, +) +export type Messages = Static +export const messagesResolver = resolve({ + properties: {}, +}) +``` + +## External Resolvers + +```ts +// External resolvers https://dove.feathersjs.com/guides/cli/schemas-and-resolvers.html#external-resolvers +export const messagesExternalResolver = resolve({ + properties: {}, +}) +``` + +## Data Schema and Resolvers + +```ts +// Schema for creating new entries https://dove.feathersjs.com/guides/cli/schemas-and-resolvers.html#data-schema-and-resolvers +export const messagesDataSchema = Type.Pick(messagesSchema, ['string'], { + $id: 'MessagesData', + additionalProperties: false, +}) +export type MessagesData = Static +export const messagesDataValidator = getDataValidator(messagesDataSchema, dataValidator) +export const messagesDataResolver = resolve({ + properties: {}, +}) +``` + +## Query Schema and Resolvers + +```ts +// Schema for allowed query properties https://dove.feathersjs.com/guides/cli/schemas-and-resolvers.html#query-schema-and-resolvers +export const messagesQueryProperties = Type.Pick(messagesSchema, ['_id', 'name'], { additionalProperties: false }) +export const messagesQuerySchema = querySyntax(messagesQueryProperties) +export type MessagesQuery = Static +export const messagesQueryValidator = getValidator(messagesQuerySchema, queryValidator) +export const messagesQueryResolver = resolve({ + properties: {}, +}) + +``` \ No newline at end of file diff --git a/docs/guides/cli/service-classes.md b/docs/guides/cli/service-classes.md new file mode 100644 index 0000000000..99863f8f5f --- /dev/null +++ b/docs/guides/cli/service-classes.md @@ -0,0 +1,33 @@ +--- +outline: deep +--- + +# Service Class + +Link here from the class file in the generated app: + +Todo: Implement ServiceAdapter picker for the following + +## Knex + +## MongoDB + +```ts +import { MongoDBService } from '@feathersjs/mongodb' +import type { MongoDBAdapterParams } from '@feathersjs/mongodb' + +import type { Application } from '../../declarations' +import type { Message, MessageData, MessageQuery } from './messages.schema' + +export interface MessageParams extends MongoDBAdapterParams {} + +// Message class for MongoDB https://dove.feathersjs.com/cli/service-class +export class MessageService extends MongoDBService {} + +export const getOptions = (app: Application) => { + return { + paginate: app.get('paginate'), + Model: app.get('mongodbClient').then((db) => db.collection('message')), + } +} +``` \ No newline at end of file diff --git a/docs/guides/cli/service-hooks.md b/docs/guides/cli/service-hooks.md new file mode 100644 index 0000000000..aed9b1e575 --- /dev/null +++ b/docs/guides/cli/service-hooks.md @@ -0,0 +1,53 @@ +--- +outline: deep +--- + +# Generate a Service + +## Hooks Overview + +Where to link for the comment above the service hooks?: + +```ts +import { authenticate } from '@feathersjs/authentication' + +import type { Application } from '../../../../declarations' +import { TestingService, getOptions } from './test.class' + +export * from './test.class' + +// Messages service and hooks https://dove.feathersjs.com/cli/service-overview.html +export const testing = (app: Application) => { + app.use('messages', new TestingService(getOptions(app)), { + methods: ['find', 'get', 'create', 'update', 'patch', 'remove'], + events: [], + }) + app.service('messages').hooks({ + around: { + all: [authenticate('jwt')], + }, + before: { + all: [], + }, + after: { + all: [], + }, + error: { + all: [], + }, + }) +} +``` + +## Service TypeScript Declarations + +We already have an explanation of this in the troubleshooting guide, so we can link to there. + +```ts +// Update Service Declarations https://dove.feathersjs.com/cli/service-overview.html#service-types +declare module '../../../../declarations' { + interface ServiceTypes { + 'messages': TestingService + } +} +``` diff --git a/docs/guides/cli/validators.md b/docs/guides/cli/validators.md new file mode 100644 index 0000000000..c4ae36a751 --- /dev/null +++ b/docs/guides/cli/validators.md @@ -0,0 +1,14 @@ +--- +outline: deep +--- + +# Validators + +## AJV and TypeBox + +General data about AJV and TypeBox. + + +## MongoDB-Specific Stuff + +TODO: hook up the ServiceAdapter picker for this section \ No newline at end of file From 19d3fd3d7140eca7b0b93f6163b6257c564b9813 Mon Sep 17 00:00:00 2001 From: Marshall Thompson Date: Fri, 4 Nov 2022 21:51:02 -0600 Subject: [PATCH 02/25] Update sidebar --- docs/.vitepress/config.sidebar.ts | 35 +++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/docs/.vitepress/config.sidebar.ts b/docs/.vitepress/config.sidebar.ts index 7d4ae9e694..b7f72e6a96 100644 --- a/docs/.vitepress/config.sidebar.ts +++ b/docs/.vitepress/config.sidebar.ts @@ -76,6 +76,41 @@ export default { // } ] }, + { + text: 'CLI', + collapsible: true, + collapsed: true, + items: [ + { + text: 'Overview', + link: '/guides/cli/index.md' + }, + { + text: 'Generate App', + link: '/guides/cli/generate-app.md' + }, + { + text: 'Configuration', + link: '/guides/cli/config.md' + }, + { + text: 'Validators', + link: '/guides/cli/validators.md' + }, + { + text: 'Service Classes', + link: '/guides/cli/service-classes.md' + }, + { + text: 'Service Hooks', + link: '/guides/cli/service-hooks.md' + }, + { + text: 'Schemas and Resolvers', + link: '/guides/cli/schemas-and-resolvers.md' + } + ] + }, { text: 'Migrating', // collapsible: true, From 2ddb9680ce1e4b61ea2c49ebd3cf835b359817e0 Mon Sep 17 00:00:00 2001 From: daffl Date: Thu, 10 Nov 2022 17:05:40 -0800 Subject: [PATCH 03/25] Further work on CLI guides --- docs/.vitepress/config.sidebar.ts | 32 +++++-- docs/api/schema/validators.md | 2 +- docs/guides/cli/application.md | 75 ++++++++++++++++ docs/guides/cli/authentication.md | 1 + docs/guides/cli/config.md | 59 +++++++++++- docs/guides/cli/databases.md | 63 +++++++++++++ docs/guides/cli/hooks.md | 5 ++ docs/guides/cli/logging.md | 1 + .../{schemas-and-resolvers.md => schemas.md} | 0 docs/guides/cli/service-classes.md | 33 ------- .../cli/{service-hooks.md => services.md} | 44 +++++++-- docs/guides/cli/typescript.md | 86 ++++++++++++++++++ docs/guides/cli/validators.md | 36 +++++++- .../cli/src/app/templates/app.test.tpl.ts | 5 +- packages/cli/src/app/templates/app.tpl.ts | 6 +- packages/cli/src/app/templates/config.tpl.ts | 53 +++++++++++ .../cli/src/app/templates/declarations.tpl.ts | 3 +- packages/cli/src/app/templates/schemas.tpl.ts | 89 ------------------- .../cli/src/app/templates/services.tpl.ts | 4 +- .../cli/src/app/templates/validators.tpl.ts | 39 ++++++++ .../cli/src/connection/templates/knex.tpl.ts | 8 +- .../src/connection/templates/mongodb.tpl.ts | 4 +- 22 files changed, 496 insertions(+), 152 deletions(-) create mode 100644 docs/guides/cli/application.md create mode 100644 docs/guides/cli/authentication.md create mode 100644 docs/guides/cli/databases.md create mode 100644 docs/guides/cli/hooks.md create mode 100644 docs/guides/cli/logging.md rename docs/guides/cli/{schemas-and-resolvers.md => schemas.md} (100%) delete mode 100644 docs/guides/cli/service-classes.md rename docs/guides/cli/{service-hooks.md => services.md} (51%) create mode 100644 docs/guides/cli/typescript.md delete mode 100644 packages/cli/src/app/templates/schemas.tpl.ts create mode 100644 packages/cli/src/app/templates/validators.tpl.ts diff --git a/docs/.vitepress/config.sidebar.ts b/docs/.vitepress/config.sidebar.ts index b7f72e6a96..71472b42ff 100644 --- a/docs/.vitepress/config.sidebar.ts +++ b/docs/.vitepress/config.sidebar.ts @@ -89,25 +89,45 @@ export default { text: 'Generate App', link: '/guides/cli/generate-app.md' }, + { + text: 'Application', + link: '/guides/cli/application.md' + }, { text: 'Configuration', link: '/guides/cli/config.md' }, + { + text: 'TypeScript', + link: '/guides/cli/typescript.md' + }, { text: 'Validators', link: '/guides/cli/validators.md' }, { - text: 'Service Classes', - link: '/guides/cli/service-classes.md' + text: 'Logging', + link: '/guides/cli/logging.md' + }, + { + text: 'Databases', + link: '/guides/cli/databases.md' }, { - text: 'Service Hooks', - link: '/guides/cli/service-hooks.md' + text: 'Authentication', + link: '/guides/cli/authentication.md' }, { - text: 'Schemas and Resolvers', - link: '/guides/cli/schemas-and-resolvers.md' + text: 'Services', + link: '/guides/cli/services.md' + }, + { + text: 'Schemas', + link: '/guides/cli/schemas.md' + }, + { + text: 'Hooks', + link: '/guides/cli/hooks.md' } ] }, diff --git a/docs/api/schema/validators.md b/docs/api/schema/validators.md index e799d70788..fbfdabdede 100644 --- a/docs/api/schema/validators.md +++ b/docs/api/schema/validators.md @@ -14,7 +14,7 @@ Ajv and most other validation libraries are only used for ensuring data is valid ## Usage -The following is the standard `validators.ts` file that sets up a validator for data and querys (for which string types will be coerced automatically). It also sets up a collection of additional formats using [ajv-formats](https://ajv.js.org/packages/ajv-formats.html). The validators in this file can be customized according to the [Ajv documentation](https://ajv.js.org/) and [its plugins](https://ajv.js.org/packages/). You can find the available Ajv options in the [Ajs class API docs](https://ajv.js.org/options.html). +The following is the standard `validators.ts` file that sets up a validator for data and querys (for which string types will be coerced automatically). It also sets up a collection of additional formats using [ajv-formats](https://ajv.js.org/packages/ajv-formats.html). The validators in this file can be customized according to the [Ajv documentation](https://ajv.js.org/) and [its plugins](https://ajv.js.org/packages/). You can find the available Ajv options in the [Ajv class API docs](https://ajv.js.org/options.html). ```ts import { Ajv, addFormats } from '@feathersjs/schema' diff --git a/docs/guides/cli/application.md b/docs/guides/cli/application.md new file mode 100644 index 0000000000..deee74e485 --- /dev/null +++ b/docs/guides/cli/application.md @@ -0,0 +1,75 @@ +# Application + +The `src/app.ts` file is the main file where the [Feathers application](../../api/application.md) gets initialized and wired up with a Feathers transport. + +## Transports + +The available transports are [Koa](../../api/koa.md) or [Express](../../api/express.md) for HTTP and [Socket.io](../../api/socketio.md) for real-time functionality. For both, Koa and Express, the Feathers application (`app` object) will also be fully compatible with the respective framework. For both frameworks, additional required middleware will be registered in the application file. More information can be found in the linked API documentation. + +## Configure functions + +The Feathers application does not use a complicated dependency injection mechanism. Instead, the application is wired together using _configure functions_ to split things up into individual files. They are functions that are exported from a file and that take the Feathers [app object](../../api/application.md) and then use it to e.g. register services. Those functions are then passed to [app.configure](../../api/application.md#configurecallback). + +For example, have a look at the following files: + +`src/services/index.ts` looks like this: + +```ts +import type { Application } from '../declarations' +import { user } from './users/users' + +export const services = (app: Application) => { + app.configure(user) + // All services will be registered here +} +``` + +It uses another configure function exported from `src/services/users/users.ts`. The export from `src/services/index.js` is in turn used in `src/app.ts` as: + +```ts +// ... +import { services } from './services' + +// ... +app.configure(authentication) +app.configure(services) +// ... +``` + +This is how the generator splits things up into separate files and any documentation example that uses the `app` object can be used in a configure function. You can create your own files that export a configure function and `require`/`import` and `app.configure` them. + +
+ +Keep in mind that the order in which configure functions are called might matter, e.g. if it is using a service, that service has to be registered first. Configure functions are not asynchronous. Any asynchronous operations should be done in [application setup hooks](#application-hooks). + +
+ +## Application hooks + +The application file also includes a section to set up [application hooks](../../api/hooks.md#application-hooks) which are hooks that run for every service. In our case, the `logErrorHook` to log any service errors has already been registered: + +```ts +// Register hooks that run on all service methods +app.hooks({ + around: { + all: [logErrorHook] + }, + before: {}, + after: {}, + error: {} +}) +``` + +Following that is the special [setup and teardown](../../api/hooks.md#setup-and-teardown) hook section to register hooks that run once when the application starts or shuts down. This can be used to e.g. set dynamic configuration values. + +```ts +// Register application setup and teardown hooks here +app.hooks({ + setup: [], + teardown: [] +}) +``` + +## Tests, jobs and SSR + +The `app` file can be imported like any other Node module. This means it can be used directly in tests, scheduled jobs or server side rendering without having to spin up a separate server instance. diff --git a/docs/guides/cli/authentication.md b/docs/guides/cli/authentication.md new file mode 100644 index 0000000000..9c8a9e1387 --- /dev/null +++ b/docs/guides/cli/authentication.md @@ -0,0 +1 @@ +# Authentication diff --git a/docs/guides/cli/config.md b/docs/guides/cli/config.md index 95e73284f2..f7169d3f6a 100644 --- a/docs/guides/cli/config.md +++ b/docs/guides/cli/config.md @@ -2,8 +2,61 @@ outline: deep --- -# Generated Configuration +# Application Configuration -## The Config Package +## App configuration -## Config Schemas +A generated application uses the **[configuration package](../../api/configuration.md)** to load configuration information based on the environment. It is based on the battle-tested and widely used [node-config](https://github.com/node-config/node-config) and loads configuration settings so that they are available via [app.get()](../../api/application.md#getname). It can also [validate the initial configuration against a schema](#config-schemas). + +
+ +For more information on application configuration and schemas see the [configuration API documentation](../../api/configuration.md). + +
+ +### Environment variables + +The most important environment variable for loading the configuration is `NODE_ENV`. For example, setting `NODE_ENV=development` (in a single command e.g. as `NODE_ENV=development npm run dev`) will first load `config/default.json` and then merge it with `config/development.json`. If no environment is set, `config/default.json` will be used. + +While `node-config` recommends to pass environment based configuration as a JSON string in a single `NODE_CONFIG` environment variable, it is also possible to use other environment variables via the `config/custom-environment-variables.json` file which looks like this by default: + +```json +{ + "port": { + "__name": "PORT", + "__format": "number" + }, + "host": "HOSTNAME" +} +``` + +This sets `app.get('port')` using the `PORT` environment variable (if it is available) parsing it as a number and `app.get('host')` from the `HOSTNAME` environment variable. + +### Configuration Schemas + +A generated application comes with a schema that validates the initial configuration when the application is started. This makes it much easier to catch configuration errors early which can otherwise be especially difficult to debug in remote environments. + +The configuration [schema definition](../../api/schema/index.md) can be found in `schemas/configuration.ts`. It is used as a [configuration schema](../../api/configuration.md#configuration-validation) and loads some default schemas for authentication and database connection configuration and adds values for `host`, `port` and the `public` hosted file folder. The types of this schema are also used for `app.get()` and `app.set()` [typings](./typescript.md). The initial configuration schema will be validated on application startup when calling [`app.listen()`](../../api/application.md#listenport) or [`app.setup()`](../../api/application.md#setupserver). + +## Folders + +The source and test folders to which files are generated is set in the `package.json`. To change them, rename the `src/` or `test/` folder to what you want it to and then update `package.json` `directories` section accordingly: + +```json +{ + "directories": { + "lib": "api/src", + "test": "api/test" + } +} +``` + +## Code formatting + +The Feathers CLI uses [Prettier](https://prettier.io/) for code formatting and generates a configuration for it in a new application. To change the options, like the use of semicolons, quotes etc, edit the `.prettierrc` file with the [options available](https://prettier.io/docs/en/options.html). To update all existing source files with the new code style run + +``` +npm run prettier +``` + +Any new files generated will use current Prettier configuration. See the [Prettier Integration with Linters](https://prettier.io/docs/en/integrating-with-linters.html) documentation for how to integrate with tools like ESLint. diff --git a/docs/guides/cli/databases.md b/docs/guides/cli/databases.md new file mode 100644 index 0000000000..d28c1a185d --- /dev/null +++ b/docs/guides/cli/databases.md @@ -0,0 +1,63 @@ +# Databases + +## SQL + +### Connection + +Depending on the SQL database you selected, a `src/.ts` file will be created that sets up a connection using [KnexJS](../../api/databases/knex.md). It uses the connection settings from the `` [configuration value](./config.md#app-configuration) and exports a [configure function](./application.md#configure-functions) that initializes the database connection. The Knex connection object is then acessible wherever you have access to the [app object](./application.md) via + +```ts +const knex = app.get('Client') +``` + +### Migrations + +Migrations are a best practise for SQL databases to roll out and undo changes to the data model and are set up automatically with an SQL database connection. The generated `knexfile.ts` imports the [app object](./application.md) to establish the connection to the database. To run migration scripts for the connection from the [configuration environment](./config.md#environment-variables) use: + +``` +npm run migrate +``` + +To create a new migration, run + +``` +npm run migrate:make -- +``` + +and replace `` with the name of the migration you want to create. This will create a new file in the `migrations/` folder. + +
+ +For more information on what is available in migration files, see the [Knex migrations documentation](https://knexjs.org/guide/migrations.html). + +
+ +### Models + +KnexJS does not have a concept of models. Instead a new service is initialized with the table name and `app.get('Client')` as the connection. For more information on how to create custom queries and more, see the [SQL database adapter API documentation](../../api/databases/knex.md). + +## MongoDB + +### Connection + +`src/mongodb.ts` exports a [configure function](./application.md#configure-functions) that connects to the MongoDB connection string set as `mongodb` in your [configuration](./config.md#app-configuration). The [MongoDB NodeJS client](https://www.mongodb.com/languages/mongodb-with-nodejs) is then accessible wherever you have access to the [app object](./application.md) via + +```ts +const db = await app.get('mongodbClient') +``` + +The default connection string tries to connect to a local MongoDB instance with no password. To use e.g. [MongoDB Atlas](https://www.mongodb.com/cloud) change the `mongodb` property in `config/default.json` or add it as an [environment variable](./config.md#environment-variables) with the connection string that will look similar to this: + +``` +mongodb+srv://:@cluster0.xyz.mongodb.net/?retryWrites=true&w=majority +``` + +### Models + +The collection for a MongoDB service can be accessed via + +```ts +const userCollection = await app.service('users').Model +``` + +See the [MongoDB service API documentation](../../api/databases/mongodb.md) for more information. diff --git a/docs/guides/cli/hooks.md b/docs/guides/cli/hooks.md new file mode 100644 index 0000000000..6aac6e934f --- /dev/null +++ b/docs/guides/cli/hooks.md @@ -0,0 +1,5 @@ +--- +outline: deep +--- + +# Hooks diff --git a/docs/guides/cli/logging.md b/docs/guides/cli/logging.md new file mode 100644 index 0000000000..b921bbe501 --- /dev/null +++ b/docs/guides/cli/logging.md @@ -0,0 +1 @@ +# Logging diff --git a/docs/guides/cli/schemas-and-resolvers.md b/docs/guides/cli/schemas.md similarity index 100% rename from docs/guides/cli/schemas-and-resolvers.md rename to docs/guides/cli/schemas.md diff --git a/docs/guides/cli/service-classes.md b/docs/guides/cli/service-classes.md deleted file mode 100644 index 99863f8f5f..0000000000 --- a/docs/guides/cli/service-classes.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -outline: deep ---- - -# Service Class - -Link here from the class file in the generated app: - -Todo: Implement ServiceAdapter picker for the following - -## Knex - -## MongoDB - -```ts -import { MongoDBService } from '@feathersjs/mongodb' -import type { MongoDBAdapterParams } from '@feathersjs/mongodb' - -import type { Application } from '../../declarations' -import type { Message, MessageData, MessageQuery } from './messages.schema' - -export interface MessageParams extends MongoDBAdapterParams {} - -// Message class for MongoDB https://dove.feathersjs.com/cli/service-class -export class MessageService extends MongoDBService {} - -export const getOptions = (app: Application) => { - return { - paginate: app.get('paginate'), - Model: app.get('mongodbClient').then((db) => db.collection('message')), - } -} -``` \ No newline at end of file diff --git a/docs/guides/cli/service-hooks.md b/docs/guides/cli/services.md similarity index 51% rename from docs/guides/cli/service-hooks.md rename to docs/guides/cli/services.md index aed9b1e575..0da9e8f8cd 100644 --- a/docs/guides/cli/service-hooks.md +++ b/docs/guides/cli/services.md @@ -2,7 +2,35 @@ outline: deep --- -# Generate a Service +# Service Class + +Link here from the class file in the generated app: + +Todo: Implement ServiceAdapter picker for the following + +## Knex + +## MongoDB + +```ts +import { MongoDBService } from '@feathersjs/mongodb' +import type { MongoDBAdapterParams } from '@feathersjs/mongodb' + +import type { Application } from '../../declarations' +import type { Message, MessageData, MessageQuery } from './messages.schema' + +export interface MessageParams extends MongoDBAdapterParams {} + +// Message class for MongoDB https://dove.feathersjs.com/cli/service-class +export class MessageService extends MongoDBService {} + +export const getOptions = (app: Application) => { + return { + paginate: app.get('paginate'), + Model: app.get('mongodbClient').then((db) => db.collection('message')) + } +} +``` ## Hooks Overview @@ -20,21 +48,21 @@ export * from './test.class' export const testing = (app: Application) => { app.use('messages', new TestingService(getOptions(app)), { methods: ['find', 'get', 'create', 'update', 'patch', 'remove'], - events: [], + events: [] }) app.service('messages').hooks({ around: { - all: [authenticate('jwt')], + all: [authenticate('jwt')] }, before: { - all: [], + all: [] }, after: { - all: [], + all: [] }, error: { - all: [], - }, + all: [] + } }) } ``` @@ -47,7 +75,7 @@ We already have an explanation of this in the troubleshooting guide, so we can l // Update Service Declarations https://dove.feathersjs.com/cli/service-overview.html#service-types declare module '../../../../declarations' { interface ServiceTypes { - 'messages': TestingService + messages: TestingService } } ``` diff --git a/docs/guides/cli/typescript.md b/docs/guides/cli/typescript.md new file mode 100644 index 0000000000..ef1d843f24 --- /dev/null +++ b/docs/guides/cli/typescript.md @@ -0,0 +1,86 @@ +# TypeScript + + + +The main file for application specific TypeScript declarations can be found at `src/declarations.ts`. + +## Configuration Types + +The `Configuration` interface defines the types for [app.get](../../api/application.md#getname) and [app.set](../../api/application.md#setname-value). It is extended from the type inferred from the [configuration schema](./config.md#configuration-schemas). Since you can store anything global to the application in `app.get` and `app.set`, you can add additional types that are not part of the initial application configuration here. + +```ts +// The types for app.get(name) and app.set(name) +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface Configuration extends ApplicationConfiguration { + startupTime: Date +} +``` + +
+ +Both `Configuration` and `ServiceTypes` need to be declared as an `interface` (even if it is empty) so they can be extended via `declare module` in other files. Do not remove the `eslint-disable-next-line` comments. + +
+ +## Service Types + +The `ServiceTypes` interface contains a mapping of all service paths to their service type so that [app.use](../../api/application.md#usepath-service--options) and [app.service](../../api/application.md#servicepath) use the correct type. + +```ts +// A mapping of service names to types. Will be extended in service files. +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface ServiceTypes {} +``` + +Usually the `ServiceTypes` interface is not modified directly in this file but instead extended via `declare module` in the files where the services are registered. This usually looks like this: + +```ts +// Add this service to the service type index +declare module '../../declarations' { + interface ServiceTypes { + users: UserService + } +} +``` + +## Application + +The `Application` interface is the type for the main [app object](./application.md) using the [ServiceTypes](#service-types) interface as the service index and [ConfigurationTypes](#configuration-types) for `app.get` and `app.set`. + +```ts +// The application instance type that will be used everywhere else +export type Application = FeathersApplication +``` + +
+ +Always use `import { Application } from './declarations'` to get the proper service and configuration typings. You normally do **not** need to use `import { Application } from '@feathersjs/feathers'` directly. + +
+ +## Hook Context + +The `HookContext` type exports a [hook context](../../api/hooks.md) type with the `Application` and a generic service type `S`. + +```ts +// The context for hook functions - can be typed with a service class +export type HookContext = FeathersHookContext +``` + +Use `HookContext>` to get the full hook context for a service. + +## Services and Params + +See the [services chapter](./services.md) for more information on service and parameter typings. + +
+ + + +
+ +Please pick **TypeScript** as the Code language in the main menu dropdown. + +
+ +
diff --git a/docs/guides/cli/validators.md b/docs/guides/cli/validators.md index c4ae36a751..b6a9fb20e1 100644 --- a/docs/guides/cli/validators.md +++ b/docs/guides/cli/validators.md @@ -4,11 +4,39 @@ outline: deep # Validators -## AJV and TypeBox +For all currently supported schema types, AJV is used as the default validator. See the [validators API documentation](../../api/schema/validators.md) for more information. -General data about AJV and TypeBox. +## AJV validators +The `src/schemas/validators.ts` file sets up two Ajv instances for data and querys (for which string types will be coerced automatically). It also sets up a collection of additional formats using [ajv-formats](https://ajv.js.org/packages/ajv-formats.html). The validators in this file can be customized according to the [Ajv documentation](https://ajv.js.org/) and [its plugins](https://ajv.js.org/packages/). You can find the available Ajv options in the [Ajs class API docs](https://ajv.js.org/options.html). -## MongoDB-Specific Stuff +```ts +import { Ajv, addFormats } from '@feathersjs/schema' +import type { FormatsPluginOptions } from '@feathersjs/schema' -TODO: hook up the ServiceAdapter picker for this section \ No newline at end of file +const formats: FormatsPluginOptions = [ + 'date-time', + 'time', + 'date', + 'email', + 'hostname', + 'ipv4', + 'ipv6', + 'uri', + 'uri-reference', + 'uuid', + 'uri-template', + 'json-pointer', + 'relative-json-pointer', + 'regex' +] + +export const dataValidator = addFormats(new Ajv({}), formats) + +export const queryValidator = addFormats( + new Ajv({ + coerceTypes: true + }), + formats +) +``` diff --git a/packages/cli/src/app/templates/app.test.tpl.ts b/packages/cli/src/app/templates/app.test.tpl.ts index f2d9334297..6e3b638f4f 100644 --- a/packages/cli/src/app/templates/app.test.tpl.ts +++ b/packages/cli/src/app/templates/app.test.tpl.ts @@ -2,7 +2,10 @@ import { generator, toFile } from '@feathershq/pinion' import { renderSource } from '../../commons' import { AppGeneratorContext } from '../index' -const template = ({ lib }: AppGeneratorContext) => /* ts */ `import assert from 'assert' +const template = ({ + lib +}: AppGeneratorContext) => /* ts */ `// For more information about this file see https://dove.feathersjs.com/guides/cli/application.html +import assert from 'assert' import axios from 'axios' import type { Server } from 'http' import { app } from '../${lib}/app' diff --git a/packages/cli/src/app/templates/app.tpl.ts b/packages/cli/src/app/templates/app.tpl.ts index 95981a162f..c8bf47691a 100644 --- a/packages/cli/src/app/templates/app.tpl.ts +++ b/packages/cli/src/app/templates/app.tpl.ts @@ -4,7 +4,8 @@ import { AppGeneratorContext } from '../index' const tsKoaApp = ({ transports -}: AppGeneratorContext) => /* ts */ `import { feathers } from '@feathersjs/feathers' +}: AppGeneratorContext) => /* ts */ `// For more information about this file see https://dove.feathersjs.com/guides/cli/application.html +import { feathers } from '@feathersjs/feathers' import configuration from '@feathersjs/configuration' import { koa, rest, bodyParser, errorHandler, parseAuthentication, cors, serveStatic @@ -63,7 +64,8 @@ export { app } const tsExpressApp = ({ transports -}: AppGeneratorContext) => /* ts */ `import { feathers } from '@feathersjs/feathers' +}: AppGeneratorContext) => /* ts */ `// For more information about this file see https://dove.feathersjs.com/guides/cli/application.html +import { feathers } from '@feathersjs/feathers' import express, { rest, json, urlencoded, cors, compression, serveStatic, notFound, errorHandler diff --git a/packages/cli/src/app/templates/config.tpl.ts b/packages/cli/src/app/templates/config.tpl.ts index 05971de523..88e806d353 100644 --- a/packages/cli/src/app/templates/config.tpl.ts +++ b/packages/cli/src/app/templates/config.tpl.ts @@ -1,4 +1,5 @@ import { generator, toFile, writeJSON } from '@feathershq/pinion' +import { renderSource } from '../../commons' import { AppGeneratorContext } from '../index' const defaultConfig = ({}: AppGeneratorContext) => ({ @@ -24,8 +25,60 @@ const testConfig = { port: 8998 } +const configurationJsonTemplate = + ({}: AppGeneratorContext) => /* ts */ `// For more information about this file see https://dove.feathersjs.com/guides/cli/config.html#configuration-schemas +import { defaultAppSettings, getValidator } from '@feathersjs/schema' +import type { FromSchema } from '@feathersjs/schema' + +import { dataValidator } from './validators' + +export const configurationSchema = { + type: 'object', + additionalProperties: false, + required: [ 'host', 'port', 'public' ], + properties: { + ...defaultAppSettings, + host: { type: 'string' }, + port: { type: 'number' }, + public: { type: 'string' } + } +} as const + +export const configurationValidator = getValidator(configurationSchema, dataValidator) + +export type ApplicationConfiguration = FromSchema +` + +const configurationTypeboxTemplate = + ({}: AppGeneratorContext) => /* ts */ `// For more information about this file see https://dove.feathersjs.com/guides/cli/config.html#configuration-schemas + import { Type, getValidator, defaultAppConfiguration } from '@feathersjs/typebox' +import type { Static } from '@feathersjs/typebox' + +import { dataValidator } from './validators' + +export const configurationSchema = Type.Intersect([ + defaultAppConfiguration, + Type.Object({ + host: Type.String(), + port: Type.Number(), + public: Type.String() + }) +]) + +export type ApplicationConfiguration = Static + +export const configurationValidator = getValidator(configurationSchema, dataValidator) +` + export const generate = (ctx: AppGeneratorContext) => generator(ctx) .then(writeJSON(defaultConfig, toFile('config', 'default.json'))) .then(writeJSON(testConfig, toFile('config', 'test.json'))) .then(writeJSON(customEnvironment, toFile('config', 'custom-environment-variables.json'))) + .then( + renderSource( + async (ctx) => + ctx.schema === 'typebox' ? configurationTypeboxTemplate(ctx) : configurationJsonTemplate(ctx), + toFile(({ lib }) => lib, 'schemas', 'configuration') + ) + ) diff --git a/packages/cli/src/app/templates/declarations.tpl.ts b/packages/cli/src/app/templates/declarations.tpl.ts index 7ad58ed54f..f453e2c587 100644 --- a/packages/cli/src/app/templates/declarations.tpl.ts +++ b/packages/cli/src/app/templates/declarations.tpl.ts @@ -3,7 +3,8 @@ import { AppGeneratorContext } from '../index' const template = ({ framework -}: AppGeneratorContext) => /* ts */ `import { HookContext as FeathersHookContext, NextFunction } from '@feathersjs/feathers' +}: AppGeneratorContext) => /* ts */ `// For more information about this file see https://dove.feathersjs.com/guides/cli/typescript.html +import { HookContext as FeathersHookContext, NextFunction } from '@feathersjs/feathers' import { Application as FeathersApplication } from '@feathersjs/${framework}' import { ApplicationConfiguration } from './schemas/configuration' diff --git a/packages/cli/src/app/templates/schemas.tpl.ts b/packages/cli/src/app/templates/schemas.tpl.ts deleted file mode 100644 index 2182986a78..0000000000 --- a/packages/cli/src/app/templates/schemas.tpl.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { generator, toFile } from '@feathershq/pinion' -import { renderSource } from '../../commons' -import { AppGeneratorContext } from '../index' - -const validatorTemplate = /* ts */ `import { Ajv, addFormats } from '@feathersjs/schema' -import type { FormatsPluginOptions } from '@feathersjs/schema' - -const formats: FormatsPluginOptions = [ - 'date-time', - 'time', - 'date', - 'email', - 'hostname', - 'ipv4', - 'ipv6', - 'uri', - 'uri-reference', - 'uuid', - 'uri-template', - 'json-pointer', - 'relative-json-pointer', - 'regex' -] - -export const dataValidator = addFormats(new Ajv({}), formats) - -export const queryValidator = addFormats(new Ajv({ - coerceTypes: true -}), formats) -` - -const configurationJsonTemplate = - ({}: AppGeneratorContext) => /* ts */ `import { defaultAppSettings, getValidator } from '@feathersjs/schema' -import type { FromSchema } from '@feathersjs/schema' - -import { dataValidator } from './validators' - -export const configurationSchema = { - type: 'object', - additionalProperties: false, - required: [ 'host', 'port', 'public' ], - properties: { - ...defaultAppSettings, - host: { type: 'string' }, - port: { type: 'number' }, - public: { type: 'string' } - } -} as const - -export const configurationValidator = getValidator(configurationSchema, dataValidator) - -export type ApplicationConfiguration = FromSchema -` - -const configurationTypeboxTemplate = - ({}: AppGeneratorContext) => /* ts */ `import { Type, getValidator, defaultAppConfiguration } from '@feathersjs/typebox' -import type { Static } from '@feathersjs/typebox' - -import { dataValidator } from './validators' - -export const configurationSchema = Type.Intersect([ - defaultAppConfiguration, - Type.Object({ - host: Type.String(), - port: Type.Number(), - public: Type.String() - }) -]) - -export type ApplicationConfiguration = Static - -export const configurationValidator = getValidator(configurationSchema, dataValidator) -` - -export const generate = (ctx: AppGeneratorContext) => - generator(ctx) - .then( - renderSource( - async (ctx) => - ctx.schema === 'typebox' ? configurationTypeboxTemplate(ctx) : configurationJsonTemplate(ctx), - toFile(({ lib }) => lib, 'schemas', 'configuration') - ) - ) - .then( - renderSource( - validatorTemplate, - toFile(({ lib }) => lib, 'schemas', 'validators') - ) - ) diff --git a/packages/cli/src/app/templates/services.tpl.ts b/packages/cli/src/app/templates/services.tpl.ts index 87c9b1f3d9..fdcd265f5f 100644 --- a/packages/cli/src/app/templates/services.tpl.ts +++ b/packages/cli/src/app/templates/services.tpl.ts @@ -2,7 +2,9 @@ import { generator, toFile } from '@feathershq/pinion' import { renderSource } from '../../commons' import { AppGeneratorContext } from '../index' -const template = ({}: AppGeneratorContext) => /* ts */ `import type { Application } from '../declarations' +const template = + ({}: AppGeneratorContext) => /* ts */ `// For more information about this file see https://dove.feathersjs.com/guides/cli/application.html#configure-functions +import type { Application } from '../declarations' export const services = (app: Application) => { // All services will be registered here diff --git a/packages/cli/src/app/templates/validators.tpl.ts b/packages/cli/src/app/templates/validators.tpl.ts new file mode 100644 index 0000000000..8bec2b3d33 --- /dev/null +++ b/packages/cli/src/app/templates/validators.tpl.ts @@ -0,0 +1,39 @@ +import { generator, toFile } from '@feathershq/pinion' +import { renderSource } from '../../commons' +import { AppGeneratorContext } from '../index' + +const validatorTemplate = /* ts */ `// For more information about this file see https://dove.feathersjs.com/guides/cli/validators.html +import { Ajv, addFormats } from '@feathersjs/schema' +import type { FormatsPluginOptions } from '@feathersjs/schema' + +const formats: FormatsPluginOptions = [ + 'date-time', + 'time', + 'date', + 'email', + 'hostname', + 'ipv4', + 'ipv6', + 'uri', + 'uri-reference', + 'uuid', + 'uri-template', + 'json-pointer', + 'relative-json-pointer', + 'regex' +] + +export const dataValidator = addFormats(new Ajv({}), formats) + +export const queryValidator = addFormats(new Ajv({ + coerceTypes: true +}), formats) +` + +export const generate = (ctx: AppGeneratorContext) => + generator(ctx).then( + renderSource( + validatorTemplate, + toFile(({ lib }) => lib, 'schemas', 'validators') + ) + ) diff --git a/packages/cli/src/connection/templates/knex.tpl.ts b/packages/cli/src/connection/templates/knex.tpl.ts index bbc0f5a9df..a2ac0d630d 100644 --- a/packages/cli/src/connection/templates/knex.tpl.ts +++ b/packages/cli/src/connection/templates/knex.tpl.ts @@ -2,7 +2,10 @@ import { generator, toFile, before, mergeJSON } from '@feathershq/pinion' import { ConnectionGeneratorContext } from '../index' import { injectSource, renderSource } from '../../commons' -const template = ({ database }: ConnectionGeneratorContext) => /* ts */ `import knex from 'knex' +const template = ({ + database +}: ConnectionGeneratorContext) => /* ts */ `// For more information about this file see https://dove.feathersjs.com/guides/cli/databases.html#sql +import knex from 'knex' import type { Knex } from 'knex' import type { Application } from './declarations' @@ -24,7 +27,8 @@ const knexfile = ({ lib, language, database -}: ConnectionGeneratorContext) => /* ts */ `import { app } from './${lib}/app' +}: ConnectionGeneratorContext) => /* ts */ `// For more information about this file see https://dove.feathersjs.com/guides/cli/databases.html#sql +import { app } from './${lib}/app' // Load our database connection info from the app configuration const config = app.get('${database}') diff --git a/packages/cli/src/connection/templates/mongodb.tpl.ts b/packages/cli/src/connection/templates/mongodb.tpl.ts index 203eba6a32..af55d38529 100644 --- a/packages/cli/src/connection/templates/mongodb.tpl.ts +++ b/packages/cli/src/connection/templates/mongodb.tpl.ts @@ -2,7 +2,9 @@ import { generator, toFile, before } from '@feathershq/pinion' import { ConnectionGeneratorContext } from '../index' import { injectSource, renderSource } from '../../commons' -const template = ({}: ConnectionGeneratorContext) => /* ts */ `import { MongoClient } from 'mongodb' +const template = + ({}: ConnectionGeneratorContext) => /* ts */ `// For more information about this file see https://dove.feathersjs.com/guides/cli/databases.html#mongodb +import { MongoClient } from 'mongodb' import type { Db } from 'mongodb' import type { Application } from './declarations' From 128d68f1185aecb1b9619818c075a3429bca957d Mon Sep 17 00:00:00 2001 From: daffl Date: Fri, 11 Nov 2022 14:12:21 -0800 Subject: [PATCH 04/25] More CLI guides --- docs/.vitepress/config.sidebar.ts | 10 ++- docs/guides/cli/application.md | 19 ++++- docs/guides/cli/authentication.md | 4 + docs/guides/cli/channels.md | 1 + docs/guides/cli/client.md | 3 + .../cli/{config.md => configuration.md} | 4 +- docs/guides/cli/databases.md | 8 +- docs/guides/cli/logging.md | 75 +++++++++++++++++++ docs/guides/cli/typescript.md | 27 ++++++- packages/cli/src/app/templates/app.tpl.ts | 9 ++- packages/cli/src/app/templates/logger.tpl.ts | 28 +++++-- .../cli/src/app/templates/validators.tpl.ts | 4 +- 12 files changed, 172 insertions(+), 20 deletions(-) create mode 100644 docs/guides/cli/channels.md create mode 100644 docs/guides/cli/client.md rename docs/guides/cli/{config.md => configuration.md} (92%) diff --git a/docs/.vitepress/config.sidebar.ts b/docs/.vitepress/config.sidebar.ts index 71472b42ff..0b113e58f5 100644 --- a/docs/.vitepress/config.sidebar.ts +++ b/docs/.vitepress/config.sidebar.ts @@ -93,9 +93,13 @@ export default { text: 'Application', link: '/guides/cli/application.md' }, + { + text: 'Client', + link: '/guides/cli/client.md' + }, { text: 'Configuration', - link: '/guides/cli/config.md' + link: '/guides/cli/configuration.md' }, { text: 'TypeScript', @@ -128,6 +132,10 @@ export default { { text: 'Hooks', link: '/guides/cli/hooks.md' + }, + { + text: 'Channels', + link: '/guides/cli/channels.md' } ] }, diff --git a/docs/guides/cli/application.md b/docs/guides/cli/application.md index deee74e485..21308ffe35 100644 --- a/docs/guides/cli/application.md +++ b/docs/guides/cli/application.md @@ -1,3 +1,7 @@ +--- +outline: deep +--- + # Application The `src/app.ts` file is the main file where the [Feathers application](../../api/application.md) gets initialized and wired up with a Feathers transport. @@ -72,4 +76,17 @@ app.hooks({ ## Tests, jobs and SSR -The `app` file can be imported like any other Node module. This means it can be used directly in tests, scheduled jobs or server side rendering without having to spin up a separate server instance. +The `app` file can be imported like any other Node module. This means it can be used directly in tests, scheduled jobs or server side rendering without having to start a separate server instance. For example, the unit tests import the application like this: + +```ts +import assert from 'assert' +import { app } from '../../src/app' + +describe('messages service', () => { + it('registered the service', () => { + const service = app.service('messages') + + assert.ok(service, 'Registered the service') + }) +}) +``` diff --git a/docs/guides/cli/authentication.md b/docs/guides/cli/authentication.md index 9c8a9e1387..3e582cedfa 100644 --- a/docs/guides/cli/authentication.md +++ b/docs/guides/cli/authentication.md @@ -1 +1,5 @@ +--- +outline: deep +--- + # Authentication diff --git a/docs/guides/cli/channels.md b/docs/guides/cli/channels.md new file mode 100644 index 0000000000..a3c35c63e0 --- /dev/null +++ b/docs/guides/cli/channels.md @@ -0,0 +1 @@ +# Channels diff --git a/docs/guides/cli/client.md b/docs/guides/cli/client.md new file mode 100644 index 0000000000..a1e25027d0 --- /dev/null +++ b/docs/guides/cli/client.md @@ -0,0 +1,3 @@ +# Client + +## Usage diff --git a/docs/guides/cli/config.md b/docs/guides/cli/configuration.md similarity index 92% rename from docs/guides/cli/config.md rename to docs/guides/cli/configuration.md index f7169d3f6a..d296cf56f3 100644 --- a/docs/guides/cli/config.md +++ b/docs/guides/cli/configuration.md @@ -2,7 +2,9 @@ outline: deep --- -# Application Configuration +# Configuration + +This page describes how the application loads its settings from [configuration files](#app-configuration) or [environment variables](#environment-variables) and how the generated file [folders](#folders) and [code style](#code-formatting) can be changed. ## App configuration diff --git a/docs/guides/cli/databases.md b/docs/guides/cli/databases.md index d28c1a185d..cdbf19fd7b 100644 --- a/docs/guides/cli/databases.md +++ b/docs/guides/cli/databases.md @@ -1,3 +1,7 @@ +--- +outline: deep +--- + # Databases ## SQL @@ -12,7 +16,7 @@ const knex = app.get('Client') ### Migrations -Migrations are a best practise for SQL databases to roll out and undo changes to the data model and are set up automatically with an SQL database connection. The generated `knexfile.ts` imports the [app object](./application.md) to establish the connection to the database. To run migration scripts for the connection from the [configuration environment](./config.md#environment-variables) use: +Migrations are a best practise for SQL databases to roll out and undo changes to the data model and are set up automatically with an SQL database connection. The generated `knexfile.ts` imports the [app object](./application.md) to establish the connection to the database. To run migration scripts for the connection from the [configuration environment](./configuration.md#environment-variables) use: ``` npm run migrate @@ -46,7 +50,7 @@ KnexJS does not have a concept of models. Instead a new service is initialized w const db = await app.get('mongodbClient') ``` -The default connection string tries to connect to a local MongoDB instance with no password. To use e.g. [MongoDB Atlas](https://www.mongodb.com/cloud) change the `mongodb` property in `config/default.json` or add it as an [environment variable](./config.md#environment-variables) with the connection string that will look similar to this: +The default connection string tries to connect to a local MongoDB instance with no password. To use e.g. [MongoDB Atlas](https://www.mongodb.com/cloud) change the `mongodb` property in `config/default.json` or add it as an [environment variable](./configuration.md#environment-variables) with the connection string that will look similar to this: ``` mongodb+srv://:@cluster0.xyz.mongodb.net/?retryWrites=true&w=majority diff --git a/docs/guides/cli/logging.md b/docs/guides/cli/logging.md index b921bbe501..301c500d41 100644 --- a/docs/guides/cli/logging.md +++ b/docs/guides/cli/logging.md @@ -1 +1,76 @@ +--- +outline: deep +--- + # Logging + +## Logger + +The `src/logger.ts` file initialises the widely used [Winston logger](https://github.com/winstonjs/winston) library, by default with the `info` log level, logging to the console. + +```ts +import { createLogger, format, transports } from 'winston' + +// Configure the Winston logger. For the complete documentation see https://github.com/winstonjs/winston +export const logger = createLogger({ + // To see more detailed errors, change this to 'debug' + level: 'info', + format: format.combine(format.splat(), format.simple()), + transports: [new transports.Console()] +}) +``` + +You can import the logger directly in any file where you want to add logging information. + +```ts +import { logger } from './logger' + +logger.info('Log some information here') +``` + +## Error Logging Hook + +The `src/hooks/log-error.ts` file exports a `logError` hook that uses the above logger to log any error for a service method, including validation error details (when they are available). It is registered as an [application hook](./application.md#application-hooks) `all` hook, meaning it will log errors for any service method. + +## Profiling example + +To log some basic profiling information like which method was called and how long it took to run you can create a new _around_ hook called `profiler` via + +``` +npx feathers generate hook +``` + +Then update `src/hooks/profiler.ts` as follows: + +```ts +import type { HookContext, NextFunction } from '../declarations' +import { logger } from '../logger' + +export const profiler = async (context: HookContext, next: NextFunction) => { + const startTime = Date.now() + + await next() + + const runtime = Date.now() - startTime + + console.log(`Calling ${context.method} on service ${context.path} took ${runtime}ms`) +} +``` + +And add it in `src/app.ts` as an application hook after the `logError` hook as follows: + +```ts{1,8} +import { profiler } from './hooks/profiler' + +//... + +// Register hooks that run on all service methods +app.hooks({ + around: { + all: [ logError, profiler ] + }, + before: {}, + after: {}, + error: {} +}) +``` diff --git a/docs/guides/cli/typescript.md b/docs/guides/cli/typescript.md index ef1d843f24..87264fdc61 100644 --- a/docs/guides/cli/typescript.md +++ b/docs/guides/cli/typescript.md @@ -1,12 +1,37 @@ +--- +outline: deep +--- + # TypeScript The main file for application specific TypeScript declarations can be found at `src/declarations.ts`. +## Compilation + +In order to compile and start the application use + +``` +npm run compile +npm start +``` + +For development with live reload use + +``` +npm run dev +``` + +
+ +To get the latest types in the [client](./client.md) and any time before `npm start`, `npm run compile` needs to run. + +
+ ## Configuration Types -The `Configuration` interface defines the types for [app.get](../../api/application.md#getname) and [app.set](../../api/application.md#setname-value). It is extended from the type inferred from the [configuration schema](./config.md#configuration-schemas). Since you can store anything global to the application in `app.get` and `app.set`, you can add additional types that are not part of the initial application configuration here. +The `Configuration` interface defines the types for [app.get](../../api/application.md#getname) and [app.set](../../api/application.md#setname-value). It is extended from the type inferred from the [configuration schema](./configuration.md#configuration-schemas). Since you can store anything global to the application in `app.get` and `app.set`, you can add additional types that are not part of the initial application configuration here. ```ts // The types for app.get(name) and app.set(name) diff --git a/packages/cli/src/app/templates/app.tpl.ts b/packages/cli/src/app/templates/app.tpl.ts index c8bf47691a..5c105b3ec5 100644 --- a/packages/cli/src/app/templates/app.tpl.ts +++ b/packages/cli/src/app/templates/app.tpl.ts @@ -14,7 +14,7 @@ ${transports.includes('websockets') ? "import socketio from '@feathersjs/socketi import type { Application } from './declarations' import { configurationValidator } from './schemas/configuration' -import { logErrorHook } from './logger' +import { logError } from './hooks/log-error' import { services } from './services/index' import { channels } from './channels' @@ -47,7 +47,7 @@ app.configure(channels) // Register hooks that run on all service methods app.hooks({ around: { - all: [ logErrorHook ] + all: [ logError ] }, before: {}, after: {}, @@ -75,7 +75,8 @@ ${transports.includes('websockets') ? "import socketio from '@feathersjs/socketi import type { Application } from './declarations' import { configurationValidator } from './schemas/configuration' -import { logger, logErrorHook } from './logger' +import { logger } from './logger' +import { logError } from './hooks/log-error' import { services } from './services/index' import { channels } from './channels' @@ -111,7 +112,7 @@ app.use(errorHandler({ logger })) // Register hooks that run on all service methods app.hooks({ around: { - all: [ logErrorHook ] + all: [ logError ] }, before: {}, after: {}, diff --git a/packages/cli/src/app/templates/logger.tpl.ts b/packages/cli/src/app/templates/logger.tpl.ts index c192111942..2b4aa099e5 100644 --- a/packages/cli/src/app/templates/logger.tpl.ts +++ b/packages/cli/src/app/templates/logger.tpl.ts @@ -3,8 +3,8 @@ import { renderSource } from '../../commons' import { AppGeneratorContext } from '../index' const template = - ({}: AppGeneratorContext) => /* ts */ `import { createLogger, format, transports } from 'winston' -import type { HookContext, NextFunction } from './declarations' + ({}: AppGeneratorContext) => /* ts */ `// For more information about this file see https://dove.feathersjs.com/guides/cli/logging.html +import { createLogger, format, transports } from 'winston' // Configure the Winston logger. For the complete documentation see https://github.com/winstonjs/winston export const logger = createLogger({ @@ -18,8 +18,13 @@ export const logger = createLogger({ new transports.Console() ] }) +` + +export const logErrorTemplate = /* ts */ `// For more information about this file see https://dove.feathersjs.com/guides/cli/logging.html +import type { HookContext, NextFunction } from '../declarations' +import { logger } from '../logger' -export const logErrorHook = async (context: HookContext, next: NextFunction) => { +export const logError = async (context: HookContext, next: NextFunction) => { try { await next() } catch (error: any) { @@ -36,9 +41,16 @@ export const logErrorHook = async (context: HookContext, next: NextFunction) => ` export const generate = (ctx: AppGeneratorContext) => - generator(ctx).then( - renderSource( - template, - toFile(({ lib }) => lib, 'logger') + generator(ctx) + .then( + renderSource( + template, + toFile(({ lib }) => lib, 'logger') + ) + ) + .then( + renderSource( + logErrorTemplate, + toFile(({ lib }) => lib, 'hooks', 'log-error') + ) ) - ) diff --git a/packages/cli/src/app/templates/validators.tpl.ts b/packages/cli/src/app/templates/validators.tpl.ts index 8bec2b3d33..72ac51942e 100644 --- a/packages/cli/src/app/templates/validators.tpl.ts +++ b/packages/cli/src/app/templates/validators.tpl.ts @@ -23,9 +23,9 @@ const formats: FormatsPluginOptions = [ 'regex' ] -export const dataValidator = addFormats(new Ajv({}), formats) +export const dataValidator: Ajv = addFormats(new Ajv({}), formats) -export const queryValidator = addFormats(new Ajv({ +export const queryValidator: Ajv = addFormats(new Ajv({ coerceTypes: true }), formats) ` From d3adafeaeea31c82b40380b758ddb6fc32e87827 Mon Sep 17 00:00:00 2001 From: daffl Date: Fri, 11 Nov 2022 15:11:39 -0800 Subject: [PATCH 05/25] Small tweaks in generate app --- docs/guides/cli/generate-app.md | 231 +++++--------------------------- 1 file changed, 30 insertions(+), 201 deletions(-) diff --git a/docs/guides/cli/generate-app.md b/docs/guides/cli/generate-app.md index 95fe30137b..86e84726af 100644 --- a/docs/guides/cli/generate-app.md +++ b/docs/guides/cli/generate-app.md @@ -4,22 +4,24 @@ outline: deep # Generate an App -You can generate a new app with `feathers generate app`. Before running the generator, create a directory for your project. Then open that project in a terminal and run the command: +You can generate a new app with `feathers generate app`. Before running the generator, create a directory for your project. Then open that project in a terminal and run the command: ```bash -feathers generate app +npm create feathers@pre myapp ``` +It will create a `myapp` folder and then ask the following questions. + ## Generator Questions -The app generator will ask you a series of questions. The following sections provide an overview of those questions. +The app generator will ask you a series of questions. The following sections provide an overview of those questions. ### TypeScript or JavaScript ```bash ? Do you want to use JavaScript or TypeScript? (Use arrow keys) -❯ TypeScript - JavaScript +❯ TypeScript + JavaScript ``` You can select a TypeScript project or a JavaScript project. The apps produced by each option are exactly equivalent in functionality. When you select "JavaScript" we generate a TypeScript app, compile it to JavaScript, then write the `.js` files to disk. The benefits of this approach are that you @@ -27,7 +29,7 @@ You can select a TypeScript project or a JavaScript project. The apps produced b ### App Name ```bash -? What is the name of your application? (my-app) +? What is the name of your application? (my-app) ``` The default name (in parentheses) will be the name of the folder that you created. Pay attention that it's the correct folder and that you don't accidentally generate in the parent folder. The name you select will become the `name` property in the `package.json`. @@ -35,7 +37,7 @@ The default name (in parentheses) will be the name of the folder that you create ### App Description ```bash -? Write a short description +? Write a short description ``` The text you specify here will become the `description` in the `package.json`. @@ -44,11 +46,11 @@ The text you specify here will become the `description` in the `package.json`. ```bash ? Which HTTP framework do you want to use? (Use arrow keys) -❯ KoaJS (recommended) - Express +❯ KoaJS (recommended) + Express ``` -The generator allows you to build on top of either of the big names in Node HTTP Frameworks: KoaJS or Express. For most Feathers applications, you only ever lightly touch the underlying framework for tasks like setting up CORS. +The generator allows you to build on top of either of the big names in Node HTTP Frameworks: KoaJS or Express. For most Feathers applications, you only ever lightly touch the underlying framework for tasks like setting up CORS. If you don't absolutely need some Express API or middleware package, we recommend using the more-modern KoaJS framework. We've selected it as the new default in Feathers v5. @@ -70,9 +72,9 @@ If you select `Real-time`, the `@feathersjs/socketio` adapter will be installed. ```bash ? Which package manager are you using? (Use arrow keys) -❯ npm - Yarn - pnpm +❯ npm + Yarn + pnpm ``` You answer here will determine which package manager is used to install modules. If you haven't installed Yarn or pnpm, select `npm`. @@ -81,24 +83,24 @@ You answer here will determine which package manager is used to install modules. ```bash ? What is your preferred schema (model) definition format? (Use arrow keys) -❯ TypeBox (recommended) - JSON schema +❯ TypeBox (recommended) + JSON schema ``` -The new Feathers v5 Dove has built-in support for TypeBox and JSON Schema schema definition formats. Both options are type friendly. Just define your schema and your get automatic TypeScript types for free. TypeBox is fully JSON schema compatible but in a format that is more concise and easier to read. +The new Feathers v5 Dove has built-in support for TypeBox and JSON Schema schema definition formats. Both options are type friendly. Just define your schema and your get automatic TypeScript types for free. TypeBox is fully JSON schema compatible but in a format that is more concise and easier to read. ### Database ```bash ? Which database are you connecting to? Other databases can be added at any time (Use arrow keys) -❯ SQLite - MongoDB - PostgreSQL - MySQL/MariaDB - Microsoft SQL +❯ SQLite + MongoDB + PostgreSQL + MySQL/MariaDB + Microsoft SQL ``` -Feathers will setup a database connection to whichever database you select in this step. The Feathers v5 Dove generator supports the most popular databases using two database adapter that have been brought into core: +Feathers will setup a database connection to whichever database you select in this step. The Feathers v5 Dove generator supports the most popular databases using two database adapter that have been brought into core: - If you select `MongoDB`, the `@feathersjs/mongodb` package will be installed and configured. - If you select `SQLite`, `PostgreSQL`, `MySQL/MariaDB`, or `Microsoft SQL` the `@feathersjs/knex` adapter will be installed. @@ -107,7 +109,7 @@ Feathers will setup a database connection to whichever database you select in th Feathers v5 Dove is still 100% compatible with database adapters for previous versions, so even though the generator doesn't directly set them up for you, you can manually add them using the instructions in each project. You can find additional Feathers-supported databases and other plugins by searching the [feathers-plugin tag on npm](https://www.npmjs.com/search?q=keywords:feathers-plugin). -If Feathers still supports the other database adapters, why are they not listed? Since we now have built-in support for Feathers Schemas, we chose to support the most popular databases by bringing low-level adapters into Feathers core. By "low-level" adapters, we mean the ones that run closest to the database, itself, without their own ORM or schema layer. For example, since KnexJS is a query builder for the popular SQL databases, we chose to bring it into core. ObjectionJS and Sequelize adapters can still be setup manually. Similarly, we brought the MongoDB adapter into core and no longer generate feathers-mongoose services, automatically. +If Feathers still supports the other database adapters, why are they not listed? Since we now have built-in support for Feathers Schemas, we chose to support the most popular databases by bringing low-level adapters into Feathers core. By "low-level" adapters, we mean the ones that run closest to the database, itself, without their own ORM or schema layer. For example, since KnexJS is a query builder for the popular SQL databases, we chose to bring it into core. ObjectionJS and Sequelize adapters can still be setup manually. Similarly, we brought the MongoDB adapter into core and no longer generate feathers-mongoose services, automatically. Our ultimate plan is to use the new generator (which we built from the ground up, ourselves) to bring back pluggable support for custom generators. In the future, each database adapter's repo will also host its generator code, along with custom generators for your own projects. @@ -118,7 +120,7 @@ Now, back to the generator. Once you've selected a database adapter you can move ### Connection String ```bash -? Enter your database connection string (mongodb://localhost:27017/my-app) +? Enter your database connection string (mongodb://localhost:27017/my-app) ``` Generally, you'll want to enter the connection string for the development database and not the production database. Production database connection strings most likely are safest in an environment variable. A development database usually doesn't require the same level of security. Depending on which database you selected, a default connection string will be presented. Press `enter` to accept it, or enter your own connection string. @@ -126,7 +128,7 @@ Generally, you'll want to enter the connection string for the development databa ### Authentication Method ```bash -? Which authentication methods do you want to use? Other methods and providers can be added at any time. (Press to select,
to toggle all, +? Which authentication methods do you want to use? Other methods and providers can be added at any time. (Press to select, to toggle all, to invert selection, and to proceed) ❯◉ Email + Password ◯ Google @@ -144,7 +146,7 @@ Choosing any of the other options will install the `@feathersjs/authentication-o ### Install dependencies -Once you've selected the final question, the generator will create the necessary files and then install packages. The output will look something like the below, but will vary a bit if you selected a package manager other than `npm`. +Once you've selected the final question, the generator will create the necessary files and then install packages. The output will look something like the below, but will vary a bit if you selected a package manager other than `npm`. ```bash Wrote file test/app.test.ts @@ -159,183 +161,10 @@ Once you've selected the final question, the generator will create the necessary Wrote file src/index.ts Wrote file src/logger.ts Wrote file package.json - Wrote file .prettierrc - Wrote file readme.md - Wrote file src/schemas/configuration.ts - Wrote file src/schemas/validators.ts - Wrote file src/services/index.ts - Wrote file tsconfig.json - Wrote file src/mongodb.ts - Updated src/app.ts + # Other files may show up here depending on your selections Running npm install --save -added 197 packages, and audited 198 packages in 19s - -24 packages are looking for funding - run `npm fund` for details +# ... found 0 vulnerabilities - Running npm install --save-dev - -added 168 packages, and audited 366 packages in 20s - -57 packages are looking for funding - run `npm fund` for details - -found 0 vulnerabilities -``` - - - -## The generated files - -Let's have a brief look at the files that have been generated: - - - - - -
- -* `config/` contains the configuration files for the app - * `default.json` contains the basic application configuration - * `production.json` files override `default.json` when in production mode by setting `NODE_ENV=production`. For details, see the [configuration API documentation](../../api/configuration.md) -* `node_modules/` our installed dependencies which are also added in the `package.json` -* `public/` contains static files to be served. A sample favicon and `index.html` (which will show up when going directly to the server URL) are already included. -* `src/` contains the Feathers server code. - * `hooks/` contains our custom [hooks](../basics/hooks.md) - * `services/` contains our [services](../basics/services.md) - * `users/` is a service that has been generated automatically to allow registering and authenticating users - * `users.class.ts` is the service class - * `users.hooks.ts` initializes Feathers hooks for this service - * `users.service.ts` registers this service on our Feathers application - * `middleware/` contains any [Express middleware](http://expressjs.com/en/guide/writing-middleware.html) - * `models/` contains database model files - * `users.model.ts` sets up our user collection for NeDB - * `app.ts` configures our Feathers application like we did in the [getting started chapter](../basics/starting.md) - * `app.hooks.ts` registers hooks that apply to every service - * `authentication.ts` sets up Feathers authentication system - * `channels.ts` sets up Feathers [event channels](../../api/channels.md) - * `declarations.ts` contains TypeScript declarations for our app - * `index.ts` loads and starts the application -* `test/` contains test files for the app, hooks and services - * `services/` has our service tests - * `users.test.ts` contains some basic tests for the `users` service - * `app.test.ts` tests that the index page appears, as well as 404 errors for HTML pages and JSON - * `authentication.test.ts` includes some tests that basic authentication works -* `.editorconfig` is an [EditorConfig](http://editorconfig.org/) setting to help developers define and maintain consistent coding styles among different editors and IDEs. -* `.gitignore` specifies [intentionally untracked files](https://git-scm.com/docs/gitignore) which [git](https://git-scm.com/), [GitHub](https://github.com/) and other similar projects ignore. -* `tsconfig.json` the TypeScript [compiler configuration](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html) -* `package.json` contains [information](https://docs.npmjs.com/files/package.json) about our NodeJS project like its name or dependencies. -* `README.md` has installation and usage instructions - - - - - -* `config/` contains the configuration files for the app - * `default.json` contains the basic application configuration - * `production.json` files override `default.json` when in production mode by setting `NODE_ENV=production`. For details, see the [configuration API documentation](../../api/configuration.md) -* `node_modules/` our installed dependencies which are also added in the `package.json` -* `public/` contains static files to be served. A sample favicon and `index.html` (which will show up when going directly to the server URL) are already included. -* `src/` contains the Feathers server code. - * `hooks/` contains our custom [hooks](../basics/hooks.md) - * `services/` contains our [services](../basics/services.md) - * `users/` is a service that has been generated automatically to allow registering and authenticating users - * `users.class.js` is the service class - * `users.hooks.js` initializes Feathers hooks for this service - * `users.service.js` registers this service on our Feathers application - * `middleware/` contains any [Express middleware](http://expressjs.com/en/guide/writing-middleware.html) - * `models/` contains database model files - * `users.model.js` sets up our user collection for NeDB - * `app.js` configures our Feathers application like we did in the [getting started chapter](../basics/starting.md) - * `app.hooks.js` registers hooks that apply to every service - * `authentication.js` sets up Feathers authentication system - * `channels.js` sets up Feathers [event channels](../../api/channels.md) - * `index.js` loads and starts the application -* `test/` contains test files for the app, hooks and services - * `services/` has our service tests - * `users.test.js` contains some basic tests for the `users` service - * `app.test.js` tests that the index page appears, as well as 404 errors for HTML pages and JSON - * `authentication.test.js` includes some tests that basic authentication works -* `.editorconfig` is an [EditorConfig](http://editorconfig.org/) setting to help developers define and maintain consistent coding styles among different editors and IDEs. -* `.eslintrc.json` contains defaults for linting your code with [ESLint](http://eslint.org/docs/user-guide/getting-started). -* `.gitignore` specifies [intentionally untracked files](https://git-scm.com/docs/gitignore) which [git](https://git-scm.com/), [GitHub](https://github.com/) and other similar projects ignore. -* `package.json` contains [information](https://docs.npmjs.com/files/package.json) about our NodeJS project like its name or dependencies. -* `README.md` has installation and usage instructions - - - - - -## Configure functions - -The most important pattern used in the generated application to split things up into individual files are _configure functions_ which are functions that are exported from a file and take the Feathers [app object](../../api/application.md) and then use it to e.g. register services. Those functions are then passed to [app.configure](../../api/application.md#configurecallback). - -For example, have a look at the following files: - - - - - -
- -`src/services/index.ts` looks like this: - -```ts -import { Application } from '../declarations'; -import users from './users/users.service'; -// Don't remove this comment. It's needed to format import lines nicely. - -export default function (app: Application) { - app.configure(users); -} -``` - -It uses another configure function exported from `src/services/users/users.service.ts`. The export from `src/services/index.js` is in turn used in `src/app.ts` as: - -```ts -// ... -import services from './services'; - -// ... -app.configure(authentication); -// Set up our services (see `services/index.js`) -app.configure(services); -// ... ``` - - - - - -`src/services/index.js` looks like this: - -```js -const users = require('./users/users.service.js'); -// eslint-disable-next-line no-unused-vars -module.exports = function (app) { - app.configure(users); -}; -``` - -It uses another configure function exported from `src/services/users/users.service.js`. The export from `src/services/index.js` is in turn used in `src/app.js` as: - -```js -// ... -const services = require('./services'); - -// ... -app.configure(authentication); -// Set up our services (see `services/index.js`) -app.configure(services); -// ... -``` - - - - - -This is how the generator splits things up into separate files and any documentation example that uses the `app` object can be used in a configure function. You can create your own files that export a configure function and `require`/`import` and `app.configure` them in `app.js`. - -> __Note:__ Keep in mind that the order in which configure functions are called might matter, e.g. if it is using a service, that service has to be registered first. From 0c2aaa5e694d487d08ba1b11a8cb8a1c03ae6374 Mon Sep 17 00:00:00 2001 From: daffl Date: Thu, 17 Nov 2022 19:52:51 -0800 Subject: [PATCH 06/25] Start directory and file based generator documentation --- docs/.vitepress/config.sidebar.ts | 177 ++++++++++++------ docs/guides/basics/schemas.md | 8 +- docs/guides/cli/authentication.md | 65 +++++++ docs/guides/cli/configuration.md | 44 +---- .../cli/custom-environment-variables.md | 21 +++ docs/guides/cli/databases.md | 54 ++++-- .../cli/{typescript.md => declarations.md} | 0 docs/guides/cli/default.json.md | 79 ++++++++ docs/guides/cli/hooks.md | 70 +++++++ docs/guides/cli/knexfile.md | 1 + docs/guides/cli/{logging.md => logger.md} | 0 docs/guides/cli/package.md | 1 + docs/guides/cli/prettierrc.md | 9 + docs/guides/cli/schemas.md | 25 +-- docs/guides/cli/services.md | 24 ++- docs/guides/cli/tsconfig.md | 1 + docs/guides/cli/validators.md | 2 +- packages/cli/package.json | 1 + packages/cli/src/app/templates/app.tpl.ts | 4 +- packages/cli/src/app/templates/config.tpl.ts | 84 --------- .../cli/src/app/templates/declarations.tpl.ts | 2 +- .../cli/src/app/templates/validators.tpl.ts | 2 +- .../templates/authentication.tpl.ts | 3 +- .../templates/schema.json.tpl.ts | 2 +- .../templates/schema.typebox.tpl.ts | 2 +- .../cli/src/connection/templates/knex.tpl.ts | 2 +- .../src/connection/templates/mongodb.tpl.ts | 2 +- packages/cli/src/hook/templates/hook.tpl.ts | 6 +- .../src/service/templates/schema.json.tpl.ts | 2 +- .../service/templates/schema.typebox.tpl.ts | 2 +- 30 files changed, 463 insertions(+), 232 deletions(-) create mode 100644 docs/guides/cli/custom-environment-variables.md rename docs/guides/cli/{typescript.md => declarations.md} (100%) create mode 100644 docs/guides/cli/default.json.md create mode 100644 docs/guides/cli/knexfile.md rename docs/guides/cli/{logging.md => logger.md} (100%) create mode 100644 docs/guides/cli/package.md create mode 100644 docs/guides/cli/prettierrc.md create mode 100644 docs/guides/cli/tsconfig.md delete mode 100644 packages/cli/src/app/templates/config.tpl.ts diff --git a/docs/.vitepress/config.sidebar.ts b/docs/.vitepress/config.sidebar.ts index 0b113e58f5..b534a4a940 100644 --- a/docs/.vitepress/config.sidebar.ts +++ b/docs/.vitepress/config.sidebar.ts @@ -65,6 +65,7 @@ export default { { text: 'Frontend', collapsible: true, + collapsed: false, items: [ { text: 'JavaScript', @@ -77,65 +78,125 @@ export default { ] }, { - text: 'CLI', + text: 'Generator', collapsible: true, - collapsed: true, + collapsed: false, items: [ { - text: 'Overview', + text: '📝 readme', link: '/guides/cli/index.md' }, - { - text: 'Generate App', - link: '/guides/cli/generate-app.md' - }, - { - text: 'Application', - link: '/guides/cli/application.md' - }, - { - text: 'Client', - link: '/guides/cli/client.md' - }, - { - text: 'Configuration', - link: '/guides/cli/configuration.md' - }, - { - text: 'TypeScript', - link: '/guides/cli/typescript.md' - }, - { - text: 'Validators', - link: '/guides/cli/validators.md' - }, - { - text: 'Logging', - link: '/guides/cli/logging.md' - }, - { - text: 'Databases', - link: '/guides/cli/databases.md' - }, - { - text: 'Authentication', - link: '/guides/cli/authentication.md' - }, - { - text: 'Services', - link: '/guides/cli/services.md' - }, - { - text: 'Schemas', - link: '/guides/cli/schemas.md' - }, - { - text: 'Hooks', - link: '/guides/cli/hooks.md' - }, - { - text: 'Channels', - link: '/guides/cli/channels.md' + // { + // text: 'Generate App', + // link: '/guides/cli/generate-app.md' + // }, + { + text: '📂 config', + items: [ + { + text: '📄 default.json', + link: '/guides/cli/default.json.md' + }, + { + text: '📄 custom-environment-variables.json', + link: '/guides/cli/custom-environment-variables.md' + } + ] + }, + { + text: '📂 hooks', + items: [ + { + text: '📄 Hooks', + link: '/guides/cli/hooks.md' + } + ] + }, + { + text: '📂 src', + items: [ + { + text: '📂 services', + items: [ + { + text: '📂 <service>', + items: [ + { + text: '📄 <service>', + link: '/guides/cli/services.md' + }, + { + text: '📄 <service>.class', + link: '/guides/cli/schemas.md' + }, + { + text: '📄 <service>.schema', + link: '/guides/cli/schemas.md' + } + ] + }, + { + text: '📄 index' + } + ] + }, + { + text: '📄 app', + link: '/guides/cli/application.md' + }, + { + text: '📄 authentication', + link: '/guides/cli/authentication.md' + }, + { + text: '📄 channels', + link: '/guides/cli/channels.md' + }, + { + text: '📄 client', + link: '/guides/cli/client.md' + }, + { + text: '📄 configuration', + link: '/guides/cli/configuration.md' + }, + { + text: '📄 declarations', + link: '/guides/cli/declarations.md' + }, + { + text: '📄 logger', + link: '/guides/cli/logger.md' + }, + { + text: '📄 validators', + link: '/guides/cli/validators.md' + }, + { + text: '📄 <database>', + link: '/guides/cli/databases.md' + } + ] + }, + { + text: '📂 test', + items: [] + }, + { + text: '📄 .prettierrc', + link: '/guides/cli/prettierrc.md' + }, + { + text: '📄 knexfile', + link: '/guides/cli/knexfile.md' + }, + { + text: '📄 package.json', + link: '/guides/cli/package.md' + }, + { + text: '📄 tsconfig.json', + link: '/guides/cli/tsconfig.md' } ] }, @@ -143,13 +204,13 @@ export default { text: 'Migrating', // collapsible: true, items: [ - { - text: 'Migration guide', - link: '/guides/migrating.md' - }, { text: "What's new?", link: '/guides/whats-new.md' + }, + { + text: 'Migration guide', + link: '/guides/migrating.md' } ] } diff --git a/docs/guides/basics/schemas.md b/docs/guides/basics/schemas.md index cd3a26f60f..629dd546f9 100644 --- a/docs/guides/basics/schemas.md +++ b/docs/guides/basics/schemas.md @@ -55,7 +55,7 @@ import type { Static } from '@feathersjs/typebox' import { passwordHash } from '@feathersjs/authentication-local' import type { HookContext } from '../../declarations' -import { dataValidator, queryValidator } from '../../schemas/validators' +import { dataValidator, queryValidator } from '../../validators' // Main data model schema export const userSchema = Type.Object( @@ -141,7 +141,7 @@ import type { Static } from '@feathersjs/typebox' import { passwordHash } from '@feathersjs/authentication-local' import type { HookContext } from '../../declarations' -import { dataValidator, queryValidator } from '../../schemas/validators' +import { dataValidator, queryValidator } from '../../validators' // Main data model schema export const userSchema = Type.Object( @@ -236,7 +236,7 @@ import { Type, getDataValidator, getValidator, querySyntax } from '@feathersjs/t import type { Static } from '@feathersjs/typebox' import type { HookContext } from '../../declarations' -import { dataValidator, queryValidator } from '../../schemas/validators' +import { dataValidator, queryValidator } from '../../validators' import { userSchema } from '../users/users.schema' // Main data model schema @@ -319,7 +319,7 @@ import { Type, getDataValidator, getValidator, querySyntax } from '@feathersjs/t import type { Static } from '@feathersjs/typebox' import type { HookContext } from '../../declarations' -import { dataValidator, queryValidator } from '../../schemas/validators' +import { dataValidator, queryValidator } from '../../validators' import { userSchema } from '../users/users.schema' // Main data model schema diff --git a/docs/guides/cli/authentication.md b/docs/guides/cli/authentication.md index 3e582cedfa..36a7b09796 100644 --- a/docs/guides/cli/authentication.md +++ b/docs/guides/cli/authentication.md @@ -3,3 +3,68 @@ outline: deep --- # Authentication + +The file in `src/authentication.ts` sets up an [authentication service](../../api/authentication/service.md) and registers [authentication strategies](../../api/authentication/strategy.md). Depending on the strategies you selected it looks similar to this: + +```ts +import { AuthenticationService, JWTStrategy } from '@feathersjs/authentication' +import { LocalStrategy } from '@feathersjs/authentication-local' + +import type { Application } from './declarations' + +declare module './declarations' { + interface ServiceTypes { + authentication: AuthenticationService + } +} + +export const authentication = (app: Application) => { + const authentication = new AuthenticationService(app) + + authentication.register('jwt', new JWTStrategy()) + authentication.register('local', new LocalStrategy()) + + app.use('authentication', authentication) +} +``` + +## Client tests + +If you selected a local strategy, `src/client.ts` will be updated with a client side integration test that looks similar to this: + +```ts +it('creates and authenticates a user with email and password', async () => { + const userData: userData = { + email: 'someone@example.com', + password: 'supersecret' + } + + await client.service('users').create(userData) + + const { user, accessToken } = await client.authenticate({ + strategy: 'local', + ...userData + }) + + assert.ok(accessToken, 'Created access token for user') + assert.ok(user, 'Includes user in authentication data') + assert.strictEqual(user.password, undefined, 'Password is hidden to clients') + + await client.logout() + + // Remove the test user on the server + await app.service('users').remove(user.id) +}) +``` + +This test will create a new user with the generated client, log in, verify a user was returned and log out again. To keep the test self-contained it will then remove the test user on the server + +
+ +Note that you can use `client` for client side interactions and the server side [application](./application.md#application) `app` object for server side calls in the same file. For example, if the user required an email verification but you don't want to test sending out emails you can call something like `app.service('users').patch(user.id, { verified: true })` to enable the new user on the server. + +
+ +## oAuth + +Note that when selecting oAuth logins (Google, Facebook, GitHub etc.), the standard registered oAuth strategy only uses the `Id` property to create a new user. This will fail validation against the default user [schema](./schemas.md) which requires an `email` property to exist. If the provider (and user) allows fetching the email, you can customize the oAuth strategy like shown for GitHub in the [oAuth authentication guide](../basics/authentication.md#login-with-github). You can also make the email in the schema optional with `email: Type.Optional(Type.String())`. diff --git a/docs/guides/cli/configuration.md b/docs/guides/cli/configuration.md index d296cf56f3..383f4c274c 100644 --- a/docs/guides/cli/configuration.md +++ b/docs/guides/cli/configuration.md @@ -2,43 +2,11 @@ outline: deep --- -# Configuration - -This page describes how the application loads its settings from [configuration files](#app-configuration) or [environment variables](#environment-variables) and how the generated file [folders](#folders) and [code style](#code-formatting) can be changed. - -## App configuration - -A generated application uses the **[configuration package](../../api/configuration.md)** to load configuration information based on the environment. It is based on the battle-tested and widely used [node-config](https://github.com/node-config/node-config) and loads configuration settings so that they are available via [app.get()](../../api/application.md#getname). It can also [validate the initial configuration against a schema](#config-schemas). - -
- -For more information on application configuration and schemas see the [configuration API documentation](../../api/configuration.md). - -
- -### Environment variables - -The most important environment variable for loading the configuration is `NODE_ENV`. For example, setting `NODE_ENV=development` (in a single command e.g. as `NODE_ENV=development npm run dev`) will first load `config/default.json` and then merge it with `config/development.json`. If no environment is set, `config/default.json` will be used. - -While `node-config` recommends to pass environment based configuration as a JSON string in a single `NODE_CONFIG` environment variable, it is also possible to use other environment variables via the `config/custom-environment-variables.json` file which looks like this by default: - -```json -{ - "port": { - "__name": "PORT", - "__format": "number" - }, - "host": "HOSTNAME" -} -``` - -This sets `app.get('port')` using the `PORT` environment variable (if it is available) parsing it as a number and `app.get('host')` from the `HOSTNAME` environment variable. - ### Configuration Schemas A generated application comes with a schema that validates the initial configuration when the application is started. This makes it much easier to catch configuration errors early which can otherwise be especially difficult to debug in remote environments. -The configuration [schema definition](../../api/schema/index.md) can be found in `schemas/configuration.ts`. It is used as a [configuration schema](../../api/configuration.md#configuration-validation) and loads some default schemas for authentication and database connection configuration and adds values for `host`, `port` and the `public` hosted file folder. The types of this schema are also used for `app.get()` and `app.set()` [typings](./typescript.md). The initial configuration schema will be validated on application startup when calling [`app.listen()`](../../api/application.md#listenport) or [`app.setup()`](../../api/application.md#setupserver). +The configuration [schema definition](../../api/schema/index.md) can be found in `configuration.ts`. It is used as a [configuration schema](../../api/configuration.md#configuration-validation) and loads some default schemas for authentication and database connection configuration and adds values for `host`, `port` and the `public` hosted file folder. The types of this schema are also used for `app.get()` and `app.set()` [typings](./typescript.md). The initial configuration schema will be validated on application startup when calling [`app.listen()`](../../api/application.md#listenport) or [`app.setup()`](../../api/application.md#setupserver). ## Folders @@ -52,13 +20,3 @@ The source and test folders to which files are generated is set in the `package. } } ``` - -## Code formatting - -The Feathers CLI uses [Prettier](https://prettier.io/) for code formatting and generates a configuration for it in a new application. To change the options, like the use of semicolons, quotes etc, edit the `.prettierrc` file with the [options available](https://prettier.io/docs/en/options.html). To update all existing source files with the new code style run - -``` -npm run prettier -``` - -Any new files generated will use current Prettier configuration. See the [Prettier Integration with Linters](https://prettier.io/docs/en/integrating-with-linters.html) documentation for how to integrate with tools like ESLint. diff --git a/docs/guides/cli/custom-environment-variables.md b/docs/guides/cli/custom-environment-variables.md new file mode 100644 index 0000000000..fcc15b17d1 --- /dev/null +++ b/docs/guides/cli/custom-environment-variables.md @@ -0,0 +1,21 @@ +# Custom Environment Variables + +While `node-config` used for [application configuration](./default.json.md) recommends to pass environment based configuration as a JSON string in a single `NODE_CONFIG` environment variable, it is also possible to use other environment variables via the `config/custom-environment-variables.json` file which looks like this by default: + +```json +{ + "port": { + "__name": "PORT", + "__format": "number" + }, + "host": "HOSTNAME" +} +``` + +This sets `app.get('port')` using the `PORT` environment variable (if it is available) parsing it as a number and `app.get('host')` from the `HOSTNAME` environment variable. + +
+ +See the [node-config custom envrionment variable](https://github.com/node-config/node-config/wiki/Environment-Variables#custom-environment-variables) documentation for more information. + +
diff --git a/docs/guides/cli/databases.md b/docs/guides/cli/databases.md index cdbf19fd7b..e4fe0c317f 100644 --- a/docs/guides/cli/databases.md +++ b/docs/guides/cli/databases.md @@ -4,9 +4,9 @@ outline: deep # Databases -## SQL +## Connection -### Connection + Depending on the SQL database you selected, a `src/.ts` file will be created that sets up a connection using [KnexJS](../../api/databases/knex.md). It uses the connection settings from the `` [configuration value](./config.md#app-configuration) and exports a [configure function](./application.md#configure-functions) that initializes the database connection. The Knex connection object is then acessible wherever you have access to the [app object](./application.md) via @@ -14,7 +14,27 @@ Depending on the SQL database you selected, a `src/.ts` file will be c const knex = app.get('Client') ``` -### Migrations + + + + +`src/mongodb.ts` exports a [configure function](./application.md#configure-functions) that connects to the MongoDB connection string set as `mongodb` in your [configuration](./configuration.md#app-configuration). The [MongoDB NodeJS client](https://www.mongodb.com/languages/mongodb-with-nodejs) is then accessible wherever you have access to the [app object](./application.md) via + +```ts +const db = await app.get('mongodbClient') +``` + +The default connection string tries to connect to a local MongoDB instance with no password. To use e.g. [MongoDB Atlas](https://www.mongodb.com/cloud) change the `mongodb` property in `config/default.json` or add it as an [environment variable](./configuration.md#environment-variables) with the connection string that will look similar to this: + +``` +mongodb+srv://:@cluster0.xyz.mongodb.net/?retryWrites=true&w=majority +``` + + + +## Migrations + + Migrations are a best practise for SQL databases to roll out and undo changes to the data model and are set up automatically with an SQL database connection. The generated `knexfile.ts` imports the [app object](./application.md) to establish the connection to the database. To run migration scripts for the connection from the [configuration environment](./configuration.md#environment-variables) use: @@ -36,27 +56,27 @@ For more information on what is available in migration files, see the [Knex migr -### Models + -KnexJS does not have a concept of models. Instead a new service is initialized with the table name and `app.get('Client')` as the connection. For more information on how to create custom queries and more, see the [SQL database adapter API documentation](../../api/databases/knex.md). + -## MongoDB +
-### Connection +Migrations are not necessary for MongoDB. -`src/mongodb.ts` exports a [configure function](./application.md#configure-functions) that connects to the MongoDB connection string set as `mongodb` in your [configuration](./config.md#app-configuration). The [MongoDB NodeJS client](https://www.mongodb.com/languages/mongodb-with-nodejs) is then accessible wherever you have access to the [app object](./application.md) via +
-```ts -const db = await app.get('mongodbClient') -``` +
-The default connection string tries to connect to a local MongoDB instance with no password. To use e.g. [MongoDB Atlas](https://www.mongodb.com/cloud) change the `mongodb` property in `config/default.json` or add it as an [environment variable](./configuration.md#environment-variables) with the connection string that will look similar to this: +## Models -``` -mongodb+srv://:@cluster0.xyz.mongodb.net/?retryWrites=true&w=majority -``` + -### Models +KnexJS does not have a concept of models. Instead a new service is initialized with the table name and `app.get('Client')` as the connection. For more information on how to create custom queries and more, see the [SQL database adapter API documentation](../../api/databases/knex.md). + + + + The collection for a MongoDB service can be accessed via @@ -65,3 +85,5 @@ const userCollection = await app.service('users').Model ``` See the [MongoDB service API documentation](../../api/databases/mongodb.md) for more information. + + diff --git a/docs/guides/cli/typescript.md b/docs/guides/cli/declarations.md similarity index 100% rename from docs/guides/cli/typescript.md rename to docs/guides/cli/declarations.md diff --git a/docs/guides/cli/default.json.md b/docs/guides/cli/default.json.md new file mode 100644 index 0000000000..20cba6db0f --- /dev/null +++ b/docs/guides/cli/default.json.md @@ -0,0 +1,79 @@ +--- +outline: deep +--- + +# Application configuration + +A generated application uses the **[configuration module](../../api/configuration.md)** to load configuration information based on the environment. It is based on the battle-tested and widely used [node-config](https://github.com/node-config/node-config) and loads configuration settings so that they are available via [app.get()](../../api/application.md#getname). + +
+ +For more information on application configuration and schemas see the [configuration API documentation](../../api/configuration.md). + +
+ +## Environments + +The `NODE_ENV` environment variable determines which configuration file is used. For example, setting `NODE_ENV=development` (in a single command e.g. as `NODE_ENV=development npm run dev`) will first load `config/default.json` and then merge it with `config/development.json`. If no environment is set, `config/default.json` will be used. + +## Default configuration + +The application uses the following configuration values. + +### host, port, public + +These options are used directly in the generated application + +- `host` - Is the hostname of the API server +- `port` - The port it listens on +- `public` - The name of the folder static assets are hosted in + +### paginate + +`paginate` sets the default and maximum page size when using [pagination](../../api/databases/common.md#pagination) with a [database service](../../api/databases/adapters.md). + +```json +{ + "paginate": { + "default": 10, + "max": 100 + } +} +``` + +### origins + +`origins` contains a list of frontend URLs that requests can be made from. This is used to configure cross origin (CORS) policies and oAuth (Twitter, Facebook etc.) login redirects. For example to develop locally with a [create-react-app](https://create-react-app.dev/) frontend and deploy to `app.feathersjs.com`: + +```json +{ + "origins": ["http://localhost:3030", "http://localhost:5000", "https://app.feathersjs.com"] +} +``` + +### authentication + +`authentication` contains the configuration for the authentication service and strategies. See the [authentication service configuration](../../api/authentication/service.md#configuration) for more information. For strategy specific settings refer to the [jwt](../../api/authentication/jwt.md#options), [local](../../api/authentication/local.md#options) and [oAuth](../../api/authentication/oauth.md#options) API documentation. + +### Databases + + + +Depending on the SQL database selected the `` setting contains a `connection` with the database driver package name and a `client` option with the database connection string. + +```json +{ + "postgresql": { + "connection": "pg", + "client": "postgres://postgres:@localhost:5432/feathers-chat" + } +} +``` + + + + + +When selecting MongoDB, the `mongodb` setting contains the MongoDB connection string. + + diff --git a/docs/guides/cli/hooks.md b/docs/guides/cli/hooks.md index 6aac6e934f..8eea8ba233 100644 --- a/docs/guides/cli/hooks.md +++ b/docs/guides/cli/hooks.md @@ -3,3 +3,73 @@ outline: deep --- # Hooks + +## Generating a hook + +A new hook can be generated via + +``` +npx feathers generate hook +``` + +## Hook name + +The hook generator will first ask for a name. Based on the name it will create a kebab-cased filename in the `hooks/` folder that exports a camelCased hook function. For example a name of `my fancy Hook` will create a `src/my-fancy-hook.ts` file that exports a `myFancyHook` [hook function](../../api/hooks.md#hook-functions). + +## Hook types + +There are two hook types that can be generated. + +
+ +For more information see the [hooks API documentation](../../api/hooks.md). + +
+ +### Around hooks + +[Around hooks](../../api/hooks.md#around) allow to control the entire `before`, `after` and `error` flow in a single function. An `around` hook is an `async` function that accepts two arguments: + +- The [hook context](../../api/hooks.md#hook-context) +- An asynchronous `next` function. Somewhere in the body of the hook function, there is a call to `await next()`, which calls the `next` hooks OR the original function if all other hooks have run. + +```ts +import type { HookContext, NextFunction } from '../declarations' + +export const myFancyHook = async (context: HookContext, next: NextFunction) => { + console.log(`Running hook ${name} on ${context.path}.${context.method}`) + await next() + // Do things after here +} +``` + +You can wrap the `await next()` in a `try/catch` block to also handle errors. + +### Before, after, error + +[Before, after or error hooks](../../api/hooks.md#before-after-and-error) are `async` functions that take the [hook context](#hook-context) as the parameter. + +```ts +import type { HookContext } from '../declarations' + +export const myFancyHook = async (context: HookContext) => { + console.log(`Running hook ${name} on ${context.path}.${context.method}`) +} +``` + +## Context types + +If the hook is for a specific service, you can pass the service as a generic to the [HookContext](./typescript.md#hook-context) type which will give you the correct types for [context.data](../../api/hooks.md#contextdata), [context.result](../../api/hooks.md#contextresult) and [context.params](../../api/hooks.md#contextparams): + +```ts +import type { UserService } from '../services/users/users' +import type { HookContext } from '../declarations' + +export const myFancyUserHook = async (context: HookContext) => { + console.log(`Running hook ${name} on ${context.path}.${context.method}`) +} +``` + +## Registering hooks + +A generated hook can be registered as an [application hook](./application.md#application-hooks) or as a [service hook](./services.md#registering-hooks). Also see the [hook registration API documentation](../../api/hooks.md#registering-hooks). diff --git a/docs/guides/cli/knexfile.md b/docs/guides/cli/knexfile.md new file mode 100644 index 0000000000..bc9ed2af09 --- /dev/null +++ b/docs/guides/cli/knexfile.md @@ -0,0 +1 @@ +# Knexfile diff --git a/docs/guides/cli/logging.md b/docs/guides/cli/logger.md similarity index 100% rename from docs/guides/cli/logging.md rename to docs/guides/cli/logger.md diff --git a/docs/guides/cli/package.md b/docs/guides/cli/package.md new file mode 100644 index 0000000000..f4a354d6c0 --- /dev/null +++ b/docs/guides/cli/package.md @@ -0,0 +1 @@ +# package.json diff --git a/docs/guides/cli/prettierrc.md b/docs/guides/cli/prettierrc.md new file mode 100644 index 0000000000..e1ce9ef800 --- /dev/null +++ b/docs/guides/cli/prettierrc.md @@ -0,0 +1,9 @@ +# Prettier + +The Feathers CLI uses [Prettier](https://prettier.io/) for code formatting and generates a configuration for it in a new application. To change the options, like the use of semicolons, quotes etc, edit the `.prettierrc` file with the [options available](https://prettier.io/docs/en/options.html). To update all existing source files with the new code style run + +``` +npm run prettier +``` + +When new files are generated, they will use the current Prettier configuration. See the [Prettier Integration with Linters](https://prettier.io/docs/en/integrating-with-linters.html) documentation for how to integrate with tools like ESLint. diff --git a/docs/guides/cli/schemas.md b/docs/guides/cli/schemas.md index f4d9227b4c..213b850341 100644 --- a/docs/guides/cli/schemas.md +++ b/docs/guides/cli/schemas.md @@ -4,7 +4,7 @@ outline: deep # Service Schemas and Resolvers -Give a tour of the generated schemas and resolvers for a service. +Give a tour of the generated schemas and resolvers for a service. ## Main Schemas and Resolvers @@ -13,13 +13,13 @@ Give a tour of the generated schemas and resolvers for a service. export const messagesSchema = Type.Object( { _id: objectId, - text: Type.String(), + text: Type.String() }, - { $id: 'Messages', additionalProperties: false }, + { $id: 'Messages', additionalProperties: false } ) export type Messages = Static export const messagesResolver = resolve({ - properties: {}, + properties: {} }) ``` @@ -28,7 +28,7 @@ export const messagesResolver = resolve({ ```ts // External resolvers https://dove.feathersjs.com/guides/cli/schemas-and-resolvers.html#external-resolvers export const messagesExternalResolver = resolve({ - properties: {}, + properties: {} }) ``` @@ -38,25 +38,28 @@ export const messagesExternalResolver = resolve({ // Schema for creating new entries https://dove.feathersjs.com/guides/cli/schemas-and-resolvers.html#data-schema-and-resolvers export const messagesDataSchema = Type.Pick(messagesSchema, ['string'], { $id: 'MessagesData', - additionalProperties: false, + additionalProperties: false }) export type MessagesData = Static export const messagesDataValidator = getDataValidator(messagesDataSchema, dataValidator) export const messagesDataResolver = resolve({ - properties: {}, + properties: {} }) ``` +### create, patch and update + ## Query Schema and Resolvers ```ts // Schema for allowed query properties https://dove.feathersjs.com/guides/cli/schemas-and-resolvers.html#query-schema-and-resolvers -export const messagesQueryProperties = Type.Pick(messagesSchema, ['_id', 'name'], { additionalProperties: false }) +export const messagesQueryProperties = Type.Pick(messagesSchema, ['_id', 'name'], { + additionalProperties: false +}) export const messagesQuerySchema = querySyntax(messagesQueryProperties) export type MessagesQuery = Static export const messagesQueryValidator = getValidator(messagesQuerySchema, queryValidator) export const messagesQueryResolver = resolve({ - properties: {}, + properties: {} }) - -``` \ No newline at end of file +``` diff --git a/docs/guides/cli/services.md b/docs/guides/cli/services.md index 0da9e8f8cd..80419c8f92 100644 --- a/docs/guides/cli/services.md +++ b/docs/guides/cli/services.md @@ -2,7 +2,27 @@ outline: deep --- -# Service Class +# Service + +## Service class + +### Database services + +### Custom services + +## Service file + +### Registration + +### Custom methods + +### Hooks + +## TypeScript + +### Generics + +### Registration Link here from the class file in the generated app: @@ -32,7 +52,7 @@ export const getOptions = (app: Application) => { } ``` -## Hooks Overview +## Registering hooks Where to link for the comment above the service hooks?: diff --git a/docs/guides/cli/tsconfig.md b/docs/guides/cli/tsconfig.md new file mode 100644 index 0000000000..f612bb2378 --- /dev/null +++ b/docs/guides/cli/tsconfig.md @@ -0,0 +1 @@ +# tsconfig.json diff --git a/docs/guides/cli/validators.md b/docs/guides/cli/validators.md index b6a9fb20e1..f99d1bfd24 100644 --- a/docs/guides/cli/validators.md +++ b/docs/guides/cli/validators.md @@ -8,7 +8,7 @@ For all currently supported schema types, AJV is used as the default validator. ## AJV validators -The `src/schemas/validators.ts` file sets up two Ajv instances for data and querys (for which string types will be coerced automatically). It also sets up a collection of additional formats using [ajv-formats](https://ajv.js.org/packages/ajv-formats.html). The validators in this file can be customized according to the [Ajv documentation](https://ajv.js.org/) and [its plugins](https://ajv.js.org/packages/). You can find the available Ajv options in the [Ajs class API docs](https://ajv.js.org/options.html). +The `src/validators.ts` file sets up two Ajv instances for data and querys (for which string types will be coerced automatically). It also sets up a collection of additional formats using [ajv-formats](https://ajv.js.org/packages/ajv-formats.html). The validators in this file can be customized according to the [Ajv documentation](https://ajv.js.org/) and [its plugins](https://ajv.js.org/packages/). You can find the available Ajv options in the [Ajs class API docs](https://ajv.js.org/options.html). ```ts import { Ajv, addFormats } from '@feathersjs/schema' diff --git a/packages/cli/package.json b/packages/cli/package.json index f23000d58c..536ae3bacc 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -37,6 +37,7 @@ "LICENSE", "README.md", "lib/**", + "lib/app/static/.gitignore", "bin/**", "*.d.ts", "*.js" diff --git a/packages/cli/src/app/templates/app.tpl.ts b/packages/cli/src/app/templates/app.tpl.ts index 5c105b3ec5..b0fc4087ad 100644 --- a/packages/cli/src/app/templates/app.tpl.ts +++ b/packages/cli/src/app/templates/app.tpl.ts @@ -13,7 +13,7 @@ import { ${transports.includes('websockets') ? "import socketio from '@feathersjs/socketio'" : ''} import type { Application } from './declarations' -import { configurationValidator } from './schemas/configuration' +import { configurationValidator } from './configuration' import { logError } from './hooks/log-error' import { services } from './services/index' import { channels } from './channels' @@ -74,7 +74,7 @@ import configuration from '@feathersjs/configuration' ${transports.includes('websockets') ? "import socketio from '@feathersjs/socketio'" : ''} import type { Application } from './declarations' -import { configurationValidator } from './schemas/configuration' +import { configurationValidator } from './configuration' import { logger } from './logger' import { logError } from './hooks/log-error' import { services } from './services/index' diff --git a/packages/cli/src/app/templates/config.tpl.ts b/packages/cli/src/app/templates/config.tpl.ts deleted file mode 100644 index 88e806d353..0000000000 --- a/packages/cli/src/app/templates/config.tpl.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { generator, toFile, writeJSON } from '@feathershq/pinion' -import { renderSource } from '../../commons' -import { AppGeneratorContext } from '../index' - -const defaultConfig = ({}: AppGeneratorContext) => ({ - host: 'localhost', - port: 3030, - public: './public/', - origins: ['http://localhost:3030'], - paginate: { - default: 10, - max: 50 - } -}) - -const customEnvironment = { - port: { - __name: 'PORT', - __format: 'number' - }, - host: 'HOSTNAME' -} - -const testConfig = { - port: 8998 -} - -const configurationJsonTemplate = - ({}: AppGeneratorContext) => /* ts */ `// For more information about this file see https://dove.feathersjs.com/guides/cli/config.html#configuration-schemas -import { defaultAppSettings, getValidator } from '@feathersjs/schema' -import type { FromSchema } from '@feathersjs/schema' - -import { dataValidator } from './validators' - -export const configurationSchema = { - type: 'object', - additionalProperties: false, - required: [ 'host', 'port', 'public' ], - properties: { - ...defaultAppSettings, - host: { type: 'string' }, - port: { type: 'number' }, - public: { type: 'string' } - } -} as const - -export const configurationValidator = getValidator(configurationSchema, dataValidator) - -export type ApplicationConfiguration = FromSchema -` - -const configurationTypeboxTemplate = - ({}: AppGeneratorContext) => /* ts */ `// For more information about this file see https://dove.feathersjs.com/guides/cli/config.html#configuration-schemas - import { Type, getValidator, defaultAppConfiguration } from '@feathersjs/typebox' -import type { Static } from '@feathersjs/typebox' - -import { dataValidator } from './validators' - -export const configurationSchema = Type.Intersect([ - defaultAppConfiguration, - Type.Object({ - host: Type.String(), - port: Type.Number(), - public: Type.String() - }) -]) - -export type ApplicationConfiguration = Static - -export const configurationValidator = getValidator(configurationSchema, dataValidator) -` - -export const generate = (ctx: AppGeneratorContext) => - generator(ctx) - .then(writeJSON(defaultConfig, toFile('config', 'default.json'))) - .then(writeJSON(testConfig, toFile('config', 'test.json'))) - .then(writeJSON(customEnvironment, toFile('config', 'custom-environment-variables.json'))) - .then( - renderSource( - async (ctx) => - ctx.schema === 'typebox' ? configurationTypeboxTemplate(ctx) : configurationJsonTemplate(ctx), - toFile(({ lib }) => lib, 'schemas', 'configuration') - ) - ) diff --git a/packages/cli/src/app/templates/declarations.tpl.ts b/packages/cli/src/app/templates/declarations.tpl.ts index f453e2c587..1e90fef7a9 100644 --- a/packages/cli/src/app/templates/declarations.tpl.ts +++ b/packages/cli/src/app/templates/declarations.tpl.ts @@ -6,7 +6,7 @@ const template = ({ }: AppGeneratorContext) => /* ts */ `// For more information about this file see https://dove.feathersjs.com/guides/cli/typescript.html import { HookContext as FeathersHookContext, NextFunction } from '@feathersjs/feathers' import { Application as FeathersApplication } from '@feathersjs/${framework}' -import { ApplicationConfiguration } from './schemas/configuration' +import { ApplicationConfiguration } from './configuration' export { NextFunction } diff --git a/packages/cli/src/app/templates/validators.tpl.ts b/packages/cli/src/app/templates/validators.tpl.ts index 72ac51942e..2464d364a0 100644 --- a/packages/cli/src/app/templates/validators.tpl.ts +++ b/packages/cli/src/app/templates/validators.tpl.ts @@ -34,6 +34,6 @@ export const generate = (ctx: AppGeneratorContext) => generator(ctx).then( renderSource( validatorTemplate, - toFile(({ lib }) => lib, 'schemas', 'validators') + toFile(({ lib }) => lib, 'validators') ) ) diff --git a/packages/cli/src/authentication/templates/authentication.tpl.ts b/packages/cli/src/authentication/templates/authentication.tpl.ts index 8bddc5f946..2ff901b5d7 100644 --- a/packages/cli/src/authentication/templates/authentication.tpl.ts +++ b/packages/cli/src/authentication/templates/authentication.tpl.ts @@ -4,7 +4,8 @@ import { AuthenticationGeneratorContext, localTemplate, oauthTemplate } from '.. const template = ({ authStrategies -}: AuthenticationGeneratorContext) => /* ts */ `import { AuthenticationService, JWTStrategy } from '@feathersjs/authentication' +}: AuthenticationGeneratorContext) => /* ts */ `// For more information about this file see https://dove.feathersjs.com/guides/cli/authentication.html +import { AuthenticationService, JWTStrategy } from '@feathersjs/authentication' ${localTemplate(authStrategies, `import { LocalStrategy } from '@feathersjs/authentication-local'`)} ${oauthTemplate(authStrategies, `import { oauth, OAuthStrategy } from '@feathersjs/authentication-oauth'`)} diff --git a/packages/cli/src/authentication/templates/schema.json.tpl.ts b/packages/cli/src/authentication/templates/schema.json.tpl.ts index a92ceb62da..9a461eb2b8 100644 --- a/packages/cli/src/authentication/templates/schema.json.tpl.ts +++ b/packages/cli/src/authentication/templates/schema.json.tpl.ts @@ -13,7 +13,7 @@ import type { FromSchema } from '@feathersjs/schema' ${localTemplate(authStrategies, `import { passwordHash } from '@feathersjs/authentication-local'`)} import type { HookContext } from '${relative}/declarations' -import { dataValidator, queryValidator } from '${relative}/schemas/validators' +import { dataValidator, queryValidator } from '${relative}/validators' // Main data model schema export const ${camelName}Schema = { diff --git a/packages/cli/src/authentication/templates/schema.typebox.tpl.ts b/packages/cli/src/authentication/templates/schema.typebox.tpl.ts index e8ffa8ca59..6e9e3e66b6 100644 --- a/packages/cli/src/authentication/templates/schema.typebox.tpl.ts +++ b/packages/cli/src/authentication/templates/schema.typebox.tpl.ts @@ -14,7 +14,7 @@ import type { Static } from '@feathersjs/typebox' ${localTemplate(authStrategies, `import { passwordHash } from '@feathersjs/authentication-local'`)} import type { HookContext } from '${relative}/declarations' -import { dataValidator, queryValidator } from '${relative}/schemas/validators' +import { dataValidator, queryValidator } from '${relative}/validators' // Main data model schema export const ${camelName}Schema = Type.Object({ diff --git a/packages/cli/src/connection/templates/knex.tpl.ts b/packages/cli/src/connection/templates/knex.tpl.ts index a2ac0d630d..6351a4ce2c 100644 --- a/packages/cli/src/connection/templates/knex.tpl.ts +++ b/packages/cli/src/connection/templates/knex.tpl.ts @@ -27,7 +27,7 @@ const knexfile = ({ lib, language, database -}: ConnectionGeneratorContext) => /* ts */ `// For more information about this file see https://dove.feathersjs.com/guides/cli/databases.html#sql +}: ConnectionGeneratorContext) => /* ts */ `// For more information about this file see https://dove.feathersjs.com/guides/cli/databases.html import { app } from './${lib}/app' // Load our database connection info from the app configuration diff --git a/packages/cli/src/connection/templates/mongodb.tpl.ts b/packages/cli/src/connection/templates/mongodb.tpl.ts index af55d38529..f83020452b 100644 --- a/packages/cli/src/connection/templates/mongodb.tpl.ts +++ b/packages/cli/src/connection/templates/mongodb.tpl.ts @@ -3,7 +3,7 @@ import { ConnectionGeneratorContext } from '../index' import { injectSource, renderSource } from '../../commons' const template = - ({}: ConnectionGeneratorContext) => /* ts */ `// For more information about this file see https://dove.feathersjs.com/guides/cli/databases.html#mongodb + ({}: ConnectionGeneratorContext) => /* ts */ `// For more information about this file see https://dove.feathersjs.com/guides/cli/databases.html import { MongoClient } from 'mongodb' import type { Db } from 'mongodb' import type { Application } from './declarations' diff --git a/packages/cli/src/hook/templates/hook.tpl.ts b/packages/cli/src/hook/templates/hook.tpl.ts index 8671e9d980..500fc4195b 100644 --- a/packages/cli/src/hook/templates/hook.tpl.ts +++ b/packages/cli/src/hook/templates/hook.tpl.ts @@ -5,7 +5,8 @@ import { renderSource } from '../../commons' const aroundTemplate = ({ camelName, name -}: HookGeneratorContext) => /* ts */ `import type { HookContext, NextFunction } from '../declarations' +}: HookGeneratorContext) => /* ts */ `// For more information about this file see https://dove.feathersjs.com/guides/cli/hooks.html +import type { HookContext, NextFunction } from '../declarations' export const ${camelName} = async (context: HookContext, next: NextFunction) => { console.log(\`Running hook ${name} on \${context.path}\.\${context.method}\`) @@ -16,7 +17,8 @@ export const ${camelName} = async (context: HookContext, next: NextFunction) => const regularTemplate = ({ camelName, name -}: HookGeneratorContext) => /* ts */ `import type { HookContext } from '../declarations' +}: HookGeneratorContext) => /* ts */ `// For more information about this file see https://dove.feathersjs.com/guides/cli/hooks.html +import type { HookContext } from '../declarations' export const ${camelName} = async (context: HookContext) => { console.log(\`Running hook ${name} on \${context.path}\.\${context.method}\`) diff --git a/packages/cli/src/service/templates/schema.json.tpl.ts b/packages/cli/src/service/templates/schema.json.tpl.ts index f713559151..3053192441 100644 --- a/packages/cli/src/service/templates/schema.json.tpl.ts +++ b/packages/cli/src/service/templates/schema.json.tpl.ts @@ -11,7 +11,7 @@ const template = ({ import type { FromSchema } from '@feathersjs/schema' import type { HookContext } from '${relative}/declarations' -import { dataValidator, queryValidator } from '${relative}/schemas/validators' +import { dataValidator, queryValidator } from '${relative}/validators' // Main data model schema export const ${camelName}Schema = { diff --git a/packages/cli/src/service/templates/schema.typebox.tpl.ts b/packages/cli/src/service/templates/schema.typebox.tpl.ts index 761472a105..ccbd5c101d 100644 --- a/packages/cli/src/service/templates/schema.typebox.tpl.ts +++ b/packages/cli/src/service/templates/schema.typebox.tpl.ts @@ -12,7 +12,7 @@ import { Type, getDataValidator, getValidator, querySyntax } from '@feathersjs/t import type { Static } from '@feathersjs/typebox' import type { HookContext } from '${relative}/declarations' -import { dataValidator, queryValidator } from '${relative}/schemas/validators' +import { dataValidator, queryValidator } from '${relative}/validators' // Main data model schema export const ${camelName}Schema = Type.Object({ From eeca6e2f5bf404975c31c757145c957663a1448b Mon Sep 17 00:00:00 2001 From: daffl Date: Thu, 17 Nov 2022 20:13:25 -0800 Subject: [PATCH 07/25] Add and fix missing template file --- .../src/app/templates/configuration.tpl.ts | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 packages/cli/src/app/templates/configuration.tpl.ts diff --git a/packages/cli/src/app/templates/configuration.tpl.ts b/packages/cli/src/app/templates/configuration.tpl.ts new file mode 100644 index 0000000000..5f9173bd14 --- /dev/null +++ b/packages/cli/src/app/templates/configuration.tpl.ts @@ -0,0 +1,82 @@ +import { generator, toFile, writeJSON } from '@feathershq/pinion' +import { renderSource } from '../../commons' +import { AppGeneratorContext } from '../index' + +const defaultConfig = ({}: AppGeneratorContext) => ({ + host: 'localhost', + port: 3030, + public: './public/', + origins: ['http://localhost:3030'], + paginate: { + default: 10, + max: 50 + } +}) + +const customEnvironment = { + port: { + __name: 'PORT', + __format: 'number' + }, + host: 'HOSTNAME' +} + +const testConfig = { + port: 8998 +} + +const configurationJsonTemplate = + ({}: AppGeneratorContext) => /* ts */ `import { defaultAppSettings, getValidator } from '@feathersjs/schema' +import type { FromSchema } from '@feathersjs/schema' + +import { dataValidator } from './validators' + +export const configurationSchema = { + type: 'object', + additionalProperties: false, + required: [ 'host', 'port', 'public' ], + properties: { + ...defaultAppSettings, + host: { type: 'string' }, + port: { type: 'number' }, + public: { type: 'string' } + } +} as const + +export const configurationValidator = getValidator(configurationSchema, dataValidator) + +export type ApplicationConfiguration = FromSchema +` + +const configurationTypeboxTemplate = + ({}: AppGeneratorContext) => /* ts */ `import { Type, getValidator, defaultAppConfiguration } from '@feathersjs/typebox' +import type { Static } from '@feathersjs/typebox' + +import { dataValidator } from './validators' + +export const configurationSchema = Type.Intersect([ + defaultAppConfiguration, + Type.Object({ + host: Type.String(), + port: Type.Number(), + public: Type.String() + }) +]) + +export type ApplicationConfiguration = Static + +export const configurationValidator = getValidator(configurationSchema, dataValidator) +` + +export const generate = (ctx: AppGeneratorContext) => + generator(ctx) + .then(writeJSON(defaultConfig, toFile('config', 'default.json'))) + .then(writeJSON(testConfig, toFile('config', 'test.json'))) + .then(writeJSON(customEnvironment, toFile('config', 'custom-environment-variables.json'))) + .then( + renderSource( + async (ctx) => + ctx.schema === 'typebox' ? configurationTypeboxTemplate(ctx) : configurationJsonTemplate(ctx), + toFile(({ lib }) => lib, 'configuration') + ) + ) From f7e2575c3f231f685c0f7aa9482ffb77ebd18d75 Mon Sep 17 00:00:00 2001 From: daffl Date: Fri, 18 Nov 2022 13:34:21 -0800 Subject: [PATCH 08/25] More in-depth generator docs --- docs/.vitepress/config.sidebar.ts | 32 +++-- docs/guides/cli/{application.md => app.md} | 0 docs/guides/cli/{hooks.md => hook.md} | 43 ++++++ docs/guides/cli/log-error.md | 3 + docs/guides/cli/logger.md | 47 ------ docs/guides/cli/service.class.md | 135 ++++++++++++++++++ docs/guides/cli/service.md | 87 +++++++++++ .../cli/{schemas.md => service.schemas.md} | 29 +++- docs/guides/cli/services.md | 101 ------------- packages/cli/src/hook/templates/hook.tpl.ts | 4 +- .../src/service/templates/schema.json.tpl.ts | 3 +- .../service/templates/schema.typebox.tpl.ts | 3 +- .../cli/src/service/templates/service.tpl.ts | 2 +- packages/cli/src/service/type/custom.tpl.ts | 8 +- packages/cli/src/service/type/knex.tpl.ts | 6 +- packages/cli/src/service/type/mongodb.tpl.ts | 3 +- 16 files changed, 334 insertions(+), 172 deletions(-) rename docs/guides/cli/{application.md => app.md} (100%) rename docs/guides/cli/{hooks.md => hook.md} (74%) create mode 100644 docs/guides/cli/log-error.md create mode 100644 docs/guides/cli/service.class.md create mode 100644 docs/guides/cli/service.md rename docs/guides/cli/{schemas.md => service.schemas.md} (63%) delete mode 100644 docs/guides/cli/services.md diff --git a/docs/.vitepress/config.sidebar.ts b/docs/.vitepress/config.sidebar.ts index b534a4a940..49e6b6776f 100644 --- a/docs/.vitepress/config.sidebar.ts +++ b/docs/.vitepress/config.sidebar.ts @@ -103,18 +103,22 @@ export default { } ] }, - { - text: '📂 hooks', - items: [ - { - text: '📄 Hooks', - link: '/guides/cli/hooks.md' - } - ] - }, { text: '📂 src', items: [ + { + text: '📂 hooks', + items: [ + { + text: '📄 <hook>', + link: '/guides/cli/hook.md' + }, + { + text: '📄 log-error', + link: '/guides/cli/log-error.md' + } + ] + }, { text: '📂 services', items: [ @@ -123,15 +127,15 @@ export default { items: [ { text: '📄 <service>', - link: '/guides/cli/services.md' + link: '/guides/cli/service.md' }, { text: '📄 <service>.class', - link: '/guides/cli/schemas.md' + link: '/guides/cli/service.class.md' }, { - text: '📄 <service>.schema', - link: '/guides/cli/schemas.md' + text: '📄 <service>.schemas', + link: '/guides/cli/service.schemas.md' } ] }, @@ -142,7 +146,7 @@ export default { }, { text: '📄 app', - link: '/guides/cli/application.md' + link: '/guides/cli/app.md' }, { text: '📄 authentication', diff --git a/docs/guides/cli/application.md b/docs/guides/cli/app.md similarity index 100% rename from docs/guides/cli/application.md rename to docs/guides/cli/app.md diff --git a/docs/guides/cli/hooks.md b/docs/guides/cli/hook.md similarity index 74% rename from docs/guides/cli/hooks.md rename to docs/guides/cli/hook.md index 8eea8ba233..ebf83a4145 100644 --- a/docs/guides/cli/hooks.md +++ b/docs/guides/cli/hook.md @@ -73,3 +73,46 @@ export const myFancyUserHook = async (context: HookContext) => { ## Registering hooks A generated hook can be registered as an [application hook](./application.md#application-hooks) or as a [service hook](./services.md#registering-hooks). Also see the [hook registration API documentation](../../api/hooks.md#registering-hooks). + +## Profiling example + +To log some basic profiling information like which method was called and how long it took to run you can create a new _around_ hook called `profiler` via + +``` +npx feathers generate hook +``` + +Then update `src/hooks/profiler.ts` as follows: + +```ts +import type { HookContext, NextFunction } from '../declarations' +import { logger } from '../logger' + +export const profiler = async (context: HookContext, next: NextFunction) => { + const startTime = Date.now() + + await next() + + const runtime = Date.now() - startTime + + console.log(`Calling ${context.method} on service ${context.path} took ${runtime}ms`) +} +``` + +And add it in `src/app.ts` as an application hook after the `logError` hook as follows: + +```ts{1,8} +import { profiler } from './hooks/profiler' + +//... + +// Register hooks that run on all service methods +app.hooks({ + around: { + all: [ logError, profiler ] + }, + before: {}, + after: {}, + error: {} +}) +``` diff --git a/docs/guides/cli/log-error.md b/docs/guides/cli/log-error.md new file mode 100644 index 0000000000..af5d63b009 --- /dev/null +++ b/docs/guides/cli/log-error.md @@ -0,0 +1,3 @@ +# Error logging hook + +The `src/hooks/log-error.ts` file exports a `logError` hook that uses the [logger](./logger.md) to log any error for a service method, including validation error details (when they are available). It is registered as an [application hook](./app.md#application-hooks) `all` hook, meaning it will log errors for any service method. diff --git a/docs/guides/cli/logger.md b/docs/guides/cli/logger.md index 301c500d41..f1a00fbf0c 100644 --- a/docs/guides/cli/logger.md +++ b/docs/guides/cli/logger.md @@ -27,50 +27,3 @@ import { logger } from './logger' logger.info('Log some information here') ``` - -## Error Logging Hook - -The `src/hooks/log-error.ts` file exports a `logError` hook that uses the above logger to log any error for a service method, including validation error details (when they are available). It is registered as an [application hook](./application.md#application-hooks) `all` hook, meaning it will log errors for any service method. - -## Profiling example - -To log some basic profiling information like which method was called and how long it took to run you can create a new _around_ hook called `profiler` via - -``` -npx feathers generate hook -``` - -Then update `src/hooks/profiler.ts` as follows: - -```ts -import type { HookContext, NextFunction } from '../declarations' -import { logger } from '../logger' - -export const profiler = async (context: HookContext, next: NextFunction) => { - const startTime = Date.now() - - await next() - - const runtime = Date.now() - startTime - - console.log(`Calling ${context.method} on service ${context.path} took ${runtime}ms`) -} -``` - -And add it in `src/app.ts` as an application hook after the `logError` hook as follows: - -```ts{1,8} -import { profiler } from './hooks/profiler' - -//... - -// Register hooks that run on all service methods -app.hooks({ - around: { - all: [ logError, profiler ] - }, - before: {}, - after: {}, - error: {} -}) -``` diff --git a/docs/guides/cli/service.class.md b/docs/guides/cli/service.class.md new file mode 100644 index 0000000000..2dbf5ab3e2 --- /dev/null +++ b/docs/guides/cli/service.class.md @@ -0,0 +1,135 @@ +# Service classes + +The `service.class` file exports the [service class or object](../../api/services.md). + +## Database services + +When using a database, the service class will be extended from the [Feathers database adapter service](../../api/databases/common.md). Like any class, existing methods can be overriden or you can add your own methods (which can also be made available externally [as custom methods when registering the service](./service.md#registration)). + +### Service customization + + + +
+ +The generic types for a database service are always `AdapterService`. + +
+ +
+ + + +An [SQL Knex service](../../api/databases/knex.md) can be customized like this: + +```ts +export interface MessageParams extends KnexAdapterParams {} + +// By default calls the standard Knex adapter service methods but can be customized with your own functionality. +export class MessageService extends KnexService< + Message, + MessageData, + ServiceParams +> { + find(params: ServiceParams) { + return super.find(params) + } + + async myMethod(name: string) { + return { + message: `Hello ${name}` + } + } +} +``` + + + + + +An [MongoDB service](../../api/databases/mongodb.md) can be customized like this: + +```ts +export interface MessageParams extends MongoDBAdapterParams {} + +// By default calls the standard MongoDB adapter service methods but can be customized with your own functionality. +export class MessageService extends MongoDBService< + Message, + MessageData, + ServiceParams +> { + find(params: ServiceParams) { + return super.find(params) + } + + async myMethod(name: string) { + return { + message: `Hello ${name}` + } + } +} +``` + + + + + +Note the `MessageService` generic. This is used to change the parameter type when using this service interface as a [client side service](./client.md). + + + +## Custom services + +As shown in the [Quick start](../basics/starting.md), Feathers can work with any database, third party API or custom functionality by implementing your own [services](../../api/services.md). When generating a custom service, a basic skeleton service will be created. You can remove the methods you don't need and add your own. + + + +While service methods still have to follow the [standard](../../api/services.md#service-methods) or [custom](../../api/services.md#custom-methods) method signatures, the parameter and return types can be whatever works best for the service you are implementing. If a service method is only for internal use (and not for clients to call) there are no method signature or return value restrictions. + +```ts +import type { Id, NullableId, Params } from '@feathersjs/feathers' + +interface MyParams extends Params {} + +class MyService { + async find(params: MyParams) { + return { + message: 'This type is inferred' + } + } + + async get(id: Id) { + return [ + { + id + } + ] + } + + async create(data: Message, params: MyParams) { + return data + } + + // Custom method made available to clients needs to have `data` and `params` + async customMethod(data: CustomMethodData, params: MyParams) {} + + // A method that is only available internally can do anything + async anyOtherMethod() { + const [entry] = await this.get('david') + + return entry.id + } +} +``` + + + +
+ +When removing methods, the `methods` list in the [service](./service.md) and [client](./client.md) files also needs to be updated accordingly. + +
+ +## getOptions + +The `getOptions` function is a function that returns the options based on the [application](./app.md) that will be passed to the service class constructor. diff --git a/docs/guides/cli/service.md b/docs/guides/cli/service.md new file mode 100644 index 0000000000..fb98d992e0 --- /dev/null +++ b/docs/guides/cli/service.md @@ -0,0 +1,87 @@ +--- +outline: deep +--- + +# Service + +The main service file registers the service on the [application](./app.md) as well as the hooks used on this service. + +## Registration + +The service is added to the main application via [app.use](../../api/application.md#usepath-service--options) under the path you chose when creating the service. It usses the following options: + +- `methods` - A list of methods available for external clients. You can remove methods that are not used or add your own [custom methods](../../api/services.md#custom-methods). Not that this list also has to be updated in the [client file](./client.md). +- `events` - A list of additional [custom events](../../api/events.md#custom-events) sent to clients. + + + +In TypeScript the `ServiceTypes` interface defined in the [declarations](./declarations.md) will also be extended with the correct service class type: + +```ts +declare module '../../../declarations' { + interface ServiceTypes { + testing: TestingService + } +} +``` + + + +## Registering hooks + +This file is also where service [hooks](../../api/hooks.md) are registered on the service. Depending on the selection, it commonly includes the [authentication hook](../../api/authentication/hook.md) and hooks that validate and resolve the schemas from the [service.schemas file](./schemas.md). + +```ts +// Initialize hooks +app.service('messages').hooks({ + around: { + all: [authenticate('jwt')] + }, + before: { + all: [ + schemaHooks.validateQuery(messageQueryValidator), + schemaHooks.validateData(messageDataValidator), + schemaHooks.resolveQuery(messageQueryResolver), + schemaHooks.resolveData(messageDataResolver) + ] + }, + after: { + all: [schemaHooks.resolveResult(messageResolver), schemaHooks.resolveExternal(messageExternalResolver)] + }, + error: { + all: [] + } +}) +``` + +Note that you can add hooks to a specific method as documented in the [hook registration API](../../api/hooks.md#registering-hooks). For example, to use the [profiling hook](./hook.md#profiling-example) only for `find` and `get` the registration can be updated like this: + +```ts{8-9} +import { profiler } from '../../hooks/profiler' +// ... + +// Initialize hooks +app.service('messages').hooks({ + around: { + all: [authenticate('jwt')], + find: [profiler], + get: [profiler] + }, + before: { + all: [ + schemaHooks.validateQuery(messageQueryValidator), + schemaHooks.validateData(messageDataValidator), + schemaHooks.resolveQuery(messageQueryResolver), + schemaHooks.resolveData(messageDataResolver) + ] + }, + after: { + all: [schemaHooks.resolveResult(messageResolver), schemaHooks.resolveExternal(messageExternalResolver)] + }, + error: { + all: [] + } +}) +``` + +This also applies to any hook plugins like [feathers-hooks-common](https://hooks-common.feathersjs.com/). diff --git a/docs/guides/cli/schemas.md b/docs/guides/cli/service.schemas.md similarity index 63% rename from docs/guides/cli/schemas.md rename to docs/guides/cli/service.schemas.md index 213b850341..3418ad574d 100644 --- a/docs/guides/cli/schemas.md +++ b/docs/guides/cli/service.schemas.md @@ -4,10 +4,37 @@ outline: deep # Service Schemas and Resolvers -Give a tour of the generated schemas and resolvers for a service. +The `.schemas` file contains the [schemas and resolvers](../../api/schema/index.md) for this service. + +
+ +The examples on this page are using [TypeBox](../../api/schema/typebox.md). For more information on plain JSON schema see the [JSON schema API documentation](../../api/schema/schema.md). + +
+ +## Patterns + +There a four main types of schemas and resolvers. The schemas, resolvers and TypeScript types are declared as follows: + +```ts +// The schema definition +export const nameSchema = Type.Object({ + text: Type.String() +}) +// The TypeScript type inferred from the schema +export type Name = Static +// The resolver for the schema +export const nameResolver = resolve({ + properties: {} +}) +// The validator if it is a schema for data +export const nameValidator = getDataValidator(nameSchema, dataValidator) +``` ## Main Schemas and Resolvers +This schema defines the main data model of all properties and is normally the shape of the data that is returned. This includes database properties as well as associations and other computed properties. + ```ts // Main data model schema https://dove.feathersjs.com/guides/cli/schemas-and-resolvers.html#main-schemas-and-resolvers export const messagesSchema = Type.Object( diff --git a/docs/guides/cli/services.md b/docs/guides/cli/services.md deleted file mode 100644 index 80419c8f92..0000000000 --- a/docs/guides/cli/services.md +++ /dev/null @@ -1,101 +0,0 @@ ---- -outline: deep ---- - -# Service - -## Service class - -### Database services - -### Custom services - -## Service file - -### Registration - -### Custom methods - -### Hooks - -## TypeScript - -### Generics - -### Registration - -Link here from the class file in the generated app: - -Todo: Implement ServiceAdapter picker for the following - -## Knex - -## MongoDB - -```ts -import { MongoDBService } from '@feathersjs/mongodb' -import type { MongoDBAdapterParams } from '@feathersjs/mongodb' - -import type { Application } from '../../declarations' -import type { Message, MessageData, MessageQuery } from './messages.schema' - -export interface MessageParams extends MongoDBAdapterParams {} - -// Message class for MongoDB https://dove.feathersjs.com/cli/service-class -export class MessageService extends MongoDBService {} - -export const getOptions = (app: Application) => { - return { - paginate: app.get('paginate'), - Model: app.get('mongodbClient').then((db) => db.collection('message')) - } -} -``` - -## Registering hooks - -Where to link for the comment above the service hooks?: - -```ts -import { authenticate } from '@feathersjs/authentication' - -import type { Application } from '../../../../declarations' -import { TestingService, getOptions } from './test.class' - -export * from './test.class' - -// Messages service and hooks https://dove.feathersjs.com/cli/service-overview.html -export const testing = (app: Application) => { - app.use('messages', new TestingService(getOptions(app)), { - methods: ['find', 'get', 'create', 'update', 'patch', 'remove'], - events: [] - }) - app.service('messages').hooks({ - around: { - all: [authenticate('jwt')] - }, - before: { - all: [] - }, - after: { - all: [] - }, - error: { - all: [] - } - }) -} -``` - -## Service TypeScript Declarations - -We already have an explanation of this in the troubleshooting guide, so we can link to there. - -```ts -// Update Service Declarations https://dove.feathersjs.com/cli/service-overview.html#service-types -declare module '../../../../declarations' { - interface ServiceTypes { - messages: TestingService - } -} -``` diff --git a/packages/cli/src/hook/templates/hook.tpl.ts b/packages/cli/src/hook/templates/hook.tpl.ts index 500fc4195b..6087d03b4d 100644 --- a/packages/cli/src/hook/templates/hook.tpl.ts +++ b/packages/cli/src/hook/templates/hook.tpl.ts @@ -5,7 +5,7 @@ import { renderSource } from '../../commons' const aroundTemplate = ({ camelName, name -}: HookGeneratorContext) => /* ts */ `// For more information about this file see https://dove.feathersjs.com/guides/cli/hooks.html +}: HookGeneratorContext) => /* ts */ `// For more information about this file see https://dove.feathersjs.com/guides/cli/hook.html import type { HookContext, NextFunction } from '../declarations' export const ${camelName} = async (context: HookContext, next: NextFunction) => { @@ -17,7 +17,7 @@ export const ${camelName} = async (context: HookContext, next: NextFunction) => const regularTemplate = ({ camelName, name -}: HookGeneratorContext) => /* ts */ `// For more information about this file see https://dove.feathersjs.com/guides/cli/hooks.html +}: HookGeneratorContext) => /* ts */ `// For more information about this file see https://dove.feathersjs.com/guides/cli/hook.html import type { HookContext } from '../declarations' export const ${camelName} = async (context: HookContext) => { diff --git a/packages/cli/src/service/templates/schema.json.tpl.ts b/packages/cli/src/service/templates/schema.json.tpl.ts index 3053192441..175ba5bb45 100644 --- a/packages/cli/src/service/templates/schema.json.tpl.ts +++ b/packages/cli/src/service/templates/schema.json.tpl.ts @@ -7,7 +7,8 @@ const template = ({ upperName, relative, type -}: ServiceGeneratorContext) => /* ts */ `import { resolve, getDataValidator, getValidator, querySyntax } from '@feathersjs/schema' +}: ServiceGeneratorContext) => /* ts */ `// For more information about this file see https://dove.feathersjs.com/guides/cli/service.schemas.html +import { resolve, getDataValidator, getValidator, querySyntax } from '@feathersjs/schema' import type { FromSchema } from '@feathersjs/schema' import type { HookContext } from '${relative}/declarations' diff --git a/packages/cli/src/service/templates/schema.typebox.tpl.ts b/packages/cli/src/service/templates/schema.typebox.tpl.ts index ccbd5c101d..7b055c694b 100644 --- a/packages/cli/src/service/templates/schema.typebox.tpl.ts +++ b/packages/cli/src/service/templates/schema.typebox.tpl.ts @@ -7,7 +7,8 @@ const template = ({ upperName, relative, type -}: ServiceGeneratorContext) => /* ts */ `import { resolve } from '@feathersjs/schema' +}: ServiceGeneratorContext) => /* ts */ `// // For more information about this file see https://dove.feathersjs.com/guides/cli/service.schemas.html +import { resolve } from '@feathersjs/schema' import { Type, getDataValidator, getValidator, querySyntax } from '@feathersjs/typebox' import type { Static } from '@feathersjs/typebox' diff --git a/packages/cli/src/service/templates/service.tpl.ts b/packages/cli/src/service/templates/service.tpl.ts index 0e397471f2..315be1b069 100644 --- a/packages/cli/src/service/templates/service.tpl.ts +++ b/packages/cli/src/service/templates/service.tpl.ts @@ -11,7 +11,7 @@ export const template = ({ relative, schema, fileName -}: ServiceGeneratorContext) => /* ts */ ` +}: ServiceGeneratorContext) => /* ts */ `// For more information about this file see https://dove.feathersjs.com/guides/cli/service.html ${authentication || isEntityService ? `import { authenticate } from '@feathersjs/authentication'` : ''} ${ schema diff --git a/packages/cli/src/service/type/custom.tpl.ts b/packages/cli/src/service/type/custom.tpl.ts index 1c244601c9..f151295ce7 100644 --- a/packages/cli/src/service/type/custom.tpl.ts +++ b/packages/cli/src/service/type/custom.tpl.ts @@ -2,7 +2,13 @@ import { generator, toFile } from '@feathershq/pinion' import { renderSource } from '../../commons' import { ServiceGeneratorContext } from '../index' -export const template = ({ className, upperName, schema, fileName, relative }: ServiceGeneratorContext) => ` +export const template = ({ + className, + upperName, + schema, + fileName, + relative +}: ServiceGeneratorContext) => /* ts */ `// For more information about this file see https://dove.feathersjs.com/guides/cli/service.class.html#custom-services import type { Id, NullableId, Params } from '@feathersjs/feathers' import type { Application } from '${relative}/declarations' diff --git a/packages/cli/src/service/type/knex.tpl.ts b/packages/cli/src/service/type/knex.tpl.ts index 0dd258746a..b4e536986e 100644 --- a/packages/cli/src/service/type/knex.tpl.ts +++ b/packages/cli/src/service/type/knex.tpl.ts @@ -4,7 +4,8 @@ import { ServiceGeneratorContext } from '../index' const migrationTemplate = ({ kebabPath -}: ServiceGeneratorContext) => /* ts */ `import type { Knex } from 'knex' +}: ServiceGeneratorContext) => /* ts */ `// For more information about this file see https://dove.feathersjs.com/guides/cli/knexfile.html +import type { Knex } from 'knex' export async function up(knex: Knex): Promise { await knex.schema.createTable('${kebabPath}', table => { @@ -25,7 +26,8 @@ export const template = ({ schema, fileName, relative -}: ServiceGeneratorContext) => /* ts */ `import type { Params } from '@feathersjs/feathers' +}: ServiceGeneratorContext) => /* ts */ `// For more information about this file see https://dove.feathersjs.com/guides/cli/service.class.html#database-services +import type { Params } from '@feathersjs/feathers' import { KnexService } from '@feathersjs/knex' import type { KnexAdapterParams, KnexAdapterOptions } from '@feathersjs/knex' diff --git a/packages/cli/src/service/type/mongodb.tpl.ts b/packages/cli/src/service/type/mongodb.tpl.ts index af819e404e..b6a36e3471 100644 --- a/packages/cli/src/service/type/mongodb.tpl.ts +++ b/packages/cli/src/service/type/mongodb.tpl.ts @@ -9,7 +9,8 @@ export const template = ({ fileName, kebabPath, relative -}: ServiceGeneratorContext) => /* ts */ `import type { Params } from '@feathersjs/feathers' +}: ServiceGeneratorContext) => /* ts */ `// For more information about this file see https://dove.feathersjs.com/guides/cli/service.class.html#database-services +import type { Params } from '@feathersjs/feathers' import { MongoDBService } from \'@feathersjs/mongodb\' import type { MongoDBAdapterParams, MongoDBAdapterOptions } from \'@feathersjs/mongodb\' From c528f02877419a5eac0bed3abb5d731b71fe9244 Mon Sep 17 00:00:00 2001 From: Marshall Thompson Date: Fri, 18 Nov 2022 15:33:56 -0700 Subject: [PATCH 09/25] independent select components, reorganize cli guide --- docs/.vitepress/components.d.ts | 2 + docs/.vitepress/components/DatabaseSelect.vue | 26 ++++++++++++ docs/.vitepress/components/LanguageSelect.vue | 24 +++++++++++ docs/.vitepress/config.sidebar.ts | 21 ++++++---- docs/.vitepress/theme/FeathersLayout.vue | 40 ++----------------- docs/guides/basics/schemas.md | 3 ++ docs/guides/cli/configuration.md | 12 ------ docs/guides/cli/databases.md | 39 ++---------------- docs/guides/cli/knexfile.md | 22 ++++++++++ docs/guides/cli/package.md | 13 ++++++ 10 files changed, 111 insertions(+), 91 deletions(-) create mode 100644 docs/.vitepress/components/DatabaseSelect.vue create mode 100644 docs/.vitepress/components/LanguageSelect.vue diff --git a/docs/.vitepress/components.d.ts b/docs/.vitepress/components.d.ts index d735007bb3..f4fb8aafe9 100644 --- a/docs/.vitepress/components.d.ts +++ b/docs/.vitepress/components.d.ts @@ -11,6 +11,7 @@ declare module '@vue/runtime-core' { BlockQuote: typeof import('./components/BlockQuote.vue')['default'] Contributors: typeof import('./components/Contributors.vue')['default'] DatabaseBlock: typeof import('./components/DatabaseBlock.vue')['default'] + DatabaseSelect: typeof import('./components/DatabaseSelect.vue')['default'] ElCheckbox: typeof import('element-plus/es')['ElCheckbox'] ElInput: typeof import('element-plus/es')['ElInput'] ElOption: typeof import('element-plus/es')['ElOption'] @@ -19,6 +20,7 @@ declare module '@vue/runtime-core' { ElSelect: typeof import('element-plus/es')['ElSelect'] FeaturesList: typeof import('./components/FeaturesList.vue')['default'] LanguageBlock: typeof import('./components/LanguageBlock.vue')['default'] + LanguageSelect: typeof import('./components/LanguageSelect.vue')['default'] ListItem: typeof import('./components/ListItem.vue')['default'] Logo: typeof import('./components/Logo.vue')['default'] Select: typeof import('./components/Select.vue')['default'] diff --git a/docs/.vitepress/components/DatabaseSelect.vue b/docs/.vitepress/components/DatabaseSelect.vue new file mode 100644 index 0000000000..5a930b7612 --- /dev/null +++ b/docs/.vitepress/components/DatabaseSelect.vue @@ -0,0 +1,26 @@ + + + diff --git a/docs/.vitepress/components/LanguageSelect.vue b/docs/.vitepress/components/LanguageSelect.vue new file mode 100644 index 0000000000..b77d4c27ff --- /dev/null +++ b/docs/.vitepress/components/LanguageSelect.vue @@ -0,0 +1,24 @@ + + + diff --git a/docs/.vitepress/config.sidebar.ts b/docs/.vitepress/config.sidebar.ts index 49e6b6776f..0ea19a4c7b 100644 --- a/docs/.vitepress/config.sidebar.ts +++ b/docs/.vitepress/config.sidebar.ts @@ -78,18 +78,25 @@ export default { ] }, { - text: 'Generator', + text: 'CLI', collapsible: true, - collapsed: false, + collapsed: true, items: [ { - text: '📝 readme', + text: 'Using the CLI', link: '/guides/cli/index.md' }, - // { - // text: 'Generate App', - // link: '/guides/cli/generate-app.md' - // }, + { + text: 'Generate App', + link: '/guides/cli/generate-app.md' + } + ] + }, + { + text: 'App Structure', + collapsible: true, + collapsed: false, + items: [ { text: '📂 config', items: [ diff --git a/docs/.vitepress/theme/FeathersLayout.vue b/docs/.vitepress/theme/FeathersLayout.vue index f1e23bcd8c..033c35e569 100644 --- a/docs/.vitepress/theme/FeathersLayout.vue +++ b/docs/.vitepress/theme/FeathersLayout.vue @@ -1,49 +1,17 @@