diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index 6a251ed895..24b463928f 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -14,6 +14,7 @@ module.exports = { }, title: 'Strapi Docs', description: 'API creation made simple, secure and fast.', + base: '/documentation/', themeConfig: { versions: [ ['Version 3.x.x', '/3.x.x/'], diff --git a/docs/.vuepress/dist/1.x.x/SUMMARY.html b/docs/.vuepress/dist/1.x.x/SUMMARY.html new file mode 100644 index 0000000000..b390c82f57 --- /dev/null +++ b/docs/.vuepress/dist/1.x.x/SUMMARY.html @@ -0,0 +1,23 @@ + + + + + + Summary | Strapi Docs + + + + + + + +
+ + + diff --git a/docs/.vuepress/dist/1.x.x/admin.html b/docs/.vuepress/dist/1.x.x/admin.html new file mode 100644 index 0000000000..aa92ddf175 --- /dev/null +++ b/docs/.vuepress/dist/1.x.x/admin.html @@ -0,0 +1,41 @@ + + + + + + Admin | Strapi Docs + + + + + + + +

Admin

Where is my data ?

This is a recurring question when you create web applications from scratch or +using a web framework. That's why Strapi provides a ready-to-use admin panel.

Dashboard

This part is a summary of your application.

Work in progress.

Data Explorer

The Data Explorer allows you to easily manage your data. +The UI is auto-generated depending on the models of your application. +So, in just a few seconds, you are able to create, search, view, edit and +delete your data.

To try it, simply create a new API using the Studio or the CLI. +Then restart the server and reload the web browser page.

Strapi Admin panel Screenshot Data Explorer

Users

List of Users

List, edit and delete the users information of your application.

Roles

Each user can be related to one or many roles.

The first registered user is automatically related to the admin role.

Permissions

Strapi contains a security system based on the routes of your application. +The admin panel allows you to visualize the different routes of your server and +to manage the security of each of them.

  • Public: no level of security (anyone can use the route).
  • Registered: the user has to be logged to use the route.
  • Owner: the user must be one of the contributors of the model updated or deleted.
  • Admin: only the users related to the admin role are allowed to access the route.

Strapi Admin panel Screenshot Permissions

Customization

The admin panel is developed with Angular.js, using the John PAPA styleguide. +You can customize the admin from ./api/admin/public in your generated application.

To build the admin panel:

  • You need to install bower and gulp with $ npm install gulp bower -g.
  • Run $ npm install in this directory.
  • Run $ gulp serve.
  • Visit http://localhost:3002 from your web browser.
  • When you are ready to use your customized admin panel, run $ gulp dist. +That will update the files in the following folder: ./api/admin/public/dist.
  • Visit http://localhost:1337/admin/.

If you change the default port (1337) of your server, you will have to update +./api/admin/public/config/config.json and then run $ npm install && gulp dist +in ./api/admin/public.

NOTE: You can create your own admin generator using the .strapirc file. +Learn more how to use custom generators.

+ + + diff --git a/docs/.vuepress/dist/1.x.x/blueprints.html b/docs/.vuepress/dist/1.x.x/blueprints.html new file mode 100644 index 0000000000..59ec66c4f3 --- /dev/null +++ b/docs/.vuepress/dist/1.x.x/blueprints.html @@ -0,0 +1,189 @@ + + + + + + Blueprints | Strapi Docs + + + + + + + +

Blueprints

The blueprints are a set of useful actions containing all the logic you need to +create a clean RESTful API. The generated controllers and routes are automatically +plugged to the blueprint actions. Thanks to that, as soon as you generate a new API +from the CLI, you can enjoy a RESTful API without writing any line of code.

For example, if you generate a pet API, you will be able to immediately visit +POST /pet?name=joe to create a pet, and visit GET /pet to see an array +of your application's pets.

Blueprints are great for prototyping, but they are also a powerful tool in production +due to their ability to be overridden, protected, extended or disabled entirely.

All of the following actions return a promise.

find records

Returns a list of records from the model as a JSON array of objects.

Route:

{
+  "routes": {
+    "GET /pet": {
+      "controller": "Pet",
+      "action": "find"
+    }
+  }
+}
+

Controller function:

find: function * () {
+  this.model = 'Pet';
+
+  try {
+    this.body = yield strapi.hooks.blueprints.find(this);
+  } catch (error) {
+    this.body = error;
+  }
+}
+

Results may be filtered, paginated, and sorted based on the blueprint configuration +and/or parameters sent in the request.

Optional parameters:

  • * (string): To filter results based on a particular attribute, specify a query +parameter with the same name as the attribute defined on your model.
  • where (string): Instead of filtering based on a specific attribute, you may instead +choose to provide a where parameter with a Waterline WHERE criteria object, +encoded as a JSON string. This allows you to take advantage of contains, startsWith, +and other sub-attribute criteria modifiers for more powerful find() queries.
  • limit (number): The maximum number of records to send back (useful for pagination). +Defaults to 30.
  • skip (number): The number of records to skip (useful for pagination).
  • sort (string): The sort order. By default, returned records are sorted by primary key value +in ascending order. ASC or DESC.
  • populate (string): If specified, override the default automatic population process. +Accepts a comma separated list of attributes names for which to populate record values.

findOne record

Returns a single record from the model as a JSON object.

Route:

{
+  "routes": {
+    "GET /pet/:id": {
+      "controller": "Pet",
+      "action": "findOne"
+    }
+  }
+}
+

Controller function:

findOne: function * () {
+  this.model = 'Pet';
+
+  try {
+    this.body = yield strapi.hooks.blueprints.findOne(this);
+  } catch (error) {
+    this.body = error;
+  }
+}
+

The findOne() blueprint action returns a single record from the model as a JSON object. +The specified id is the primary key of the desired record.

Required parameters:

  • id (string or number): The desired record's primary key value.

create a record

Creates a new model instance in your database then returns its values.

Route:

{
+  "routes": {
+    "POST /pet/:id": {
+      "controller": "Pet",
+      "action": "create"
+    }
+  }
+}
+

Controller function:

create: function * () {
+  this.model = 'Pet';
+
+  try {
+    this.body = yield strapi.hooks.blueprints.create(this);
+  } catch (error) {
+    this.body = error;
+  }
+}
+

Attributes can be sent in the HTTP body as form-encoded values or JSON.

The promise returned contains a JSON object representing the newly created instance. +If a validation error occurred, a JSON response with the invalid attributes and +the Context status is set to 400.

Optional parameters:

  • * (string, number, object or array): Pass in body parameter with the same +name as the attribute defined in your model to set those values on your new record. +Nested objects and arrays passed in as parameters are handled the same +way as if they were passed into the model's .create() method.

update a record

Updates an existing record. Attributes to change should be sent in the HTTP body +as form-encoded values or JSON.

Route:

{
+  "routes": {
+    "PUT /pet/:id": {
+      "controller": "Pet",
+      "action": "update"
+    }
+  }
+}
+

Controller function:

update: function * () {
+  this.model = 'Pet';
+
+  try {
+    this.body = yield strapi.hooks.blueprints.update(this);
+  } catch (error) {
+    this.body = error;
+  }
+}
+

Updates the model instance which matches the id parameter. +The promise resolved contains a JSON object representing the newly updated instance. +If a validation error occurred, a JSON response with the invalid attributes and a +400 status code will be returned instead. If no model instance exists matching the +specified id, a 404 is returned.

Required parameters:

  • id (string or number): The desired record's primary key value.

Optional parameters:

  • * (string, number, object or array): Pass in body parameter with the same +name as the attribute defined on your model to set those values on your new record. +Nested objects and arrays passed in as parameters are handled the same +way as if they were passed into the model's .update() method.

destroy a record

Deletes an existing record specified by id from the database forever and returns +the values of the deleted record.

Route:

{
+  "routes": {
+    "DELETE /pet/:id": {
+      "controller": "Pet",
+      "action": "destroy"
+    }
+  }
+}
+

Controller function:

destroy: function * () {
+  this.model = 'Pet';
+
+  try {
+    this.body = yield strapi.hooks.blueprints.destroy(this);
+  } catch (error) {
+    this.body = error;
+  }
+}
+

Destroys the model instance which matches the id parameter. +Responds with a JSON object representing the newly destroyed instance. +If no model instance exists matching the specified id, the Context status is set to 400 and the returned promise is rejected.

Required parameters:

  • id (string or number): The desired record's primary key value.

add to a record

Adds an association between two records.

Route:

{
+  "routes": {
+    "POST /pet/:id/:parentId/:relation": {
+      "controller": "Pet",
+      "action": "add"
+    }
+  }
+}
+

Controller function:

add: function * () {
+  this.model = 'Pet';
+
+  try {
+    this.body = yield strapi.hooks.blueprints.add(this);
+  } catch (error) {
+    this.body = error;
+  }
+}
+

This action pushes a reference to some other record (the "foreign" record) onto a +collection attribute of this record (the "primary" record).

  • If :relation of an existing record is supplied, it will be associated with +the primary record.
  • If no :relation is supplied, and the body of the POST contains values for a +new record, that record will be created and associated with the primary record.
  • If the collection within the primary record already contains a reference to the +foreign record, this action will be ignored.
  • If the association is two-way (i.e. reflexive, with via on both sides) the association +on the foreign record will also be updated.

Notes:

  • This action is for dealing with plural ("collection") associations. +If you want to set or unset a singular ("model") association, just use +the update blueprint.

remove from a record

Removes an association between two records.

Route:

{
+  "routes": {
+    "DELETE /pet/:id/:parentId/:relation/:id": {
+      "controller": "Pet",
+      "action": "remove"
+    }
+  }
+}
+

Controller function:

remove: function * () {
+  this.model = 'Pet';
+
+  try {
+    this.body = yield strapi.hooks.blueprints.remove(this);
+  } catch (error) {
+    this.body = error;
+  }
+}
+

This action removes a reference to some other record (the "foreign" record) +from a collection attribute of this record (the "primary" record).

  • If the foreign record does not exist, it is created first.
  • If the collection doesn't contain a reference to the foreign record, +this action will be ignored.
  • If the association is two-way (i.e. reflexive, with via on both sides) +the association on the foreign record will also be updated.
+ + + diff --git a/docs/.vuepress/dist/1.x.x/cli.html b/docs/.vuepress/dist/1.x.x/cli.html new file mode 100644 index 0000000000..6e35060641 --- /dev/null +++ b/docs/.vuepress/dist/1.x.x/cli.html @@ -0,0 +1,49 @@ + + + + + + CLI | Strapi Docs + + + + + + + +

CLI

Strapi comes with a convenient command-line tool to quickly get your application scaffolded and running.

Login

$ strapi login
+

Ask your Strapi Studio credentials to link your new applications on your machine to +the Strapi Studio aiming to have a perfect workflow while you build APIs.

Go to the Strapi Studio to start the experience.

Create a new project

$ strapi new <appName>
+

Create a new Strapi project in a directory called appName.

$ strapi new is really just a special generator which runs strapi-generate-new. +In other words, running $ strapi new <appName> is an alias for running +$ strapi generate new <appName>, and like any Strapi generator, the actual generator module +which gets run can be overridden.

Start the server

$ cd <appName>
+$ strapi start
+

Run the Strapi application in the current directory. +If ./node_modules/strapi exists, it will be used instead of the globally installed module Strapi.

Access the console

$ cd <appName>
+$ strapi console
+

Start your Strapi application, and enter the Node.js REPL. This means you can access +and use all of your models, services, configuration, and much more. Useful for trying out +Waterline queries, quickly managing your data, and checking out your project's runtime configuration.

Note that this command still starts the server, so your routes will be accessible via HTTP and sockets.

Strapi exposes the same global variables in the console as it does in your application code. +This is particularly useful in the REPL. By default, you have access to the Strapi application +instance, your models as well as Lodash (_) and Socket.IO (io).

Generate an API

$ strapi generate api <apiName>
+

Generate a complete API with controllers, models and routes.

$ strapi version
+

Output the current globally installed Strapi version.

$ strapi link
+

Link an existing application without an appId to the Strapi Studio.

This command can be useful if you were not logged into the Studio or if you +didn't have Internet access when you generated your application.

Logout

$ strapi logout
+

If you don't want to be logged in to the Strapi Studio anymore.

+ + + diff --git a/docs/.vuepress/dist/1.x.x/configuration.html b/docs/.vuepress/dist/1.x.x/configuration.html new file mode 100644 index 0000000000..9b4b6f874d --- /dev/null +++ b/docs/.vuepress/dist/1.x.x/configuration.html @@ -0,0 +1,313 @@ + + + + + + Configuration | Strapi Docs + + + + + + + +

Configuration

While Strapi dutifully adheres to the philosophy of convention-over-configuration, +it is important to understand how to customize those handy defaults from time to time. +For almost every convention in Strapi, there is an accompanying set of configuration +options that allow you to adjust or override things to fit your needs.

Settings specified at the root directory will be available in all environments.

If you'd like to have some settings take effect only in certain environments, +you can use the special environment-specific files and folders. +Any files saved under the ./config/environments/development directory will be +loaded only when Strapi is started in the development environment.

The built-in meaning of the settings in strapi.config are, in some cases, +only interpreted by Strapi during the start process. In other words, changing some +options at runtime will have no effect. To change the port your application is running on, +for instance, you can't just change strapi.config.port. You'll need to change or +override the setting in a configuration file or as a command-line argument, +then restart the server.

Application package

strapi.config merge user config from the ./config directory with the package.json +of the application.

The most important things in your package.json are the name and version fields. +Those are actually required, and your package won't install without them. +The name and version together form an identifier that is assumed to be completely unique.

Application name

The name of the application.

  • Key: name
  • Environment: all
  • Location: ./package.json
  • Type: string

Notes:

  • The name must be shorter than 214 characters. This includes the scope for scoped packages.
  • The name can't start with a dot or an underscore.
  • New packages must not have uppercase letters in the name.
  • The name ends up being part of a URL, an argument on the command line, and a folder name. +Therefore, the name can't contain any non-URL-safe characters.
  • Don't use the same name as a core Node.js module.
  • Don't put "js" or "node" in the name. It's assumed that it's JavaScript, since you're writing +a package.json file.
  • The name will probably be passed as an argument to require(), so it should be something short, +but also reasonably descriptive. You may want to check the npm registry to see if there's something +by that name already, before you get too attached to it. https://www.npmjs.com/
  • A name can be optionally prefixed by a scope, e.g. @myorg/mypackage.

Application version

Changes to the package should come along with changes to the version.

  • Key: version
  • Environment: all
  • Location: ./package.json
  • Type: string

Notes:

  • Version must be parseable by node-semver, which is bundled with npm as a dependency.

Application description

The description of your application helps people discover your package, as it's listed in npm search.

  • Key: description
  • Environment: all
  • Location: ./package.json
  • Type: string

Global settings

Public assets

Public assets refer to static files on your server that you want to make accessible to the +outside world. In Strapi, these files are placed in the ./public directory.

Strapi is compatible with any front-end strategy; whether it's Angular, Backbone, Ember, +iOS, Android, Windows Phone, or something else that hasn't been invented yet.

  • Key: static

  • Environment: all

  • Location: ./config/general.json

  • Type: boolean

  • Defaults to:

    {
    +  "static": true
    +}
    +

Notes:

  • Set to false to disable the public assets.

Views

  • Key: views

  • Environment: all

  • Location: ./config/general.json

  • Type: object

  • Defaults to:

    {
    +  "views": false
    +}
    +

For more information, please refer to the views documentation.

Options:

  • map: Object mapping extension names to engine names.
  • default: Default extension name to use when missing.
  • cache: When true compiled template functions will be cached in-memory, +this prevents subsequent disk I/O, as well as the additional compilation step +that most template engines peform. By default this is enabled when the NODE_ENV +environment variable is anything but development, such as stage or production.

Notes:

  • Set to false to disable views support.

WebSockets

Socket.IO enables real-time bidirectional event-based communication. +It works on every platform, browser or device, focusing equally on reliability +and speed.

By default Strapi binds Socket.IO and your common websockets features are +available using the io object.

  • Key: websockets

  • Environment: all

  • Location: ./config/general.json

  • Type: boolean

  • Defaults to:

    {
    +  "websockets": true
    +}
    +

Notes:

  • Set to false to disable websockets with Socket.IO.

Favicon

Set a favicon for your web application.

  • Key: favicon

  • Environment: all

  • Location: ./config/general.json

  • Type: object

  • Defaults to:

    {
    +  "favicon": {
    +    "path": "favicon.ico",
    +    "maxAge": 86400000
    +  }
    +}
    +

Options:

  • path (string): Relative path for the favicon to use from the application root directory.
  • maxAge (integer): Cache-control max-age directive. Set to pass the cache-control in ms.

Notes:

  • Set to false to disable the favicon feature.

API prefix

Prefix your API aiming to not have any conflicts with your front-end if you have one of if need to +for some other reasons.

  • Key: prefix

  • Environment: all

  • Location: ./config/general.json

  • Type: string

  • Defaults to:

    {
    +  "prefix": ""
    +}
    +

Notes:

  • Let an empty string if you don't want to prefix your API.
  • The prefix must starts with a /, e.g. /api.

Blueprints

The blueprints are a set of useful actions containing all the logic you need to +create a clean RESTful API. The generated controllers and routes are automatically +plugged to the blueprint actions. Thanks to that, as soon as you generate a new API +from the CLI, you can enjoy a RESTful API without writing any line of code.

  • Key: blueprints

  • Environment: all

  • Location: ./config/general.json

  • Type: object

  • Defaults to:

    {
    +  "blueprints": {
    +    "defaultLimit": 30,
    +    "populate": true
    +  }
    +}
    +

Options:

  • defaultLimit (integer): The maximum number of records to send back.
  • populate (boolean): If enabled, the population process fills out attributes +in the returned list of records according to the model's defined associations.

i18n

If your application will touch people or systems from all over the world, internationalization +and localization (i18n) may be an important part of your international strategy.

Strapi provides built-in support for detecting user language preferences and translating +static words/sentences.

  • Key: i18n

  • Environment: all

  • Location: ./config/i18n.json

  • Type: object

  • Defaults to:

    {
    +  "i18n": {
    +    "defaultLocale": "en",
    +    "modes": [
    +      "query",
    +      "subdomain",
    +      "cookie",
    +      "header",
    +      "url",
    +      "tld"
    +    ],
    +    "cookieName": "locale"
    +  }
    +}
    +

Options:

  • defaultLocale (string): The default locale to use.
  • modes (array): Accept locale variable from: +
    • query: detect query string with /?locale=fr
    • subdomain: detect subdomain with fr.myapp.com
    • cookie: detect cookie with Accept-Language: en,fr;q=0.5
    • header: detect header with Cookie: locale=fr
    • url: detect url with /fr
    • tld: detect TLD with myapp.fr
  • cookieName (string): i18n cookies property, tries to find a cookie named locale here. +Allows the locale to be set from query string or from cookie.

Notes:

  • Set to false to disable the locales feature.
  • Locales may be configured in the ./config/locales directory.

Global variables

For convenience, Strapi exposes a handful of global variables. By default, your application's +models, the global strapi object and the Lodash node module are all available on the global +scope; meaning you can refer to them by name anywhere in your backend code +(as long as Strapi has been loaded).

Nothing in Strapi core relies on these global variables. Each and every global exposed in +Strapi may be disabled in strapi.config.globals.

Bear in mind that none of the globals, including strapi, are accessible until after +Strapi has loaded. In other words, you won't be able to use strapi.models.car or Car +outside of a function (since Strapi will not have finished loading yet).

  • Key: globals

  • Environment: all

  • Location: ./config/globals.json

  • Type: object

  • Defaults to:

    {
    +  "globals": {
    +    "models": true,
    +    "strapi": true,
    +    "async": true,
    +    "_": true,
    +    "graphql": true
    +  }
    +}
    +

Options:

  • models (boolean): Your application's models are exposed as global variables using their globalId. +For instance, the model defined in the file ./api/car/models/Car.js will be globally accessible as Car.
  • strapi (boolean): In most cases, you will want to keep the strapi object globally accessible, +it makes your application code much cleaner.
  • async (boolean): Exposes an instance of Async.
  • _ (boolean): Exposes an instance of Lodash.
  • graphql (boolean): Exposes an instance of GraphQL.

Notes:

  • Set to false to disable global variables.

Bootstrap function

The bootstrap function is a server-side JavaScript file that is executed by Strapi +just before your application is started.

This gives you an opportunity to set up your data model, run jobs, or perform some special logic.

  • Key: bootstrap
  • Environment: all
  • Location: ./config/functions/bootstrap.js
  • Type: function

Notes:

  • It's very important to trigger the callback method when you are finished with the bootstrap. +Otherwise your server will never start, since it's waiting on the bootstrap.

CRON tasks

CRON tasks allow you to schedule jobs (arbitrary functions) for execution at specific dates, +with optional recurrence rules. It only uses a single timer at any given time +(rather than reevaluating upcoming jobs every second/minute).

  • Key: cron

  • Environment: all

  • Location: ./config/functions/cron.js

  • Type: object

      module.exports.cron = {
    +
    +    /**
    +     * Every day at midnight.
    +     */
    +
    +    '0 0 * * *': function () {
    +      // Your code here
    +    }
    +  };
    +}
    +

Notes:

  • The cron format consists of: +
    1. second (0 - 59, optional)
    2. minute (0 - 59)
    3. hour (0 - 23)
    4. day of month (1 - 31)
    5. month (1 - 12)
    6. day of week (0 - 7)

Studio connection

The Strapi Studio is a toolbox for developers that allows you to build and manage +your APIs in realtime without writing any line of code. When your application is +linked to the Studio, you are able to generate APIs from the Studio and see +the changes in realtime in your local application.

  • Key: studio

  • Environment: all

  • Location: ./config/studio.json

  • Type: object

  • Defaults to:

    {
    +  "studio": {
    +    "enabled": true,
    +    "secretKey": "YOUR SECRET KEY HERE"
    +  }
    +}
    +

Options:

  • enabled (boolean): Do you want your application linked to the Strapi Studio?
  • secretKey (string): The secret key of your application to link your +current application with the Strapi Studio.

General environment settings

Host

The host name the connection was configured to.

  • Key: host

  • Environment: development

  • Location: ./config/environments/development/server.json

  • Type: string

  • Defaults to:

    {
    +  "host": "localhost"
    +}
    +

Notes:

  • You don't need to specify a host in a production environment.
  • Defaults to the operating system hostname when available, otherwise localhost.

Port

The actual port assigned after the server has been started.

  • Key: port

  • Environment: development

  • Location: ./config/environments/development/server.json

  • Type: integer

  • Defaults to:

    {
    +  "port": 1337
    +}
    +

Notes:

  • You don't need to specify a host in a production environment.
  • When no port is configured or set, Strapi will look for the process.env.PORT +value. If no port specified, the port will be 1337.

Front-end URL

This is the URL of your front-end application.

This config key is useful when you don't use the ./public directory for your +assets or when you run your automation tools such as Gulp or Grunt on an other port.

This address can be resourceful when you need to redirect the user after he +logged in with an authentication provider.

  • Key: frontendUrl

  • Environment: development

  • Location: ./config/environments/development/server.json

  • Type: string

  • Defaults to:

    {
    +  "frontendUrl": ""
    +}
    +

Reload

Enable or disable auto-reload when your application crashes.

  • Key: reload

  • Environment: development

  • Location: ./config/environments/development/server.json

  • Type: object

  • Defaults to:

    {
    +  "reload": {
    +    "timeout": 1000,
    +    "workers": 1
    +  }
    +}
    +

Options:

  • timeout (integer): Set the timeout before killing a worker in ms.
  • workers (integer): Set the number of workers to spawn. +If the workers key is not defined, Strapi will use every free CPU +(recommended in production environement).

Notes:

  • Set to false to disable the auto-reload and clustering features.

Request

Logger

Enable or disable request logs.

  • Key: logger

  • Environment: development

  • Location: ./config/environments/development/server.json

  • Type: boolean

  • Defaults to:

    {
    +  "logger": true
    +}
    +

Notes:

  • Set to false to disable the logger.

Body parser

Parse request bodies.

  • Key: parser

  • Environment: development

  • Location: ./config/environments/development/server.json

  • Type: object

  • Defaults to:

    {
    +  "parser": {
    +    "encode": "utf-8",
    +    "formLimit": "56kb",
    +    "jsonLimit": "1mb",
    +    "strict": true,
    +    "extendTypes": {
    +      "json": [
    +        "application/x-javascript"
    +      ]
    +    }
    +  }
    +}
    +

Options:

  • encode (string): Requested encoding.
  • formLimit (string): Limit of the urlencoded body. +If the body ends up being larger than this limit, a 413 error code is returned.
  • jsonLimit (string): Limit of the JSON body.
  • strict (boolean): When set to true, JSON parser will only accept arrays and objects.
  • extendTypes (array): Support extend types.

Notes:

  • Set to false to disable the body parser.

Response

Gzip

Enable or disable Gzip compression.

  • Key: gzip

  • Environment: development

  • Location: ./config/environments/development/server.json

  • Type: boolean

  • Defaults to:

    {
    +  "gzip": true
    +}
    +

Notes:

  • Set to false to disable Gzip.

Response time header

The X-Response-Time header records the response time for requests in HTTP servers. +The response time is defined here as the elapsed time from when a request enters the application +to when the headers are written out to the client.

  • Key: responseTime

  • Environment: development

  • Location: ./config/environments/development/reponse.json

  • Type: boolean

  • Defaults to:

    {
    +  "responseTime": true
    +}
    +

Notes:

  • Set to false to disable the response time header.

Databases

Strapi comes installed with a powerful ORM/ODM called Waterline, a datastore-agnostic tool that +dramatically simplifies interaction with one or more databases.

It provides an abstraction layer on top of the underlying database, allowing you to easily query +and manipulate your data without writing vendor-specific integration code.

  • Key: orm

  • Environment: development

  • Location: ./config/environments/development/databases.json

  • Type: object

  • Defaults to:

    {
    +  "orm": {
    +    "adapters": {
    +      "disk": "sails-disk"
    +    },
    +    "defaultConnection": "default",
    +    "connections": {
    +      "default": {
    +        "adapter": "disk",
    +        "filePath": ".tmp/",
    +        "fileName": "default.db",
    +        "migrate": "alter"
    +      },
    +      "permanent": {
    +        "adapter": "disk",
    +        "filePath": "./data/",
    +        "fileName": "permanent.db",
    +        "migrate": "alter"
    +      }
    +    }
    +  }
    +}
    +

Options:

  • adapters (object): Association between a connection and the adapter to use.
  • defaultConnection (string): The default connection will be used if the +connection key of a model is empty or missing.
  • connections (object): Options of the connection. +Every adapter has its own options such as host, port, database, etc. +The migrate option controls how Strapi will attempt to automatically +rebuild the tables/collections/sets/etc. in your schema. +
    • safe: never auto-migrate database(s).
    • alter: auto-migrate database(s), but attempt to keep existing data.
    • drop: drop all data and rebuild models every time your application starts.

Notes:

  • When your Strapi application starts, the Waterline ORM validates all of the data in your database. +This migrate flag tells waterline what to do with data when the data is corrupt. +You can set this flag to safe which will ignore the corrupt data and continue to start.
  • By using drop, or even alter, you risk losing your data. Be careful. +Never use drop or alter with a production dataset. +Additionally, on large databases alter may take a long time to complete at startup. +This may cause the start process to appear to hang.

Security

Sessions

Since HTTP driven applications are stateless, sessions provide a way to store information +about the user across requests.

Strapi provides "guest" sessions, meaning any visitor will have a session, +authenticated or not. If a session is new a Set-Cookie will be produced regardless +of populating the session.

Strapi only supports cookie sessions, for now.

  • Key: session

  • Environment: development

  • Location: ./config/environments/development/security.json

  • Type: object

  • Defaults to:

    {
    +  "session": {
    +    "key": "myApp",
    +    "secretKeys": [
    +      "mySecretKey1"
    +    ],
    +    "maxAge": 86400000
    +  }
    +}
    +

Options:

  • key (string): The cookie name.
  • secretKeys (array): Keys used to encrypt the session cookie.
  • maxAge (integer): Sets the time in seconds for when a cookie will be deleted.

Notes:

  • Set to false to disable sessions.

Cross Site Request Forgery (CSRF) headers

CSRF is a type of attack which forces an end user to execute unwanted actions on a web +application backend with which he/she is currently authenticated.

Strapi bundles optional CSRF protection out of the box.

  • Key: csrf

  • Environment: development

  • Location: ./config/environments/development/security.json

  • Type: object

  • Defaults to:

    {
    +  "csrf": false
    +}
    +

Options:

  • key (string): The name of the CSRF token added to the model. +Defaults to _csrf.
  • secret (string): The key to place on the session object which maps to the server side token. +Defaults to _csrfSecret.

Notes:

  • Set to false to disable CSRF headers.
  • If you have existing code that communicates with your Strapi backend via POST, PUT, or DELETE +requests, you'll need to acquire a CSRF token and include it as a parameter or header in those requests.

Content Security Policy (CSP) headers

Content Security Policy (CSP) is a W3C specification for instructing the client browser as to +which location and/or which type of resources are allowed to be loaded.

This spec uses "directives" to define a loading behaviors for target resource types. +Directives can be specified using HTTP response headers or or HTML Meta tags.

  • Key: csp

  • Environment: development

  • Location: ./config/environments/development/security.json

  • Type: object

  • Defaults to:

    {
    +  "csp": false
    +}
    +

Options:

  • policy (object): Object definition of policy.
  • reportOnly (boolean): Enable report only mode.
  • reportUri (string): URI where to send the report data.

Notes:

  • Set to false to disable CSP headers.

X-Frame-Options headers

Enables X-Frame-Options headers to help prevent Clickjacking.

  • Key: xframe

  • Environment: development

  • Location: ./config/environments/development/security.json

  • Type: string

  • Defaults to:

    {
    +  "xframe": "SAMEORIGIN"
    +}
    +

Notes:

  • The string is the value for the header: DENY, SAMEORIGIN or ALLOW-FROM.
  • Set to false to disable X-Frame-Options headers.

Platform for Privacy Preferences

Platform for Privacy Preferences (P3P) is a browser/web standard designed to facilitate +better consumer web privacy control. Currently out of all the major browsers, it is only +supported by Internet Explorer. It comes into play most often when dealing with legacy applications.

  • Key: p3p

  • Environment: development

  • Location: ./config/environments/development/security.json

  • Type: string

  • Defaults to:

    {
    +  "p3p": false
    +}
    +

Notes:

  • The string is the value of the compact privacy policy.
  • Set to false to disable P3P.

HTTP Strict Transport Security

Enables HTTP Strict Transport Security for the host domain.

The preload flag is required for HSTS domain submissions to Chrome's HSTS preload list.

  • Key: hsts

  • Environment: development

  • Location: ./config/environments/development/security.json

  • Type: object

  • Defaults to:

    {
    +  "hsts": {
    +    "maxAge": 31536000,
    +    "includeSubDomains": true
    +  }
    +}
    +

Options:

  • maxAge (integer): Number of seconds HSTS is in effect.
  • includeSubDomains (boolean): Applies HSTS to all subdomains of the host.

Notes:

  • Set to false to disable HSTS.

X-XSS-Protection headers

Cross-site scripting (XSS) is a type of attack in which a malicious agent manages to inject +client-side JavaScript into your website, so that it runs in the trusted environment of your users' browsers.

Enables X-XSS-Protection headers to help prevent cross site scripting (XSS) attacks in older IE browsers (IE8).

  • Key: xssProtection

  • Environment: development

  • Location: ./config/environments/development/security.json

  • Type: object

  • Defaults to:

    {
    +  "xssProtection": false
    +}
    +

Options:

  • enabled (boolean): If the header is enabled or not.
  • mode (string): Mode to set on the header.

Notes:

  • Set to false to disable HTTP Strict Transport Security.

Cross-Origin Resource Sharing (CORS)

Cross-origin resource sharing (CORS) is a mechanism that allows restricted resources +(e.g. fonts, JavaScript, etc.) on a web page to be requested from another domain outside +the domain from which the resource originated.

  • Key: cors

  • Environment: development

  • Location: ./config/environments/development/security.json

  • Type: object

  • Defaults to:

    {
    +  "cors": {
    +    "origin": true,
    +    "expose": [
    +      "WWW-Authenticate",
    +      "Server-Authorization"
    +    ],
    +    "maxAge": 31536000,
    +    "credentials": true,
    +    "methods": [
    +      "GET",
    +      "POST",
    +      "PUT",
    +      "DELETE",
    +      "OPTIONS",
    +      "HEAD"
    +    ],
    +    "headers": [
    +      "Content-Type",
    +      "Authorization"
    +    ]
    +  }
    +}
    +

Options:

  • origin (string|boolean): Configures the Access-Control-Allow-Origin CORS header. +Expects a string (ex: http://example.com) or a boolean. +Set to true to reflect the request origin, as defined by req.header('Origin'). +Set to false to disable CORS.
  • expose (array): Configures the Access-Control-Expose-Headers CORS header. +Set this to pass the header, otherwise it is omitted.
  • maxAge (integer): Configures the Access-Control-Max-Age CORS header. +Set to an integer to pass the header, otherwise it is omitted.
  • credentials (boolean): Configures the Access-Control-Allow-Credentials CORS header. +Set to true to pass the header, otherwise it is omitted.
  • methods (array): Configures the Access-Control-Allow-Methods CORS header.
  • headers (array): Configures the Access-Control-Allow-Headers CORS header. +If not specified, defaults to reflecting the headers specified in the request's +Access-Control-Request-Headers header.

Notes:

  • Set to false to disable CORS.

Secure Sockets Layer (SSL)

Secure Sockets Layer (SSL), is a cryptographic protocol designed to provide communications security +over a computer network.

This configuration enforce SSL for your application.

  • Key: ssl

  • Environment: development

  • Location: ./config/environments/development/security.json

  • Type: object

  • Defaults to:

    {
    +  "ssl": false
    +}
    +

Options:

  • disabled (boolean): If true, this middleware will allow all requests through.
  • trustProxy (boolean): If true, trust the X-Forwarded-Proto header.

Notes:

  • Set to false to disable SSL.

IP filter

The IP filter configuration allows you to whitelist or blacklist specific or range IP addresses.

The blacklisted IP addresses won't have access to your web application at all.

  • Key: ip

  • Environment: development

  • Location: ./config/environments/development/security.json

  • Type: object

  • Defaults to:

    {
    +  "ip": {
    +    "whiteList": [],
    +    "blackList": []
    +  }
    +}
    +

Options:

  • whiteList (array): IP addresses allowed.
  • blackList (array): IP addresses forbidden.

Notes:

  • Set to false to disable IP filter.

Proxy

A proxy server is a server that acts as an intermediary for requests from clients +seeking resources from other servers.

Request your server, fetch the proxy URL you typed and return.

  • Key: proxy

  • Environment: development

  • Location: ./config/environments/development/security.json

  • Type: string

  • Defaults to:

    {
    +  "proxy": false
    +}
    +

Notes:

  • The string will fetch the host and return.
  • Set to false to disable the proxy security.
+ + + diff --git a/docs/.vuepress/dist/1.x.x/context.html b/docs/.vuepress/dist/1.x.x/context.html new file mode 100644 index 0000000000..47278b7de2 --- /dev/null +++ b/docs/.vuepress/dist/1.x.x/context.html @@ -0,0 +1,67 @@ + + + + + + Context | Strapi Docs + + + + + + + +

Context

A Strapi Context encapsulates Node's request and response objects +into a single object which provides many helpful methods for writing +web applications and APIs.

These operations are used so frequently in HTTP server development +that they are added at this level instead of a higher level framework, +which would force middleware to re-implement this common functionality.

A Context is created per request, and is referenced in middleware +as the receiver, or the this identifier, as shown in the following +snippet:

strapi.app.use(function * () {
+  this; // is the Context
+  this.request; // is a Strapi Request
+  this.response; // is a Strapi Response
+});
+

Many of the context's accessors and methods simply delegate to their ctx.request or ctx.response +equivalents for convenience, and are otherwise identical. For example ctx.type and ctx.length +delegate to the response object, and ctx.path and ctx.method delegate to the request.

API

Context specific methods and accessors.

ctx.req

Node's request object.

ctx.res

Node's response object.

Bypassing Strapi's response handling is not supported. Avoid using the following Node properties:

  • res.statusCode
  • res.writeHead()
  • res.write()
  • res.end()

ctx.request

A Strapi Request object.

ctx.response

A Strapi Response object.

ctx.state

The recommended namespace for passing information through middleware and to your front-end views.

this.state.user = yield User.find(id);
+

ctx.app

Application instance reference.

ctx.cookies.get(name, [options])

Get cookie name with options:

  • signed the cookie requested should be signed

Strapi uses the cookies module where options are simply passed.

ctx.cookies.set(name, value, [options])

Set cookie name to value with options:

  • signed sign the cookie value
  • expires a Date for cookie expiration
  • path cookie path, /' by default
  • domain cookie domain
  • secure secure cookie
  • httpOnly server-accessible cookie, true by default

Strapi uses the cookies module where options are simply passed.

ctx.throw([msg], [status], [properties])

Helper method to throw an error with a .status property +defaulting to 500 that will allow Strapi to respond appropriately. +The following combinations are allowed:

this.throw(403);
+this.throw('name required', 400);
+this.throw(400, 'name required');
+this.throw('something exploded');
+

For example this.throw('name required', 400) is equivalent to:

const err = new Error('name required');
+err.status = 400;
+throw err;
+

Note that these are user-level errors and are flagged with +err.expose meaning the messages are appropriate for +client responses, which is typically not the case for +error messages since you do not want to leak failure +details.

You may optionally pass a properties object which is merged into the error as-is, +useful for decorating machine-friendly errors which are reported to the requester upstream.

this.throw(401, 'access_denied', { user: user });
+this.throw('access_denied', { user: user });
+

Strapi uses http-errors to create errors.

ctx.assert(value, [msg], [status], [properties])

Helper method to throw an error similar to .throw() +when !value. Similar to Node's assert() +method.

this.assert(this.state.user, 401, 'User not found. Please login!');
+

Strapi uses http-assert for assertions.

ctx.respond

To bypass Strapi's built-in response handling, you may explicitly set this.respond = false;. +Use this if you want to write to the raw res object instead of letting Strapi handle +the response for you.

Note that using this is not supported by Strapi. This may break intended functionality +of Strapi middleware and Strapi itself. Using this property is considered a hack and is +only a convenience to those wishing to use traditional fn(req, res) functions and middleware +within Strapi.

Request aliases

The following accessors and alias Request equivalents:

  • ctx.header
  • ctx.headers
  • ctx.method
  • ctx.method=
  • ctx.url
  • ctx.url=
  • ctx.originalUrl
  • ctx.origin
  • ctx.href
  • ctx.path
  • ctx.path=
  • ctx.query
  • ctx.query=
  • ctx.querystring
  • ctx.querystring=
  • ctx.host
  • ctx.hostname
  • ctx.fresh
  • ctx.stale
  • ctx.socket
  • ctx.protocol
  • ctx.secure
  • ctx.ip
  • ctx.ips
  • ctx.subdomains
  • ctx.is()
  • ctx.accepts()
  • ctx.acceptsEncodings()
  • ctx.acceptsCharsets()
  • ctx.acceptsLanguages()
  • ctx.get()

Response aliases

The following accessors and alias Response equivalents:

  • ctx.body
  • ctx.body=
  • ctx.status
  • ctx.status=
  • ctx.message
  • ctx.message=
  • ctx.length=
  • ctx.length
  • ctx.type=
  • ctx.type
  • ctx.headerSent
  • ctx.redirect()
  • ctx.attachment()
  • ctx.set()
  • ctx.append()
  • ctx.remove()
  • ctx.lastModified=
  • ctx.etag=
+ + + diff --git a/docs/.vuepress/dist/1.x.x/customization.html b/docs/.vuepress/dist/1.x.x/customization.html new file mode 100644 index 0000000000..3af3965093 --- /dev/null +++ b/docs/.vuepress/dist/1.x.x/customization.html @@ -0,0 +1,53 @@ + + + + + + Customization | Strapi Docs + + + + + + + +

Customization

In keeping with the Node.js philosophy, Strapi aims to keep its core as small +as possible, delegating all but the most critical functions to separate modules.

Generators are designed to make it easier to customize the $ strapi new +and $ strapi generate command-line tools, and provide better support +for different user features, custom admin panel, configuration options, +view engines, etc.

Custom generators are linked to your machine aiming to have your personal +configuration and features at any time, for every application.

You can edit your custom generators inside the .strapirc file at $HOME.

First, make sure you this file exists:

$ strapi config
+

This file should look like this:

{
+  "generators": {
+
+  }
+}
+

At this time, you don't have any custom generators on your machine.

In your .strapirc file, a custom generator is an object with three keys:

  • repository: the Git repository to clone.
  • remote: the current remote to pull updates from.
  • branch: the branch you want to pull updates from.

For example, to add a custom blog generator, follow this:

{
+  "generators": {
+    "blog": {
+      "repository": "git@github.com:username/strapi-generate-blog.git",
+      "remote": "origin",
+      "branch": "master"
+    }
+  }
+}
+

Once you have updated your .strapirc file, you need to clone and/or update your +generators. To do so, just execute:

$ strapi update
+

This command will clone every new repository written in your configuration file +and pull the latest updates for the other ones.

Then, you can generate your blog files inside your project with:

$ strapi generate blog
+
+ + + diff --git a/docs/.vuepress/dist/1.x.x/email.html b/docs/.vuepress/dist/1.x.x/email.html new file mode 100644 index 0000000000..8123b992e3 --- /dev/null +++ b/docs/.vuepress/dist/1.x.x/email.html @@ -0,0 +1,85 @@ + + + + + + Email | Strapi Docs + + + + + + + +

Email

Strapi contains a set of tools to send emails. This part is based on the +famous email node module: Nodemailer.

Email config

To change the STMP config, edit the ./api/email/config/environments/development/smtp.json file.

{
+  "smtp": {
+    "from": "test<no-reply@test.com>",
+    "service": {
+      "name": "",
+      "user": "",
+      "pass": ""
+    }
+  }
+}
+

Options:

  • from (string): The email address used to send emails.
  • service (object): The SMTP service info: +
    • name (string): Name of the service used to send emails (eg. Gmail).
    • user (string): Username of the service used (eg. john@gmail.com).
    • pass (string): Password of the username used (eg. 12356).

Email service

The email service allows you to easily send emails from anywhere in your application.

Usage as a promise (yieldable) :

strapi.api.email.services.email.send({
+    from: 'contact@company.com', // Sender (defaults to `strapi.config.smtp.from`).
+    to: ['john@doe.com'], // Recipients list.
+    html: '<p>Hello John</p>', // HTML version of the email content.
+    text: 'Hello John' // Text version of the email content.
+  })
+  .then(function (data) {
+    console.log(data);
+  })
+  .catch(function (err) {
+    console.log(err);
+  });
+

Usage with a callback :

strapi.api.email.services.email.send({
+    from: 'contact@company.com', // Sender (defaults to `strapi.config.smtp.from`).
+    to: ['john@doe.com'], // Recipients list.
+    html: '<p>Hello John</p>', // HTML version of the email content.
+    text: 'Hello John' // Text version of the email content.
+  }, function (err, data) {
+    if (err) {
+      console.log(err);
+    } else {
+      console.log(data);
+    }
+  });
+

Email API

The email API is a simple API which can be used from your client (front-end, mobile...) application.

Route used to send emails:

POST /email
+

Request payload:

{
+  from: 'contact@company.com', // Optional : sender (defaults to `strapi.config.smtp.from`).
+  to: ['john@doe.com'], // Recipients list.
+  html: '<p>Hello John</p>', // HTML version of the email content.
+  text: 'Hello John' // Text version of the email content.
+}
+

Response payload:

{
+  "sent": true,
+  "from": "contact@company.com",
+  "to": "john@doe.com",
+  "html": "<p>Hello John</p>",
+  "text": "Hello John",
+  "template": "default",
+  "lang": "en",
+  "createdAt": "2015-10-21T09:10:36.486Z",
+  "updatedAt": "2015-10-21T09:10:36.871Z",
+  "id": 2
+}
+

Email model

Each sent email is registered in the database. So you can retrieve them whenever +you want. However, you can disable this option by overriding the email service logic.

+ + + diff --git a/docs/.vuepress/dist/1.x.x/graphql.html b/docs/.vuepress/dist/1.x.x/graphql.html new file mode 100644 index 0000000000..f20993127e --- /dev/null +++ b/docs/.vuepress/dist/1.x.x/graphql.html @@ -0,0 +1,79 @@ + + + + + + GraphQL | Strapi Docs + + + + + + + +

GraphQL

GraphQL is a data querying language that allows you to execute complex nested +requests between your clients and server applications.

Configuration

By default, GraphQL is enabled and the HTTP endpoint is /graphql. +You can override this settings in the ./config/general.json file.

{
+  "graphql": {
+    "enabled": true,
+    "route": "/graphql"
+  }
+}
+

Options:

  • enabled (boolean): Enabled or disabled GraphQL.
  • route (string): Change GraphQL endpoint.

Note: If GraphQL is disabled, the GraphQL global variable is not exposed.

Execute simple query

Programmatically

Strapi takes over GraphQL natively. We added a function called query to execute +your query without given as a parameters the GraphQL schemas each time.

An example of how to use query function:

// Build your query
+const query = '{ users{firstName lastName posts{title}} }';
+
+// Execute the query
+graphql.query(query)
+  .then(function (result) {
+    console.log(result);
+  })
+  .catch(function (error) {
+    console.log(error);
+  });
+

And the JSON result:

{
+  "users": [{
+    "firstname": "John",
+    "lastname": "Doe",
+    "posts":[{
+      "title": "First title..."
+    }, {
+      "title": "Second title..."
+    }, {
+      "title": "Third title..."
+    }]    
+  }, {
+    "firstname": "Karl",
+    "lastname": "Doe",
+    "posts":[{
+      "title": "Fourth title..."
+    }]    
+  }]
+}
+

With a HTTP request

Strapi also provides a HTTP GraphQL server to execute request from your front-end application.

An example of how to execute the same request as above with a HTTP request with jQuery.

$.get('http://yourserver.com/graphql?query={ users{firstName lastName posts{title}} }', function (data) {
+  console.log(data);
+});
+

Execute complex queries

Query parameters

If you're using Waterline ORM installed by default with Strapi, you have access to +some Waterline query parameters in your GraphQL query such as sort, limit or skip. +Strapi also provides the start and end parameters to select records between two dates.

This example will return 10 users' records sorted alphabetically by firstName:

const query = '{ users(limit: 10, sort: "firstName ASC"){firstName lastName post{title}} }';
+

You can access to the 10 next users by adding the skip parameter:

const query = '{ users(limit: 10, sort: "firstName ASC", skip: 10){firstName lastName posts{title}} }';
+

And you also can select those records in a period between two dates with the start and end parameters:

const query = '{ users(limit: 10, sort: "firstName ASC", skip: 10, start: "09/21/2015", end:" 09/22/2015"){firstName lastName posts{title}} }';
+

Useful functions

Strapi comes with a powerful set of useful functions such as getLatest<Model>, getFirst<Model> and count<Model>.

Returns the 5 latest users from the September 27th 2015 at 8:59:59 PM:

const query = '{ getLatestUsers(count: 5, start: "9/27/2015 20:59:59"){firstName lastName posts{title}} }';
+

Returns the 5 first users:

const query = '{ getFirstUsers(count: 5){firstName lastName posts{title}} }';
+

Returns the number of subscribers the September 28th 2015:

const query = '{ countUsers(start: "9/28/2015", end: "9/28/2015") }';
+
+ + + diff --git a/docs/.vuepress/dist/1.x.x/index.html b/docs/.vuepress/dist/1.x.x/index.html new file mode 100644 index 0000000000..655df6585d --- /dev/null +++ b/docs/.vuepress/dist/1.x.x/index.html @@ -0,0 +1,45 @@ + + + + + + Strapi | Strapi Docs + + + + + + + +

Strapi

Build Status Slack Status

Strapi is an open-source Node.js rich framework for building applications and services.

Strapi enables developers to focus on writing reusable application logic instead of spending time +building infrastructure. It is designed for building practical, production-ready Node.js applications +in a matter of hours instead of weeks.

The framework sits on top of Koa. Its ensemble of small modules work +together to provide simplicity, maintainability, and structural conventions to Node.js applications.

DISCLAIMER: This version is maintained for criticals issues only.

Getting started in a minute

Installation

Install the latest stable release with the npm command-line tool:

$ npm install strapi -g
+

We advise you to use our Studio to build APIs. To do so, you need to create a Strapi account. +Go to the Strapi Studio to signup. +Studio is dedicated to developers to build applications without writing +any single line of code thanks to its powerful set of tools.

After creating an account on the Strapi Studio, you are able to link your machine to your +Strapi Studio account to get access to all features offered by the Strapi ecosystem. +Use your Strapi account credentials.

$ strapi login
+

Create your first project

You now are able to use the Strapi CLI. Simply create your first application and start the server:

$ strapi new <appName>
+

Note that you can generate a dry application using the dry option:

$ strapi new <appName> --dry
+

This will generate a Strapi application without:

  • the built-in user, email and upload APIs,
  • the grant hook,
  • the open-source admin panel,
  • the Waterline ORM (waterline and blueprints hooks disabled),
  • the Strapi Studio connection (studio hook disabled).

This feature allows you to only use Strapi for your HTTP server structure if you want to.

Start your application

$ cd <appName>
+$ strapi start
+

The default home page is accessible at http://localhost:1337/.

Create your first API

The Strapi ecosystem offers you two possibilities to create a complete RESTful API.

Via the CLI

$ strapi generate api <apiName>
+

For example, you can create a car API with a name (name), year (year) and +the license plate (license) with:

$ strapi generate api car name:string year:integer license:string
+

Via the Strapi Studio

The Strapi Studio allows you to easily build and manage your application environment +thanks to a powerful User Interface.

Log into the Strapi Studio with your user account (http://studio.strapi.io) +and follow the instructions to start the experience.

Strapi Studio Simply manage your APIs and relations thanks to the Strapi Studio.

Manage your data

Strapi comes with a simple but yet powerful dashboard.

Strapi Dashboard Create, read, update and delete your data.

Strapi Dashboard Manage user settings, login, registration, groups and permissions on the fly.

Resources

+ + + diff --git a/docs/.vuepress/dist/1.x.x/internationalization.html b/docs/.vuepress/dist/1.x.x/internationalization.html new file mode 100644 index 0000000000..19b317b9d7 --- /dev/null +++ b/docs/.vuepress/dist/1.x.x/internationalization.html @@ -0,0 +1,74 @@ + + + + + + Internationalization | Strapi Docs + + + + + + + +

Internationalization

Strapi provides built-in support for detecting user language preferences and translating +static words/sentences.

i18n settings

Settings for localization/internationalization may be configured in strapi.config.i18n. +The most common reason you'll need to modify these settings is to edit the list of your +application's supported locales and/or the location of your translation stringfiles.

Locales

Strapi reads JSON-formatted translation files from your project's ./config/locales +directory. Each file corresponds with a locale (usually a language) that your backend will support. +These files contain locale-specific strings (as JSON key-value pairs) that you can use in your +views, controllers, etc.

When your server is in production mode it will read these files only once and then cache +the result. It will not write any updated strings when in production mode.

Otherwise, the files will be read on every instantiation of the i18n object. +Additionally newly-detected strings will be automatically added, and written out, +to the locale JSON files.

These files contain locale-specific strings (as JSON key-value pairs) that you can use in your views, +controllers, etc. Here is an example locale file (./config/locales/fr.json):

{
+  "Hello!": "Bonjour!",
+  "Hello %s, how are you today?": "Bonjour %s, comment allez-vous aujourd'hui ?"
+}
+

Note that the keys in your stringfiles are case sensitive and require exact matches. +There are a few different schools of thought on the best approach here, and it really depends on +who/how often you'll be editing the stringfiles in the future. Especially if you'll be +editing the translations by hand, simpler, all-lowercase key names may be preferable for maintainability.

For example, here's another pass at ./config/locales/fr.json:

{
+  "hello": "Bonjour!",
+  "hello-how-are-you-today": "Bonjour %s, comment allez-vous aujourd'hui ?"
+}
+

And here's ./config/locales/en.json:

{
+  "hello": "Hello!",
+  "hello-how-are-you-today": "Hello %s, how are you today?"
+}
+

You can also nest locale strings. But a better approach would be to use . to represent nested strings. +For example, here's the list of labels for the index page of a user controller:

{
+  "user.index.label.id": "User ID",
+  "user.index.label.name": "User Name"
+}
+

Translate responses

Locales are accessible from everywhere in your application.

this.body = this.i18n.__('hello-how-are-you-today', 'John');
+// => "Hello John, how are you today?"
+

Different plural forms are supported as a response to count with this.i18n.__n(one, other, count).

Use this.i18n.__n() as you would use this.i18.__() directly:

this.body = this.i18n.__n('%s cat', '%s cats', 1);
+// => "1 cat"
+
+this.body = this.i18n.__n('%s cat', '%s cats', 3);
+// => "3 cats"
+

Or from locales:

{
+  "catEat": {
+    "one": "%d cat eats the %s",
+    "other": '%d cats eat the %s'
+  }
+}
+
this.body = this.i18n.__n('catEat', 10, 'mouse');
+// => "10 cats eat the mouse"
+
+ + + diff --git a/docs/.vuepress/dist/1.x.x/introduction.html b/docs/.vuepress/dist/1.x.x/introduction.html new file mode 100644 index 0000000000..2d27bab081 --- /dev/null +++ b/docs/.vuepress/dist/1.x.x/introduction.html @@ -0,0 +1,80 @@ + + + + + + Introduction | Strapi Docs + + + + + + + +

Introduction


Important Note: Strapi 1.x is on maintenance only. Development focuses on the upcoming Strapi 3.0.


Strapi is an open-source Node.js rich framework for building applications and services.

Strapi enables developers to focus on writing reusable application logic instead of spending time +building infrastructure. It is designed for building practical, production-ready Node.js applications +in a matter of hours instead of weeks.

The framework sits on top of Koa. Its ensemble of small modules work +together to provide simplicity, maintainability, and structural conventions to Node.js applications.

Getting Started

Installation

Install the latest stable release with the npm command-line tool:

$ npm install strapi -g
+

Create your first project

You now are able to use the Strapi CLI. Simply create your first application and start the server:

$ strapi new <appName>
+$ cd <appName>
+$ strapi start
+

The default home page is accessible at http://localhost:1337/.

Create your first API

$ strapi generate api <apiName>
+

For example, you can create a car API with a name (name), year (year) and +the license plate (license) with:

$ strapi generate api car name:string year:integer license:string
+

Alternatives

Dry Application

Note that you can generate a dry application using the dry option:

$ strapi new <appName> --dry
+

This will generate a Strapi application without:

  • the built-in user, email and upload APIs,
  • the grant hook,
  • the open-source admin panel,
  • the Waterline ORM (waterline and blueprints hooks disabled),
  • the Strapi Studio connection (studio hook disabled).

This feature allows you to only use Strapi for your HTTP server structure if you want to.

Create an API via the Strapi Studio

The Strapi Studio allows you to easily build and manage your application environment +thanks to a powerful User Interface.

Log into the Strapi Studio with your user account (http://studio.strapi.io) +and follow the instructions to start the experience.

We advise you to use our Studio to build APIs. To do so, you need to create a Strapi account. +Go to the Strapi Studio to signup. +Studio is dedicated to developers to build applications without writing +any single line of code thanks to its powerful set of tools.

After creating an account on the Strapi Studio, you are able to link your machine to your +Strapi Studio account to get access to all features offered by the Strapi ecosystem. +Use your Strapi account credentials.

$ strapi login
+

Key-features

100% JavaScript

Building on top of Strapi means your application is written entirely in JavaScript, +the language you and your team are already using in the browser.

Since you spend less time context-shifting, you're able to write code in a more consistent style, +which makes development more productive.

The entire Strapi framework is written in ES2015.

Getting started quickly

Strapi provides a robust layer for fundamental web applications to help you write your business +logic, without obscuring Node.js features that you know and love. Our goal is to make writing +business logic much easier than other frameworks.

Auto-generate REST APIs

Strapi comes with a generator that help jumpstart your application's backend without writing any code. Just run:

$ strapi generate api car
+

and you'll get an API that lets you read, paginate, sort, filter, create, destroy, update, +and associate cars.

Security

We take security very seriously. This is why Strapi comes with several security layers that just work +depending on your needs. Strapi provides configuration for CORS, CSRF, CSP, X-Frame-Options, XSS, HSTS, +HTTPS, SSL, proxy, IP filtering and ships reusable security policies.

No matter what you need to secure, Strapi is the right tool to make it right.

Datastore-agnostic

Strapi comes installed with a powerful ORM/ODM called Waterline, a datastore-agnostic tool that +dramatically simplifies interaction with one or more databases.

It provides an abstraction layer on top of the underlying database, allowing you to easily query +and manipulate your data without writing vendor-specific integration code.

Strapi offers a new take on the familiar relational model, aimed at making data modeling more practical. +You can do all the same things you might be used to (one-to-many, many-to-many), but you can also assign +multiple named associations per-model. Better yet, you can assign different models to different databases, +and your associations/joins will still work, even across NoSQL and relational boundries.

Strapi has no problem implicitly/automatically joining a SQL table with a NoSQL collection and vice versa.

Front-end agnostic

Strapi is compatible with any front-end strategy; whether it's Angular, Backbone, Ember, +iOS, Android, Windows Phone, or something else that hasn't been invented yet.

Plus it's easy to serve up the same API to be consumed by another web service or community of developers.

Convention over configuration

Convention over configuration is a consistent approach makes developing applications more +predictable and efficient for everybody involved.

If anyone on your team has worked with frameworks, Strapi will feel pretty familiar. +Not only that, but they can look at a Strapi project and know, generally, how to code up the basic +patterns they've implemented over and over again in the past; whether their background. +What about your second application, or your third? Each time you create a new Strapi application, +you start with a sane, familiar boilerplate that makes you more productive.

Configuration files give you extra opportunities for human error.

In many cases, you'll even be able to recycle some of your code.

Error Handling

By default outputs all errors to stderr unless NODE_ENV is test. +To perform custom error-handling logic such as centralized logging you can add an "error" event listener:

strapi.app.on('error', function (err) {
+  strapi.log.error('server error', err);
+});
+

If an error in the req/res cycle and it is not possible to respond to the client, +the Context instance is also passed:

strapi.app.on('error', function (err, ctx) {
+  strapi.log.error('server error', err, ctx);
+});
+

When an error occurs and it is still possible to respond to the client, +aka no data has been written to the socket, Strapi will respond appropriately with +a 500 "Internal Server Error". In either case an app-level "error" is emitted for logging purposes.

Different environments

Strapi has built in support for the idea of having a different set of settings for each environment. +Real applications have this too, but often the framework around them doesn't accommodate it and +you end up having to swap configuration files in and out to achieve the same effect.

Loose coupling

Strapi is flexible enough to allow you to explore and create when you have the time to but also +provides automation tools when you don't.

+ + + diff --git a/docs/.vuepress/dist/1.x.x/logging.html b/docs/.vuepress/dist/1.x.x/logging.html new file mode 100644 index 0000000000..85435164b9 --- /dev/null +++ b/docs/.vuepress/dist/1.x.x/logging.html @@ -0,0 +1,73 @@ + + + + + + Logging | Strapi Docs + + + + + + + +

Logging

Strapi comes with a simple and useful built-in logger. +Its usage is purposely very similar to console.log(), but with a handful of +extra features; namely support for multiple log levels with colorized, +prefixed console output.

The logger is accessible through the strapi object directly with strapi.log.

You can work with this logger in the same way that you work with the default logger:

strapi.log.info('Logs work!');
+

Logging with Metadata

In addition to logging string messages, the logger will also optionally log additional +JSON metadata objects. Adding metadata is simple:

strapi.log.info('Test log message', {
+  anything: 'This is metadata'
+});
+

String interpolation

The log method provides the same string interpolation methods like util.format.

This allows for the following log messages.

strapi.log.info('test message %s', 'my string');
+// => info: test message my string
+
strapi.log.info('test message %d', 123);
+// => info: test message 123
+
strapi.log.info('test message %j', {
+  number: 123
+}, {});
+// => info: test message {"number":123}
+// => meta = {}
+
strapi.log.info('test message %s, %s', 'first', 'second', {
+  number: 123
+});
+// => info: test message first, second
+// => meta = {number: 123}
+
strapi.log.info('test message', 'first', 'second', {
+  number: 123
+});
+// => info: test message first second
+// => meta = {number: 123}
+
strapi.log.info('test message %s, %s', 'first', 'second', {
+  number: 123
+}, function() {});
+// => info: test message first, second
+// => meta = {number: 123}
+// => callback = function() {}
+
strapi.log.info('test message', 'first', 'second', {
+  number: 123
+}, function() {});
+// => info: test message first second
+// => meta = {number: 123}
+// => callback = function() {}
+

Logging levels

Setting the level for your logging message can be accomplished by using +the level specified methods defined.

strapi.log.debug('This is a debug log');
+strapi.log.info('This is an info log');
+strapi.log.warn('This is a warning log');
+strapi.log.error('This is an error log ');
+
+ + + diff --git a/docs/.vuepress/dist/1.x.x/models.html b/docs/.vuepress/dist/1.x.x/models.html new file mode 100644 index 0000000000..f6819208ad --- /dev/null +++ b/docs/.vuepress/dist/1.x.x/models.html @@ -0,0 +1,416 @@ + + + + + + Models | Strapi Docs + + + + + + + +

Models

Strapi comes installed with a powerful Object-Relational-Mapper (ORM) called Waterline, +a datastore-agnostic tool that dramatically simplifies interaction with one or more databases.

Models represent a structure of data which requires persistent storage. The data may live in any data-store +but is interfaced in the same way. This allows your users to live in PostgreSQL and your user preferences +to live in MongoDB and you will interact with the data models in the exact same way.

If you're using MySQL, a model might correspond to a table. If you're using MongoDB, it might correspond +to a collection. In either case, the goal is to provide a simple, modular way of managing data without +relying on any one type of database.

Models are defined in the ./api/<apiName>/models directory.

Model settings

The following properties can be specified at the top level of your model definition to override +the defaults for that particular model.

For example, this a basic model Pet:

{
+  "identity": "pet",
+  "connection": "mongoDBServer",
+  "schema": true,
+  "attributes": {
+    "name": {
+      "type": "string",
+      "required": true
+    },
+    "gender": {
+      "type": "string",
+      "enum": ["male", "female"]
+    },
+    "age": {
+      "type": "int",
+      "max": 100
+    },
+    "birthDate": {
+      "type": "date"
+    },
+    "breed": {
+      "type": "string"
+    }
+  },
+  "autoPK": true,
+  "autoCreatedAt": true,
+  "autoUpdatedAt": true
+}
+
+

schema

A flag to toggle schemaless or schema mode in databases that support schemaless data structures. +If turned off, this will allow you to store arbitrary data in a record. If turned on, only attributes +defined in the model's attributes object will be stored.

For adapters that don't require a schema, such as MongoDB or Redis, the schema key is set to false.

{
+  "schema": true|false
+}
+

connection

The configured database connection where this model will fetch and save its data. +Defaults to defaultSQLite, the default connection that uses the waterline-sqlite3 adapter.

{
+  "connection": "mongoDBServer"
+}
+

identity

The lowercase unique key for the model. By default, a model's identity is inferred automatically +by lowercasing its filename. You should never change this property on your models.

{
+  "identity": "petModel"
+}
+

globalId

This flag changes the global name by which you can access your model (if the globalization of models +is enabled). You should never change this property on your models.

{
+  "globaId": "pets"
+}
+

For example to access to your model function:

Pets.find().exec(function (error, pets) {
+  if (error) {
+    console.log(error);
+    return false;
+  }
+
+  console.log(pets);
+});
+

autoPK

A flag to toggle the automatic definition of a primary key in your model. +The details of this default primary key vary between adapters. In any case, the primary keys generated +by autoPK will be unique. If turned off no primary key will be created by default, and you will need +to define one manually using primaryKey: true for one of the model attributes.

{
+  "autoPK": true|false
+}
+

autoCreatedAt

A flag to toggle the automatic definition of a createdAt attribute in your model. +By default, createdAt is an attribute which will be automatically set when a record is created with +the current timestamp.

{
+  "autoCreatedAt": true|false
+}
+

autoUpdatedAt

A flag to toggle the automatic definition of a updatedAt attribute in your model. +By default, updatedAt is an attribute which will be automatically set with the current timestamp +every time a record is updated.

{
+  "autoUpdatedAt": true|false
+}
+

tableName

You can define a custom name for the physical collection in your adapter by adding a tableName +attribute. This isn't just for tables. In MySQL, PostgreSQL, Oracle, etc. this setting refers +to the name of the table, but in MongoDB or Redis, it refers to the collection, and so forth. +If no tableName is specified, Waterline will use the model's identity as its tableName.

This is particularly useful for working with pre-existing/legacy databases.

{
+  "tableName": "pets_table"
+}
+

attributes

Model attributes are basic pieces of information about a model. +A model called Pet might have attributes called name, gender, age, +birthday and breed.

Options can be used to enforce various constraints and add special enhancements to model attributes.

type

Specifies the type of data that will be stored in this attribute. One of:

  • string
  • text
  • integer
  • float
  • date
  • datetime
  • boolean
  • binary
  • array
  • json

Defaults to string if not specified.

Validations

Strapi bundles support for automatic validations of your models' attributes. +Any time a record is updated, or a new record is created, the data for each attribute will +be checked against all of your predefined validation rules. This provides a convenient failsafe +to ensure that invalid entries don't make their way into your application's database(s).

Validations are defined directly in your collection attributes.

  • after (date): Checks if string date in this record is after the specified Date. +Must be valid JavaScript Date.
  • alpha (boolean): Checks if string in this record contains only letters (a-zA-Z).
  • alphadashed (boolean): Checks if string in this record contains only numbers and/or dashes.
  • alphanumeric (boolean): Checks if string in this record contains only letters and numbers.
  • alphanumericdashed (boolean): Checks if string in this record contains only numbers and/or +letters and/or dashes.
  • array (boolean): Checks if this record is a valid JavaScript array object. +Strings formatted as arrays will fail.
  • before (date): Checks if string in this record is a date that's before the specified date.
  • binary (boolean): Checks if this record is a valid binary data. Strings will pass.
  • boolean (boolean): Checks if this record is a valid boolean. Strings will fail.
  • contains (string): Checks if string in this record contains the seed.
  • creditcard (boolean): Checks if string in this record is a credit card.
  • date (boolean): Checks if string in this record is a date takes both strings and JavaScript.
  • datetime (boolean): Checks if string in this record looks like a JavaScript datetime.
  • decimal (boolean): Checks if it contains a decimal or is less than 1.
  • email (boolean): Checks if string in this record looks like an email address.
  • empty (boolean): Checks if the entry is empty. Arrays, strings, or arguments objects with +a length of 0 and objects with no +own enumerable properties are considered empty.
  • equals (integer): Checks if string in this record is equal to the specified value. +They must match in both value and type.
  • falsey (boolean): Would a Javascript engine register a value of false on this?.
  • finite (boolean): Checks if given value is, or can be coerced to, a finite number. +This is not the same as native isFinite +which will return true for booleans and empty strings.
  • float (boolean): Checks if string in this record is of the number type float.
  • hexadecimal (boolean): Checks if string in this record is a hexadecimal number.
  • hexColor (boolean): Checks if string in this record is a hexadecimal color.
  • in (array): Checks if string in this record is in the specified array of allowed +string values.
  • int (boolean): Check if string in this record is an integer.
  • integer (boolean): Check if string in this record is an integer. Alias for int.
  • ip (boolean): Checks if string in this record is a valid IP (v4 or v6).
  • ipv4 (boolean): Checks if string in this record is a valid IP v4.
  • ipv6 (boolean): Checks if string in this record is aa valid IP v6.
  • json (boolean): Checks if the record is a JSON.
  • lowercase (boolean): Check if string in this record is in all lowercase.
  • max (integer): max value for an integer.
  • maxLength (integer):
  • min (integer): min value for an integer.
  • minLength (integer):
  • notContains (string): Checks if string in this record doesn't contain the seed.
  • notIn (array): does the value of this model attribute exist inside of the defined +validator value (of the same type). +Takes strings and arrays.
  • notNull (boolean): does this not have a value of null ?.
  • null (boolean): Checks if string in this record is null.
  • number (boolean): Checks if this record is a number. NaN is considered a number.
  • numeric (boolean): Checks if string in this record contains only numbers.
  • object (boolean): Checks if this attribute is the language type of Object. +Passes for arrays, functions, objects, +regexes, new Number(0), and new String('') !
  • regex (regex): Checks if the record matches the specific regex.
  • required (boolean): Must this model attribute contain valid data before a new +record can be created?.
  • string (boolean): Checks if the record is a string.
  • text (boolean): Checks if the record is a text.
  • truthy (boolean): Would a Javascript engine register a value of false on this?
  • undefined (boolean): Would a JavaScript engine register this thing as have the +value undefined?
  • uppercase (boolean): Checks if string in this record is uppercase.
  • url (boolean): Checks if string in this record is a URL.
  • urlish (boolean): Checks if string in this record contains something that looks like +a route, ending with a file extension.
  • uuid (boolean): Checks if string in this record is a UUID (v3, v4, or v5).
  • uuidv3 (boolean): Checks if string in this record is a UUID (v3).
  • uuidv4 (boolean): Checks if string in this record is a UUID (v4).

defaultsTo

When a record is created, if no value was supplied, the record will be created with the specified +defaultsTo value.

"attributes": {
+  "usersGroup": {
+    "type": "string",
+    "defaultsTo": "guess"
+  }
+}
+

autoIncrement

Sets up the attribute as an auto-increment key. When a new record is added to the model, +if a value for this attribute is not specified, it will be generated by incrementing the most recent +record's value by one.

Attributes which specify autoIncrement should always be of type: integer. +Also, bear in mind that the level of support varies across different datastores. +For instance, MySQL will not allow more than one auto-incrementing column per table.

"attributes": {
+  "placeInLine": {
+    "type": "integer",
+    "autoIncrement": true
+  }
+}
+

unique

Ensures no two records will be allowed with the same value for the target attribute. +This is an adapter-level constraint, so in most cases this will result in a unique index on the +attribute being created in the underlying datastore.

Defaults to false if not specified.

"attributes": {
+  "username": {
+    "type": "string",
+    "unique": true
+  }
+}
+

primaryKey

Use this attribute as the the primary key for the record. Only one attribute per model can be the +primaryKey. Defaults to false if not specified.

This should never be used unless autoPK is set to false.

"attributes": {
+  "uuid": {
+    "type": "string",
+    "primaryKey": true,
+    "required": true
+  }
+}
+

enum

A special validation property which only saves data which matches a whitelisted set of values.

"attributes": {
+  "gender": {
+    "type": "string",
+    "enum": ["male", "female"]
+  }
+}
+

size

If supported in the adapter, can be used to define the size of the attribute. +For example in MySQL, size can be specified as a number (n) to create a column with the SQL +data type: varchar(n).

"attributes": {
+  "name": {
+    "type": "string",
+    "size": 24
+  }
+}
+

columnName

Inside an attribute definition, you can specify a columnName to force Waterline to store data +for that attribute in a specific column in the configured connection. +Be aware that this is not necessarily SQL-specific. It will also work for MongoDB fields, etc.

While the columnName property is primarily designed for working with existing/legacy databases, +it can also be useful in situations where your database is being shared by other applications, +or you don't have access permissions to change the schema.

"attributes": {
+  "name": {
+    "type": "string",
+    "columnName": "pet_name"
+  }
+}
+

Associations

With Waterline you can associate models with other models across all data stores. +This means that your users can live in PostgreSQL and their photos can live in MongoDB +and you can interact with the data as if they lived together on the same database. +You can also have associations that live on separate connections or in different databases +within the same adapter.

One-Way associations

A one-way association is where a model is associated with another model. +You could query that model and populate to get the associated model. +You can't however query the associated model and populate to get the associating model.

In this example, we are associating a User with a Pet but not a Pet with a User. +Because we have only formed an association on one of the models, a Pet has no restrictions +on the number of User models it can belong to. If we wanted to, we could change this and +associate the Pet with exactly one User and the User with exactly one Pet.

./api/pet/models/Pet.settings.json:

{
+  "attributes": {
+    "name": {
+      "type": "string",
+      "required": true,
+      "unique": true
+    },
+    "color": {
+      "type": "string",
+      "required": true
+    }
+  }
+}
+

./api/user/models/User.settings.json:

{
+  "attributes": {
+    "name": {
+      "type": "string",
+      "required": true,
+      "unique": true
+    },
+    "color": {
+      "type": "string",
+      "required": true
+    },
+    "pony": {
+      "model": "pet"
+    }
+  }
+}
+

One-to-One associations

A one-to-one association states that a model may only be associated with one other model. +In order for the model to know which other model it is associated with a foreign key must +be included in the record.

In this example, we are associating a Pet with a User. The User may only have one +Pet and viceversa, a Pet can only have one User. However, in order to query this association +from both sides, you will have to create/update both models.

./api/pet/models/Pet.settings.json:

{
+  "attributes": {
+    "name": {
+      "type": "string",
+      "required": true,
+      "unique": true
+    },
+    "color": {
+      "type": "string",
+      "required": true
+    },
+    "owner": {
+      "model": "user"
+    }
+  }
+}
+

./api/user/models/User.settings.json:

{
+  "attributes": {
+    "name": {
+      "type": "string",
+      "required": true,
+      "unique": true
+    },
+    "age": {
+      "type": "integer",
+      "required": true
+    },
+    "pony": {
+      "model": "pet"
+    }
+  }
+}
+

One-to-Many associations

A one-to-many association states that a model can be associated with many other models. +To build this association a virtual attribute is added to a model using the collection property. +In a one-to-many association one side must have a collection attribute and the other side must contain a +model attribute. This allows the many side to know which records it needs to get when a populate is used.

Because you may want a model to have multiple one-to-many associations on another model a via key is +needed on the collection attribute. This states which model attribute on the one side of the association +is used to populate the records.

In this example, a User can have several Pet, but a Pet has only one owner (from the User model).

./api/pet/models/Pet.settings.json:

{
+  "attributes": {
+    "name": {
+      "type": "string",
+      "required": true,
+      "unique": true
+    },
+    "color": {
+      "type": "string",
+      "required": true
+    },
+    "owner": {
+      "model": "user"
+    }
+  }
+}
+

./api/user/models/User.settings.json:

{
+  "attributes": {
+    "name": {
+      "type": "string",
+      "required": true,
+      "unique": true
+    },
+    "age": {
+      "type": "integer",
+      "required": true
+    },
+    "pets": {
+      "collection": "pet",
+      "via": "owner"
+    }
+  }
+}
+

Many-to-Many associations

A many-to-many association states that a model can be associated with many other models +and vice-versa. Because both models can have many related models a new join table will +need to be created to keep track of these relations.

Waterline will look at your models and if it finds that two models both have collection +attributes that point to each other, it will automatically build up a join table for you.

Because you may want a model to have multiple many-to-many associations on another model +a via key is needed on the collection attribute. This states which model attribute on the +one side of the association is used to populate the records.

Using the User and Pet example lets look at how to build a schema where a User may +have many Pet records and a Pet may have multiple owners.

In this example, we will start with an array of users and an array of pets. +We will create records for each element in each array then associate all of the Pets with all +of the Users. If everything worked properly, we should be able to query any User and see that +they own all of the Pets. Furthermore, we should be able to query any Pet and see that +it is owned by every User.

./api/pet/models/Pet.settings.json:

{
+  "attributes": {
+    "name": {
+      "type": "string",
+      "required": true,
+      "unique": true
+    },
+    "color": {
+      "type": "string",
+      "required": true
+    },
+    "owners": {
+      "collection": "user",
+      "via": "pets"
+    }
+  }
+}
+

./api/user/models/User.settings.json:

{
+  "attributes": {
+    "name": {
+      "type": "string",
+      "required": true,
+      "unique": true
+    },
+    "age": {
+      "type": "integer",
+      "required": true
+    },
+    "pets": {
+      "collection": "pet",
+      "via": "owners"
+    }
+  }
+}
+

Lifecycle Callbacks

Lifecycle callbacks are functions you can define to run at certain times in a query. +They are hooks that you can tap into in order to change data.

Strapi exposes a handful of lifecycle callbacks by default.

Callbacks on create

  • beforeValidate: fn(values, cb)
  • afterValidate: fn(values, cb)
  • beforeCreate: fn(values, cb)
  • afterCreate: fn(newlyInsertedRecord, cb)

Callbacks on update

  • beforeValidate: fn(valuesToUpdate, cb)
  • afterValidate: fn(valuesToUpdate, cb)
  • beforeUpdate: fn(valuesToUpdate, cb)
  • afterUpdate: fn(updatedRecord, cb)

Callbacks on destroy

  • beforeDestroy: fn(criteria, cb)
  • afterDestroy: fn(deletedRecord, cb)

For example, this could be your ./api/pet/models/Pet.js file:

module.exports = {
+  /**
+   * Basic settings
+   */
+
+  // The identity to use.
+  identity: settings.identity,
+
+  // The connection to use.
+  connection: settings.connection,
+
+  // Do you want to respect schema?
+  schema: settings.schema,
+
+  // Merge simple attributes from settings with those ones.
+  attributes: _.merge(settings.attributes, {
+
+  }),
+
+  // Do you automatically want to have time data?
+  autoCreatedAt: settings.autoCreatedAt,
+  autoUpdatedAt: settings.autoUpdatedAt,
+
+  /**
+   * Lifecycle callbacks on create
+   */
+
+  // Before creating a value.
+  beforeCreate: function (values, next) {
+    // Do some stuff
+    next();
+  },
+
+  // After creating a value.
+  afterCreate: function (newlyInsertedRecord, next) {
+    // Do some stuff
+    next();
+  },
+
+  /**
+   * Lifecycle callbacks on update
+   */
+
+  // Before updating a value.
+  beforeUpdate: function (valuesToUpdate, next) {
+    // Do some stuff
+    next();
+  },
+
+  // After updating a value.
+  afterUpdate: function (updatedRecord, next) {
+    // Do some stuff
+    next();
+  },
+
+  /**
+   * Lifecycle callbacks on destroy
+   */
+
+  // Before destroying a value.
+  beforeDestroy: function (criteria, next) {
+    // Do some stuff
+    next();
+  },
+
+  // After destroying a value.
+  afterDestroy: function (destroyedRecords, next) {
+    // Do some stuff
+    next();
+  }
+
+ + + diff --git a/docs/.vuepress/dist/1.x.x/queries.html b/docs/.vuepress/dist/1.x.x/queries.html new file mode 100644 index 0000000000..6664029b17 --- /dev/null +++ b/docs/.vuepress/dist/1.x.x/queries.html @@ -0,0 +1,311 @@ + + + + + + Query Interface | Strapi Docs + + + + + + + +

Query Interface

The Waterline Query Interface allows you to interact with your models the same +way no matter which adapter they are using. This means you can use the same Query +Language whether your data lives in MySQL, MongoDB, PostgreSQL, etc.

Query Methods

Every model in Waterline will have a set of query methods exposed on it to allow +you to interact with the database in a normalized fashion. +These are known as the CRUD (Create, Read, Update and Delete) methods and +is the primary way of interacting with your data.

There are also a special set of queries known as dynamic queries. +These are special class methods that are dynamically generated when you initialize Waterline. +We call them dynamic finders. They perform many of the same functions as the other class +methods but you can call them directly on an attribute in your model.

For most class methods, the callback parameter is optional and if one is not supplied, +it will return a chainable object.

.find(criteria, [callback])

find will return an array of records that match the supplied criteria. +Criteria can be built using the Query Language.

  • The criteria is required and accepts {}, [{}], string and int data types.
  • The callback function is optional.

Any string arguments passed must be the ID of the record. +This method will always return records in an array. +If you are trying to find an attribute that is an array, you must wrap it in an additional +set of brackets otherwise Waterline will think you want to perform an inQuery.

User.find({
+    name: 'Walter Jr'
+  })
+  .exec(function (err, users) {
+    if (err) {
+      console.log(err);
+    }
+    console.log(users);
+  });
+

.findOne(criteria, [callback])

findOne will return an object with the first matching result in the data store.

  • The criteria is required and accepts {}, [{}], string and int data types.
  • The callback function is optional.

Any string arguments passed must be the ID of the record. +If you are trying to find an attribute that is an array, you must wrap it in an additional +set of brackets otherwise Waterline will think you want to perform an inQuery.

User.findOne({
+    name: 'Walter Jr'
+  })
+  .exec(function (err, user) {
+    if (err) {
+      console.log(err);
+    }
+    console.log(user);
+  });
+

.create(criteria, [callback])

create will attempt to create a new record in the datastore. +If the data is valid and passes all validations it will be sent to the adapters create method.

  • The criteria is required and accepts {} and [{}] data types.
  • The callback function is optional.
User.create({
+    name: 'Walter Jr'
+  })
+  .exec(function (err, user) {
+    if (err) {
+      console.log(err);
+    }
+    console.log(user);
+  });
+

.findOrCreate(criteria, [values, callback])

findOrCreate will return a single record if one was found or created, +or an array of records if multiple get found/created via the supplied criteria or values. +Criteria can be built using the Query Language.

  • The criteria is required and accepts {}, [{}], string and int data types.
  • The values is optional and accepts {} and [{}] data types.
  • The callback function is optional.

Any string arguments passed must be the ID of the record. +This method can return a single record or an array of records. +If a model is not found and creation values are omitted, it will get created with the supplied criteria values.

Unless an adapter implements its own version of findOrCreate, findOrCreate will do the +find and create calls in two separate steps (not transactional). +In a high frequency scenario it's possible for duplicates to be created if the query field(s) are not indexed.

Either user(s) with the name "Walter Jr" get returned or +a single user gets created with the name "Walter Jr" and returned:

User.findOrCreate({
+    name: 'Walter Jr'
+  })
+  .exec(function (err, users) {
+    if (err) {
+      console.log(err);
+    }
+    console.log(users);
+  });
+

.update(search criteria, values, [callback])

update will attempt to update any records matching the criteria passed in. +Criteria can be built using the Query Language.

  • The criteria is required and accepts {}, [{}], string and int data types.
  • The values is required and accepts {} and [{}] data types.
  • The callback function is optional.

Although you may pass .update() an object or an array of objects, +it will always return an array of objects. Any string arguments passed must be the ID +of the record. If you specify a primary key instead of a criteria object, +any .where() filters will be ignored.

User.update({
+    name: 'Walter Jr'
+  }, {
+    name: 'Flynn'
+  })
+  .exec(function (err, user) {
+    if (err) {
+      console.log(err);
+    }
+    console.log(user);
+  });
+

.destroy(criteria, [callback])

destroy will destroy any records matching the provided criteria. +Criteria can be built using the Query Language.

  • The criteria is required and accepts {}, [{}], string and int data types.
  • The callback function is optional.

If you want to confirm the record exists before you delete it, +you must first perform a .find(). Any string arguments passed must be the ID of the record.

User.destroy({
+    name: 'Flynn'
+  })
+  .exec(function (err) {
+    if (err) {
+      console.log(err);
+    }
+  });
+

.query(query, [data], callback)

Some adapters, such as sails-mysql and sails-postgresql, support the query function +which will run the provided RAW query against the database. +This can sometimes be useful if you want to run complex queries and performance is very important.

  • The query is required and accepts string data types.
  • The data is optional and accepts array data types.
  • The callback function is required.

The type of the results returned depend on your adapter: sails-mysql returns an array of objects +and sails-postgresql returns an object containing metadata and the actual results within a 'rows' array. +This function does currently not support promises.

Using PostgreSQL:

const title = "The King's Speech";
+Movie.query('SELECT * FROM movie WHERE title = $1', [title], function (err, results) {
+  console.log('Found the following movie: ', results.rows[0]);
+});
+

Using MySQL:

const title = "The King's Speech";
+Movie.query('SELECT * FROM movie WHERE title = $1', [title], function (err, results) {
+  console.log('Found the following movie: ', results[0]);
+});
+

Query Language

The Waterline Query Language is an object based criteria used to retrieve the +records from any of the supported database adapters. +This allows you to change your database without changing your codebase.

All queries inside of Waterline are case insensitive. This allows for easier querying +but makes indexing strings tough. This is something to be aware of if you are +indexing and searching on string fields.

Query Language Basics

The criteria objects are formed using one of four types of object keys. +These are the top level keys used in a query object. It is loosely based on the +criteria used in MongoDB with a few slight variations.

Queries can be built using either a where key to specify attributes, +which will allow you to also use query options such as limit and skip or +if where is excluded the entire object will be treated as a where criteria.

User.find({
+  where: {
+    name: 'John'
+  },
+  skip: 20,
+  limit: 10,
+  sort: 'name DESC'
+});
+

Or:

User.find({
+  name: 'John'
+});
+

Key Pairs

A key pair can be used to search records for values matching exactly what is specified. +This is the base of a criteria object where the key represents an attribute on a model +and the value is a strict equality check of the records for matching values.

User.find({
+  name: 'John'
+});
+

They can be used together to search multiple attributes:

User.find({
+  name: 'John',
+  country: 'France'
+});
+

Modified Pairs

Modified pairs also have model attributes for keys but they also use any of the +supported criteria modifiers to perform queries where a strict equality check wouldn't work.

User.find({
+  name: {
+    contains: 'alt'
+  }
+})
+

In Pairs

In queries work similarly to MySQL in queries. Each element in the array is treated as or.

User.find({
+  name: ['John', 'Walter']
+});
+

Not-In Pairs

Not-In queries work similar to in queries, except for the nested object criteria.

User.find({
+  name: {
+    '!': ['John', 'Walter']
+  }
+});
+

Or Pairs

Performing OR queries is done by using an array of query pairs. +Results will be returned that match any of the criteria objects inside the array.

User.find({
+  or: [
+    {
+      name: 'John'
+    },
+    {
+      occupation: 'Developer'
+    }
+  ]
+});
+

Criteria Modifiers

The following modifiers are available to use when building queries:

  • < or lessThan
  • <= or lessThanOrEqual
  • > or greaterThan
  • >= or greaterThanOrEqual
  • ! or not
  • like
  • contains
  • startsWith
  • endsWith

< or lessThan

Searches for records where the value is less than the value specified.

User.find({
+  age: {
+    '<': 30
+  }
+});
+

<= or lessThanOrEqual

Searches for records where the value is less or equal to the value specified.

User.find({
+  age: {
+    '<=': 21
+  }
+});
+

> or greaterThan

Searches for records where the value is more than the value specified.

User.find({
+  age: {
+    '>': 18
+  }
+});
+

>= or greaterThanOrEqual

Searches for records where the value is more or equal to the value specified.

User.find({
+  age: {
+    '>=': 21
+  }
+});
+

! or not

Searches for records where the value is not equal to the value specified.

User.find({
+  name: {
+    '!': 'John'
+  }
+});
+

like

Searches for records using pattern matching with the % sign.

User.find({
+  food: {
+    'like': '%burgers'
+  }
+});
+

contains

A shorthand for pattern matching both sides of a string. +Will return records where the value contains the string anywhere inside of it.

User.find({
+  class: {
+    'like': '%history%'
+  }
+});
+
User.find({
+  class: {
+    'contains': 'history'
+  }
+});
+

startsWith

A shorthand for pattern matching the right side of a string +Will return records where the value starts with the supplied string value.

User.find({
+  class: {
+    'startsWith': 'french'
+  }
+});
+
User.find({
+  class: {
+    'like': 'french%'
+  }
+});
+

endsWith

A shorthand for pattern matching the left side of a string. +Will return records where the value ends with the supplied string value.

User.find({
+  class: {
+    'endsWith': 'can'
+  }
+});
+
User.find({
+  class: {
+    'like': '%can'
+  }
+});
+

Date Ranges

You can do date range queries using the comparison operators.

User.find({
+  date: {
+    '>': new Date('2/4/2014'),
+    '<': new Date('2/7/2014')
+  }
+});
+

Query Options

Query options allow you refine the results that are returned from a query.

Limit results

Limit the number of results returned from a query.

User.find({
+  where: {
+    name: 'John'
+  },
+  limit: 20
+});
+

Skip results

Return all the results excluding the number of items to skip.

User.find({
+  where: {
+    name: 'John'
+  },
+  skip: 10
+});
+

Pagination

skip and limit can be used together to build up a pagination system.

User.find({
+  where: {
+    name: 'John'
+  },
+  limit: 10,
+  skip: 10
+});
+

Sort results

Results can be sorted by attribute name. Simply specify an attribute name for +natural (ascending) sort, or specify an asc or desc flag for ascending or +descending orders respectively.

Sort by name in ascending order:

User.find({
+  where: {
+    name: 'John'
+  },
+  sort: 'name'
+});
+

Sort by name in descending order:

User.find({
+  where: {
+    name: 'John'
+  },
+  sort: 'name DESC'
+});
+

Sort by name in ascending order:

User.find({
+  where: {
+    name: 'John'
+  },
+  sort: 'name ASC'
+});
+

Sort by binary notation

User.find({
+  where: {
+    name: 'John'
+  },
+  sort: {
+    name: 1
+  }
+});
+

Sort by multiple attributes:

User.find({
+  where: {
+    name: 'John'
+  },
+  sort: {
+    name:  1,
+    age: 0
+  }
+});
+

Select a field

Apply a projection to a Waterline query.

This example only returns the field name:

User.find({
+  where: {
+    age: {
+      '<': 30
+    }
+  },
+  select: ['name']
+});
+
+ + + diff --git a/docs/.vuepress/dist/1.x.x/request.html b/docs/.vuepress/dist/1.x.x/request.html new file mode 100644 index 0000000000..4d51dd4c1a --- /dev/null +++ b/docs/.vuepress/dist/1.x.x/request.html @@ -0,0 +1,174 @@ + + + + + + Request | Strapi Docs + + + + + + + +

Request

A Strapi Request object is an abstraction on top of Node's vanilla request object, +providing additional functionality that is useful for every day HTTP server +development.

API

request.header

Request header object.

request.headers

Request header object. Alias as request.header.

request.method

Request method.

request.method=

Set request method, useful for implementing middleware +such as methodOverride().

request.length

Return request Content-Length as a number when present, or undefined.

request.url

Get request URL.

request.url=

Set request URL, useful for url rewrites.

request.originalUrl

Get request original URL.

request.origin

Get origin of URL, include protocol and host.

this.request.origin
+// => http://example.com
+

request.href

Get full request URL, include protocol, host and url.

this.request.href
+// => http://example.com/foo/bar?q=1
+

request.path

Get request pathname.

request.path=

Set request pathname and retain query-string when present.

request.querystring

Get raw query string void of ?.

request.querystring=

Set raw query string.

Get raw query string with the ?.

request.search=

Set raw query string.

request.host

Get host (hostname:port) when present. Supports X-Forwarded-Host +when strapi.app.proxy is true, otherwise Host is used.

request.hostname

Get hostname when present. Supports X-Forwarded-Host +when strapi.app.proxy is true, otherwise Host is used.

request.type

Get request Content-Type void of parameters such as "charset".

const ct = this.request.type;
+// => "image/png"
+

request.charset

Get request charset when present, or undefined:

this.request.charset
+// => "utf-8"
+

request.query

Get parsed query-string, returning an empty object when no +query-string is present. Note that this getter does not +support nested parsing.

For example "color=blue&size=small":

{
+  color: 'blue',
+  size: 'small'
+}
+

request.query=

Set query-string to the given object. Note that this +setter does not support nested objects.

this.query = { next: '/login' };
+

request.fresh

Check if a request cache is "fresh", aka the contents have not changed. This +method is for cache negotiation between If-None-Match / ETag, and +If-Modified-Since and Last-Modified. It should be referenced after setting +one or more of these response headers.

// freshness check requires status 20x or 304
+this.status = 200;
+this.set('ETag', '123');
+
+// cache is ok
+if (this.fresh) {
+  this.status = 304;
+  return;
+}
+
+// cache is stale
+// fetch new data
+this.body = yield db.find('something');
+

request.stale

Inverse of request.fresh.

request.protocol

Return request protocol, "https" or "http". Supports X-Forwarded-Proto +when strapi.app.proxy is true.

request.secure

Shorthand for this.protocol == "https" to check if a request was +issued via TLS.

request.ip

Request remote address. Supports X-Forwarded-For when strapi.app.proxy +is true.

request.ips

When X-Forwarded-For is present and strapi.app.proxy is enabled an array +of these ips is returned, ordered from upstream -> downstream. When disabled +an empty array is returned.

request.subdomains

Return subdomains as an array.

Subdomains are the dot-separated parts of the host before the main domain of +the app. By default, the domain of the app is assumed to be the last two +parts of the host. This can be changed by setting strapi.app.subdomainOffset.

For example, if the domain is "tobi.ferrets.example.com": +If strapi.app.subdomainOffset is not set, this.subdomains is ["ferrets", "tobi"]. +If strapi.app.subdomainOffset is 3, this.subdomains is ["tobi"].

request.is(types...)

Check if the incoming request contains the "Content-Type" +header field, and it contains any of the give mime types. +If there is no request body, undefined is returned. +If there is no content type, or the match fails false is returned. +Otherwise, it returns the matching content-type.

// With Content-Type: text/html; charset=utf-8
+this.is('html'); // => 'html'
+this.is('text/html'); // => 'text/html'
+this.is('text/*', 'text/html'); // => 'text/html'
+
+// When Content-Type is application/json
+this.is('json', 'urlencoded'); // => 'json'
+this.is('application/json'); // => 'application/json'
+this.is('html', 'application/*'); // => 'application/json'
+
+this.is('html'); // => false
+

For example if you want to ensure that +only images are sent to a given route:

if (this.is('image/*')) {
+  // process
+} else {
+  this.throw(415, 'images only!');
+}
+

Content Negotiation

Strapi's request object includes helpful content negotiation utilities powered by +accepts and +negotiator.

These utilities are:

  • request.accepts(types)
  • request.acceptsEncodings(types)
  • request.acceptsCharsets(charsets)
  • request.acceptsLanguages(langs)

If no types are supplied, all acceptable types are returned.

If multiple types are supplied, the best match will be returned. If no matches are found, +a false is returned, and you should send a 406 "Not Acceptable" response to the client.

In the case of missing accept headers where any type is acceptable, the first type will +be returned. Thus, the order of types you supply is important.

request.accepts(types)

Check if the given type(s) is acceptable, returning the best match when true, otherwise +false. The type value may be one or more mime type string +such as "application/json", the extension name +such as "json", or an array ["json", "html", "text/plain"].

// Accept: text/html
+this.accepts('html');
+// => "html"
+
+// Accept: text/*, application/json
+this.accepts('html');
+// => "html"
+this.accepts('text/html');
+// => "text/html"
+this.accepts('json', 'text');
+// => "json"
+this.accepts('application/json');
+// => "application/json"
+
+// Accept: text/*, application/json
+this.accepts('image/png');
+this.accepts('png');
+// => false
+
+// Accept: text/*;q=.5, application/json
+this.accepts(['html', 'json']);
+this.accepts('html', 'json');
+// => "json"
+
+// No Accept header
+this.accepts('html', 'json');
+// => "html"
+this.accepts('json', 'html');
+// => "json"
+

You may call this.accepts() as many times as you like, +or use a switch:

switch (this.accepts('json', 'html', 'text')) {
+  case 'json': break;
+  case 'html': break;
+  case 'text': break;
+  default: this.throw(406, 'json, html, or text only');
+}
+

request.acceptsEncodings(encodings)

Check if encodings are acceptable, returning the best match when true, otherwise false. +Note that you should include identity as one of the encodings!

// Accept-Encoding: gzip
+this.acceptsEncodings('gzip', 'deflate', 'identity');
+// => "gzip"
+
+this.acceptsEncodings(['gzip', 'deflate', 'identity']);
+// => "gzip"
+

When no arguments are given all accepted encodings +are returned as an array:

// Accept-Encoding: gzip, deflate
+this.acceptsEncodings();
+// => ["gzip", "deflate", "identity"]
+

Note that the identity encoding (which means no encoding) could be unacceptable if +the client explicitly sends identity;q=0. Although this is an edge case, you should +still handle the case where this method returns false.

request.acceptsCharsets(charsets)

Check if charsets are acceptable, returning +the best match when true, otherwise false.

// Accept-Charset: utf-8, iso-8859-1;q=0.2, utf-7;q=0.5
+this.acceptsCharsets('utf-8', 'utf-7');
+// => "utf-8"
+
+this.acceptsCharsets(['utf-7', 'utf-8']);
+// => "utf-8"
+

When no arguments are given all accepted charsets +are returned as an array:

// Accept-Charset: utf-8, iso-8859-1;q=0.2, utf-7;q=0.5
+this.acceptsCharsets();
+// => ["utf-8", "utf-7", "iso-8859-1"]
+

request.acceptsLanguages(langs)

Check if langs are acceptable, returning +the best match when true, otherwise false.

// Accept-Language: en;q=0.8, es, pt
+this.acceptsLanguages('es', 'en');
+// => "es"
+
+this.acceptsLanguages(['en', 'es']);
+// => "es"
+

When no arguments are given all accepted languages +are returned as an array:

// Accept-Language: en;q=0.8, es, pt
+this.acceptsLanguages();
+// => ["es", "pt", "en"]
+

request.idempotent

Check if the request is idempotent.

request.socket

Return the request socket.

request.get(field)

Return request header.

+ + + diff --git a/docs/.vuepress/dist/1.x.x/response.html b/docs/.vuepress/dist/1.x.x/response.html new file mode 100644 index 0000000000..454bd8c59a --- /dev/null +++ b/docs/.vuepress/dist/1.x.x/response.html @@ -0,0 +1,94 @@ + + + + + + Response | Strapi Docs + + + + + + + +

Response

A Strapi Response object is an abstraction on top of Node's vanilla response object, +providing additional functionality that is useful for every day HTTP server +development.

API

response.header

Response header object.

response.headers

Response header object. Alias as response.header.

response.socket

Request socket.

response.status

Get response status. By default, response.status is not set unlike Node's +res.statusCode which defaults to 200.

response.status=

Set response status via numeric code:

  • 100 "continue"
  • 101 "switching protocols"
  • 102 "processing"
  • 200 "ok"
  • 201 "created"
  • 202 "accepted"
  • 203 "non-authoritative information"
  • 204 "no content"
  • 205 "reset content"
  • 206 "partial content"
  • 207 "multi-status"
  • 300 "multiple choices"
  • 301 "moved permanently"
  • 302 "moved temporarily"
  • 303 "see other"
  • 304 "not modified"
  • 305 "use proxy"
  • 307 "temporary redirect"
  • 400 "bad request"
  • 401 "unauthorized"
  • 402 "payment required"
  • 403 "forbidden"
  • 404 "not found"
  • 405 "method not allowed"
  • 406 "not acceptable"
  • 407 "proxy authentication required"
  • 408 "request time-out"
  • 409 "conflict"
  • 410 "gone"
  • 411 "length required"
  • 412 "precondition failed"
  • 413 "request entity too large"
  • 414 "request-uri too large"
  • 415 "unsupported media type"
  • 416 "requested range not satisfiable"
  • 417 "expectation failed"
  • 418 "i'm a teapot"
  • 422 "unprocessable entity"
  • 423 "locked"
  • 424 "failed dependency"
  • 425 "unordered collection"
  • 426 "upgrade required"
  • 428 "precondition required"
  • 429 "too many requests"
  • 431 "request header fields too large"
  • 500 "internal server error"
  • 501 "not implemented"
  • 502 "bad gateway"
  • 503 "service unavailable"
  • 504 "gateway time-out"
  • 505 "http version not supported"
  • 506 "variant also negotiates"
  • 507 "insufficient storage"
  • 509 "bandwidth limit exceeded"
  • 510 "not extended"
  • 511 "network authentication required"

Don't worry too much about memorizing these strings, if you have a typo an error will be thrown, +displaying this list so you can make a correction.

response.message

Get response status message. By default, response.message is +associated with response.status.

response.message=

Set response status message to the given value.

response.length=

Set response Content-Length to the given value.

response.length

Return response Content-Length as a number when present, or deduce +from this.body when possible, or undefined.

response.body

Get response body.

response.body=

Set response body to one of the following:

  • string written
  • Buffer written
  • Stream piped
  • Object json-stringified
  • null no content response

If response.status has not been set, Strapi will automatically set the status to 200 or 204.

String

The Content-Type is defaulted to text/html or text/plain, both with +a default charset of utf-8. The Content-Length field is also set.

Buffer

The Content-Type is defaulted to application/octet-stream, and Content-Length +is also set.

Stream

The Content-Type is defaulted to application/octet-stream.

Object

The Content-Type is defaulted to application/json.

response.get(field)

Get a response header field value with case-insensitive field.

const etag = this.get('ETag');
+

response.set(field, value)

Set response header field to value:

this.set('Cache-Control', 'no-cache');
+

response.append(field, value)

Append additional header field with value val.

this.append('Link', '<http://127.0.0.1/>');
+

response.set(fields)

Set several response header fields with an object:

this.set({
+  'Etag': '1234',
+  'Last-Modified': date
+});
+

response.remove(field)

Remove header field.

response.type

Get response Content-Type void of parameters such as "charset".

const ct = this.type;
+// => "image/png"
+

response.type=

Set response Content-Type via mime string or file extension.

this.type = 'text/plain; charset=utf-8';
+this.type = 'image/png';
+this.type = '.png';
+this.type = 'png';
+

Note: when appropriate a charset is selected for you, for +example response.type = 'html' will default to "utf-8", however +when explicitly defined in full as response.type = 'text/html' +no charset is assigned.

response.is(types...)

Very similar to this.request.is(). +Check whether the response type is one of the supplied types. +This is particularly useful for creating middleware that +manipulate responses.

For example, this is a middleware that minifies +all HTML responses except for streams.

const minify = require('html-minifier');
+
+strapi.app.use(function *minifyHTML(next) {
+  yield next;
+
+  if (!this.response.is('html')) {
+    return;
+  }
+
+  const body = this.body;
+  if (!body || body.pipe) {
+    return;
+  }
+
+  if (Buffer.isBuffer(body)) {
+    body = body.toString();
+  }
+
+  this.body = minify(body);
+});
+

response.redirect(url, [alt])

Perform a [302] redirect to url.

The string "back" is special-cased +to provide Referrer support, when Referrer +is not present alt or "/" is used.

this.redirect('back');
+this.redirect('back', '/index.html');
+this.redirect('/login');
+this.redirect('http://google.com');
+

To alter the default status of 302, simply assign the status +before or after this call. To alter the body, assign it after this call:

this.status = 301;
+this.redirect('/cart');
+this.body = 'Redirecting to shopping cart';
+

response.attachment([filename])

Set Content-Disposition to "attachment" to signal the client +to prompt for download. Optionally specify the filename of the +download.

response.headerSent

Check if a response header has already been sent. Useful for seeing +if the client may be notified on error.

response.lastModified

Return the Last-Modified header as a Date, if it exists.

response.lastModified=

Set the Last-Modified header as an appropriate UTC string. +You can either set it as a Date or date string.

this.response.lastModified = new Date();
+

response.etag=

Set the ETag of a response including the wrapped "s. +Note that there is no corresponding response.etag getter.

this.response.etag = crypto.createHash('md5').update(this.body).digest('hex');
+

response.vary(field)

Vary on field.

+ + + diff --git a/docs/.vuepress/dist/1.x.x/router.html b/docs/.vuepress/dist/1.x.x/router.html new file mode 100644 index 0000000000..4532f990de --- /dev/null +++ b/docs/.vuepress/dist/1.x.x/router.html @@ -0,0 +1,132 @@ + + + + + + Router | Strapi Docs + + + + + + + +

Router

The most basic feature of any web application is the ability to interpret a request sent to a URL, +then send back a response. In order to do this, your application has to be able to distinguish one URL +from another.

Like most web frameworks, Strapi provides a router: a mechanism for mapping URLs to controllers. +Routes are rules that tell Strapi what to do when faced with an incoming request.

Routes can be found in ./api/<apiName>/config/routes.json.

Route format

Each route consists of an address (as a key) and a target (as an object value). +The address is a URL path and a specific HTTP method. The target is defined by an object with a +controller and an action. When the router receives an incoming request, it checks the address +of all routes for matches. If a matching route is found, the request is then passed to its target.

  {
+    "routes": {
+      "VERB /endpoint/:param": {
+        "controller": "controllerName",
+        "action": "actionName"
+      }
+    }
+  }
+

For example to manage your Post records with a CRUD, your route should look like this:

  {
+    "routes": {
+      "GET /post": {
+        "controller": "Post",
+        "action": "find"
+      }
+      "GET /post/:id": {
+        "controller": "Post",
+        "action": "findOne"
+      },
+      "POST /post": {
+        "controller": "Post",
+        "action": "create"
+      },
+      "PUT /post/:id": {
+        "controller": "Post",
+        "action": "update"
+      },
+      "DELETE /post/:id": {
+        "controller": "Post",
+        "action": "delete"
+      }
+    }
+  }
+

Route parameters

Route paths will be translated to regular expressions used to match requests. +Query strings will not be considered when matching requests.

Route parameters are captured and added to ctx.params or ctx.request.body.

By taking the previous example, your Post controller should look like this:

module.exports = {
+
+  // GET request
+  find: function *() {
+    try {
+      this.body = yield Post.find(this.params);
+    } catch (error) {
+      this.body = error;
+    }
+  },
+
+  findOne: function *() {
+    try {
+      this.body = yield Post.findOne(this.params);
+    } catch (error) {
+      this.body = error;
+    }
+  },
+
+  // POST request
+  create: function *() {
+    try {
+      this.body = yield Post.create(this.request.body);
+    } catch (error) {
+      this.body = error;
+    }
+  },
+
+  // PUT request
+  update: function *() {
+    try {
+      this.body = yield Post.update(this.params.id, this.request.body);
+    } catch (error) {
+      this.body = error;
+    }
+  },
+
+  // DELETE request
+  delete: function *() {
+    try {
+      this.body = yield Post.destroy(this.params);
+    } catch (error) {
+      this.body = error;
+    }
+  }
+};  
+
+

Router prefix

Keep in mind routes can automatically be prefixed in ./config/general.json with the prefix key. +Let an empty string if you don't want to prefix your API. The prefix must starts with a /, e.g. /api.

Policies and route process

Just because a request matches a route address doesn't necessarily mean it will be passed to that +route's target directly. The request will need to pass through any configured policies first. +Policies are versatile tools for authorization and access control. They let you allow or deny +access to your controllers down to a fine level of granularity.

Policies are defined in the policies directory of every of your APIs.

Each policy file should contain a single function. When it comes down to it, policies are +really just functions which run before your controllers. You can chain as many of them +together as you like. In fact they're designed to be used this way. Ideally, each middleware +function should really check just one thing.

For example to access DELETE /post/:id, the request will go through the isAdmin policy first. +If the policy allows the request, then the delete action from the Post controller is executed.

  {
+    "routes": {
+      "DELETE /post/:id": {
+        "controller": "Post",
+        "action": "delete",
+        "policies": ["isAdmin"]
+      }
+    }
+  }
+

Do not forget to yield next when you need to move on.

+ + + diff --git a/docs/.vuepress/dist/1.x.x/services.html b/docs/.vuepress/dist/1.x.x/services.html new file mode 100644 index 0000000000..6b069bfdb4 --- /dev/null +++ b/docs/.vuepress/dist/1.x.x/services.html @@ -0,0 +1,66 @@ + + + + + + Services | Strapi Docs + + + + + + + +

Services

Services can be thought of as libraries which contain functions that you might want to use +in many places of your application. For example, you might have an Email service which +wraps some default email message boilerplate code that you would want to use in many parts +of your application.

Simply create a JavaScript file containing a function or an object into your +./api/<apiName>/services directory.

For example, you could have an Email service like this:

const nodemailer = require('nodemailer');
+
+module.exports = {
+  sendEmail: function (from, to, subject, text) {
+
+    // Create reusable transporter object using SMTP transport
+    const transporter = nodemailer.createTransport({
+      service: 'Gmail',
+      auth: {
+        user: 'gmail.user@gmail.com',
+        pass: 'userpass'
+      }
+    });
+
+    // Setup e-mail data
+    const options = {
+      from: from,
+      to: to,
+      subject: subject,
+      text: text
+    };
+
+    // Send mail
+    transporter.sendMail(options, function(error, info){
+      if (error) {
+        console.log(error);
+        return false;
+      }
+
+      console.log('Message sent: ' + info.response);
+    });
+  }
+};
+
+ + + diff --git a/docs/.vuepress/dist/1.x.x/sessions.html b/docs/.vuepress/dist/1.x.x/sessions.html new file mode 100644 index 0000000000..221f0b7ca0 --- /dev/null +++ b/docs/.vuepress/dist/1.x.x/sessions.html @@ -0,0 +1,58 @@ + + + + + + Sessions | Strapi Docs + + + + + + + +

Sessions

Since HTTP driven applications are stateless, sessions provide a way to store information +about the user across requests.

Strapi provides "guest" sessions, meaning any visitor will have a session, +authenticated or not. If a session is new a Set-Cookie will be produced regardless +of populating the session.

Strapi only supports cookie sessions, for now.

The current session is available in this.session inside a controller action.

module.exports = {
+  find: function *() {
+
+    // Limit request rate to 100
+    if (this.session.views < 100) {
+      try {
+        this.session.views++;
+        this.body = yield Post.find(this.params);
+      } catch (error) {
+        this.body = error;
+      }
+    } else {
+      this.body = 'You have reached your request rate limit';
+    }
+  }
+};  
+

To destroy an active session, simply set it to null:

module.exports = {
+  logout: function () {
+    try {
+      this.session = null;
+      this.redirect('./');
+    } catch (error) {
+      this.body = error;
+    }
+  }
+};  
+
+ + + diff --git a/docs/.vuepress/dist/1.x.x/testing.html b/docs/.vuepress/dist/1.x.x/testing.html new file mode 100644 index 0000000000..36b7a58066 --- /dev/null +++ b/docs/.vuepress/dist/1.x.x/testing.html @@ -0,0 +1,85 @@ + + + + + + Testing | Strapi Docs + + + + + + + +

Testing

Strapi's test suite is written using mocha and although +Strapi doesn't impose any testing framework for your apps, in this example we +will setup tests using the mocha framework.

Setup

Before writing tests, you should setup a basic directory structure, like this:

./strapiApp
+├── api/
+├── ...
+├── test/
+│  ├── integration/
+│  │  ├── controllers/
+│  │  │  └── my_endpoint.test.js
+│  │  ├── models/
+│  │  │  └── my_model.test.js
+│  │  └── ...
+|  ├── ...
+│  ├── bootstrap.js
+

Boostrap

We are going to setup a bootstrap.js with before and after hooks to +perform any actions before and after our tests.
+In this example, the app server is started before running any tests an stop +the server after tests are completed.

./test/bootstrap.js

const strapi = require('strapi');
+
+before(function (done) {
+  strapi.start({}, function(err) {
+    if (err) {
+      return done(err);
+    }
+
+    done(err, strapi);
+  });
+});
+
+after(function (done) {
+  strapi.stop(done());
+});
+

Writing tests

Once you have setup your directory structure, you can start writing your tests. +In this example we use co-supertest, +a co and Supertest integration library. +Supertest provides several useful +methods for testing HTTP requests.
+If you want to test an api endpoint, you can do it like this:

./test/integration/controllers/my_endpoint.js

const request = require('co-supertest');
+
+describe('MyEndpoint Controller Integration', function() {
+  describe('GET /my_endpoint', function() {
+    it('should return 200 status code', function *() {
+      yield request(strapi.config.url)
+        .get('/my_endpoint')
+        .expect(200)
+        .expect('Content-Type', /json/)
+        .end();
+    });
+  });
+});
+

Running tests

In order to run tests you can use npm test. In your package.json, in the +scripts section, add this:

./package.json

"scripts": {
+  "test": "mocha --require co-mocha test/bootstrap.js test/**/*.test.js"
+}
+

Remember to run test/bootstrap.js before any other tests and, if you want, +use the --require option to pass any required dependencies you need available +in your tests.

+ + + diff --git a/docs/.vuepress/dist/1.x.x/upload.html b/docs/.vuepress/dist/1.x.x/upload.html new file mode 100644 index 0000000000..af3051e78f --- /dev/null +++ b/docs/.vuepress/dist/1.x.x/upload.html @@ -0,0 +1,77 @@ + + + + + + Upload | Strapi Docs + + + + + + + +

Upload

Strapi contains a set of tools to upload files.

Upload config

To change the upload config, edit the ./api/upload/config/settings.json file.

For the config bellow, please use refer to the [co-busboy](https://github.com/cojs/busboy) node module documentation.

{
+  "upload": {
+    "folder": "public/upload",
+    "acceptedExtensions": [
+      "*"
+    ],
+    "headers": {},
+    "highWaterMark": "",
+    "fileHwm": "",
+    "defCharset": "",
+    "preservePath": "",
+    "limits": {
+      "fieldNameSize": "",
+      "fieldSize": "",
+      "fields": "",
+      "fileSize": "",
+      "files": "",
+      "parts": "",
+      "headerPairs": ""
+    }
+  }
+}
+

Upload service

The upload service allows you to easily upload files from anywhere in your application.

Usage as a promise (yieldable) :

yield strapi.api.upload.services.upload.upload(part, this);
+

Upload API

The upload API is a simple API which can be used from your client +(front-end, mobile...) application to upload files.

Route used to upload files:

POST /upload
+

To use this route, you have to submit a HTML form with multipart/* enctype +(or fake it if you are using a web front-end framework like AngularJS).

Response payload:

[
+  {
+    "readable": true,
+    "domain": null,
+    "truncated": false,
+    "fieldname": "file",
+    "filename": "1445421755771-image.jpg",
+    "encoding": "7bit",
+    "transferEncoding": "7bit",
+    "mime": "image/jpeg",
+    "mimeType": "image/jpeg",
+    "originalFilenameFormatted": "image.jpg",
+    "originalFilename": "image.jpg",
+    "template": "default",
+    "lang": "en",
+    "createdAt": "2015-10-21T10:02:35.776Z",
+    "updatedAt": "2015-10-21T10:02:35.776Z",
+    "id": 2
+  }
+]
+

Upload model

Each uploaded file description is registered in the database. So you can retrieve +them whenever you want. However, you can disable this option by overriding the +upload service logic.

+ + + diff --git a/docs/.vuepress/dist/1.x.x/users.html b/docs/.vuepress/dist/1.x.x/users.html new file mode 100644 index 0000000000..684b597a56 --- /dev/null +++ b/docs/.vuepress/dist/1.x.x/users.html @@ -0,0 +1,66 @@ + + + + + + Users | Strapi Docs + + + + + + + +

Users

Most of the web applications require a user management system: registration, login, +reset password, etc.

To avoid you to reinvent the wheel, Strapi embedded a full featured user management +system powered by Grant and JSON Web Token (JWT).

Local Registration

Route used to register a user to your application: POST /auth/local/register.

Request payload:

{
+  "username": "John DOE",
+  "email": "contact@company.com",
+  "password": "123456"
+}
+

Response payload:

{
+  "user": {},
+  "jwt": ""
+}
+

Local Login

Route used to login a user to your application: POST /auth/local.

Request payload:

{
+  "identifier": "contact@company.com",
+  "password": "123456"
+}
+

Response payload:

{
+  "user": {},
+  "jwt": ""
+}
+

Authentication

JWT does not use session. Once you get the token, it has to be stored in front (for +example in the localstorage), and sent within each request. The token can be sent:

  • in the header (Bearer)
  • in the body (token field)
  • in the querystring (token field)

Providers

Thanks to Grant and Purest, you can easily use OAuth and OAuth2 +providers to enable authentication in your application. By default, +Strapi comes with four providers:

  • Facebook
  • Google
  • Github
  • Linkedin2 (Oauth2 Provider for Linkedin)

To use the providers authentication, set your credentials in +./api/user/config/environments/development/grant.json.

Redirect your user to: GET /connect/:provider.

After his approval, he will be redirected to /auth/:provider/callback. The jwt and user will be available in the querystring.

Response payload:

{
+  "user": {},
+  "jwt": ""
+}
+

Custom providers

Strapi comes with 5 providers. If you want to add another one, it can be easily done thanks to Purest, by adding it in the Grant service.

Forgot password

Send an email to the user with an activation code: POST /auth/forgot-password.

Request payload:

{
+  "email": "contact@company.com"
+}
+

Change password

Route used to update the password of a user after he asked for a +"forgot-password" email: POST /auth/change-password.

Request payload:

{
+  "code": "",
+  "password": "123456",
+  "passwordConfirmation": "123456"
+}
+

Response payload:

{
+  "user": {},
+  "jwt": ""
+}
+

Accessing user from requests.

If you want to access attributes of the logged in user, you can use this.user inside of your controller action.

+ + + diff --git a/docs/.vuepress/dist/1.x.x/views.html b/docs/.vuepress/dist/1.x.x/views.html new file mode 100644 index 0000000000..d0a4eae876 --- /dev/null +++ b/docs/.vuepress/dist/1.x.x/views.html @@ -0,0 +1,67 @@ + + + + + + Views | Strapi Docs + + + + + + + +

Views

In Strapi, views are markup templates that are compiled on the server into HTML pages. +In most cases, views are used as the response to an incoming HTTP request.

By default, Strapi doesn't use views. The philosophy of the framework is to +separate the reusable backend application logic from the frontend.

If you want to activate views, set the views in ./config/general.json.

For example, if you want to use lodash for .html files and use it by default, +you may set up your views object as below:

{
+  "views": {
+    "map": {
+      "html": "lodash"
+    },
+    "default": "html"
+  }
+}
+

Views are defined in your application's ./views directory.

Render a view

Simply use this.render instead of this.body to render a view.

You don't need to specify the view extension if you use the default one sets in config.

Using the config we wrote above with lodash for .html files and use the html +extension by default, this example will render ./views/user.html with +Lodash as template engine.

yield this.render('user', {
+  firstname: 'John',
+  lastname: 'Doe'
+});
+
<html>
+  <head>...</head>
+  <body>
+    <p>Firstname: <% firstname %><br>Lastname: <% lastname %></p>
+  </body>
+</html>
+

Here is the same example with the jade extension, not used by default:

yield this.render('user.jade', {
+  firstname: 'John',
+  lastname: 'Doe'
+});
+

Supported template engines

To use a view engine, you should use npm to install it in your project and +set the map object in strapi.config.views. For example, if you want to use +swig for .html files and hogan for .md files, you may configure the +map object as below:

{
+  "views": {
+    "map": {
+      "html": "swig",
+      "md": "hogan"
+    }
+  }
+}
+

Strapi supports all of those view engines:

+ + + diff --git a/docs/.vuepress/dist/3.x.x/SUMMARY.html b/docs/.vuepress/dist/3.x.x/SUMMARY.html new file mode 100644 index 0000000000..43e872beea --- /dev/null +++ b/docs/.vuepress/dist/3.x.x/SUMMARY.html @@ -0,0 +1,23 @@ + + + + + + Summary | Strapi Docs + + + + + + + +
+ + + diff --git a/docs/.vuepress/dist/3.x.x/advanced/customize-admin.html b/docs/.vuepress/dist/3.x.x/advanced/customize-admin.html new file mode 100644 index 0000000000..ac05843ee4 --- /dev/null +++ b/docs/.vuepress/dist/3.x.x/advanced/customize-admin.html @@ -0,0 +1,164 @@ + + + + + + Admin panel | Strapi Docs + + + + + + + +

Admin panel

One of Strapi's main feature is its fully extendable and customizable admin panel. This section explains how the admin panel section is structured and how to customize it.

See the Contributing Guide for informations on how to develop the Strapi's admin interface.

Files structure

The entire logic of the admin panel is located in a single folder named ./admin/. This directory contains the following structure:

/admin
+└─── admin
+|   └─── build // Webpack generated build of the front-end
+|   └─── src // Front-end directory
+|        └─── app.js // Entry point of the React application
+|        └─── assets // Assets directory containing images,...
+|        └─── components // Admin's React components directory
+|        └─── containers // Admin's high level components directory
+|        └─── favicon.ico // Favicon displayed in web browser
+|        └─── i18n.js // Internalization logic
+|        └─── index.html // Basic html file in which are injected necessary styles and scripts
+|        └─── reducers.js // Redux reducers logic
+|        └─── store.js // Redux store logic
+|        └─── styles // Directory containing the global styles. Specific styles are defined at the component level
+|        └─── translations  // Directory containing text messages for each supported languages
+└─── config
+|    └─── routes.json // Admin's API routes
+|    └─── layout.json // Admin's specific settings
+└─── controllers // Admin's API controllers
+└─── services // Admin's API services
+└─── packages.json // Admin's npm dependencies
+

Customization

The administration panel can be customised according to your needs, so you can make it reflects your identity: colors, fonts, logo, etc.

Change access URL

By default, the administration panel is exposed via http://localhost:1337/admin. However, for security reasons, you can easily update this path.

Path — ./config/environment/**/server.json.

{
+  "host": "localhost",
+  "port": 1337,
+  "autoReload": {
+    "enabled": true
+  },
+  "cron": {
+    "enabled": false
+  },
+  "admin": {
+    "path": "/dashboard"
+  }
+}
+

The panel will be available through http://localhost:1337/dashboard with the configurations above.

Development mode

Note that to modify the administration panel, your project needs to be created with using the dev flag, an example of such would be: strapi new strapi --dev.

#1 — Install its own dependencies

Run npm install from the ./admin folder.

#2 — Launch the development server

Run npm start from the ./admin folder. That's all.

#3 — Go to the browser

You should be able to see the admin at http://localhost:4000/admin.

In development, all the plugins of your app are mounted in the same build as the administration panel.

Colors

Admin's styles use PostCSS, and more precisely PostCSS-SCSS. In this way, colors are stored in variables. The values of these variables can be easily changed in files located in ./admin/admin/src/styles/variables/.

The changes should be automatically visible.

Fonts

Fonts can also be overridden:

  • Add the fonts files you need in ./admin/admin/src/styles/fonts.
  • Import them from ./admin/admin/src/styles/base/fonts.scss.
  • Use them by replacing the variables' values in ./admin/admin/src/styles/variables/variables.bootstrap.scss.

To change the top-left displayed admin panel's logo, replace the image located at ./admin/admin/src/assets/images/logo-strapi.png.

make sure the size of your image is the same as the existing one (434px x 120px).


Build

To build the administration, run the following command from the root directory of your project.

npm run setup
+

This will replace the folder's content located at ./admin/admin/build. Visit http://localhost:1337/admin/ to make sure your updates have been taken in account.

After you have built the admininistration you can now create a new project to develop your API with the changes implemented.

You should now create a project without --dev


Deployment

The administration is nothing more than a React front-end application calling an API. The front-end and the back-end are independent and can be deployed on different servers which brings us to different scenarios:

  1. Deploy the entire project on the same server.
  2. Deploy the administration panel on another server (AWS S3, Azure, etc) than the API.
  3. Deploy the administration panel and the plugins on another server than the API.

Let's dive into the build configurations for each case.

Deploy the entire project on the same server.

You don't need to touch anything in your configuration file. This is the default behaviour and the build configurations will be automatically set. The server will start on the defined port and the administration panel will be accessible through http://yourdomain.com:1337/dashboard.

You might want to change the path to access to the administration panel. Here the required configurations to change the path:

Path — ./config/environment/**/server.json.

{
+  "host": "localhost",
+  "port": 1337,
+  "autoReload": {
+    "enabled": false
+  },
+  "cron": {
+    "enabled": false
+  },
+  "admin": {
+    "path": "/dashboard" // We change the path to access to the admin (highly recommended for security reasons).
+  }
+}
+

You have to rebuild the administration panel to make this work. Please follow the step #2 of the deployment guide.

Deploy the administration panel on another server (AWS S3, Azure, etc) than the API.

It's very common to deploy the front-end and the back-end on different servers. Here the required configurations to handle this case:

Path — ./config/environment/**/server.json.

{
+  "host": "localhost",
+  "port": 1337,
+  "autoReload": {
+    "enabled": false
+  },
+  "cron": {
+    "enabled": false
+  },
+  "admin": {
+    "path": "/dashboard",
+    "build": {
+      "host": "/",  // Note: The administration will be accessible from the root of the domain (ex: http//yourfrontend.com/)
+      "backend": "http://yourbackend.com",
+      "plugins": {
+        "source": "backend" // What does it means? The script tags in the index.html will use the backend value to load the plugins (ex: http://yourbackend.com/dashboard/content-manager/main.js).
+      }
+    }
+  }
+}
+

The administration URL will be http://yourfrontend.com and every request from the panel will hit the backend at http://yourbackend.com. The plugins will be injected through the origin (means the API itself). In other words, the plugins URLs will be http://yourbackend.com/dashboard/content-manager/main.js.

How it is possible? The API (the Strapi server) owns the plugin and these plugins are exposed through http://yourbackend.com/admin/**/main.js

The DOM should look like this:

Path — ./admin/admin/build/index.html.

<html>
+  <head></head>
+  <body>
+    <div id="app"></div>
+    <script type="text/javascript" src="/vendor.dll.js"></script>
+    <script type="text/javascript" src="/main.js"></script>
+    <script src="http://yourbackend.com/dashboard/content-manager/main.js"></script>
+    <script src="http://yourbackend.com/dashboard/settings-manager/main.js"></script>
+    <script src="http://yourbackend.com/dashboard/content-type-builder/main.js"></script>
+  </body>
+</html>
+

The plugins are injected using the ./admin/admin/build/config/plugins.json. To see the plugins URLs in the index.html, you need to launch the administration panel in the browser.

Deploy the administration panel and the plugins on another server than the API

In this case, we suppose that you decided to put your administration and the plugins on the same server but on a different server as the API.

Path — ./config/environment/**/server.json.

{
+  "host": "localhost",
+  "port": 1337,
+  "autoReload": {
+    "enabled": false
+  },
+  "cron": {
+    "enabled": false
+  },
+  "admin": {
+    "build": {
+      "host": "http://yourfrontend.com/dashboard", // Note: The custom path has moved directly in the host URL.
+      "backend": "http://yourbackend.com",
+      "plugins": {
+        "source":  "host", // What does it means? The script tags in the index.html will use the host value to load the plugins (ex: http://yourfrontend.com/dashboard/plugins/content-manager/main.js).
+        "folder": "/plugins"
+      }
+    }
+  }
+}
+

The administration URL will be http://yourfrontend.com/dashboard and every request from the panel will hit the backend at http://yourbackend.com. The plugins will be injected through the host. It means that the plugins URLs will use the host URL as the origin. So the plugins URLs will be http://yourfrontend.com/dashboard/plugins/content-manager/main.js.

We also added a folder setting to separate the plugins from the administration build. In your server, the files structure should look like this:

- src/
+  - 0bd35bad03d09ca61ac6cce225112e36.svg
+  - 1d51d8767683a24635702f720cda4e26.svg
+  - af3aefd0529349e40e4817c87c620836.png
+  - config/
+    - plugins.json
+  - main.js
+  - main.js.map
+  - plugins/
+    - content-type-builder/
+      - 0bd35bad03d09ca61ac6cce225112e36.svg
+      - 1d51d8767683a24635702f720cda4e26.svg
+      - af3aefd0529349e40e4817c87c620836.png
+      - main.js
+      - main.js.map
+    - content-manager/
+      - ...
+      - main.js
+      - main.js.map
+    - settings-manager/
+      - ...
+      - main.js
+      - main.js.map
+  - vendor.dll.js
+  - vendor.dll.js.map
+

The generated index.html will look like this:

Path — ./admin/admin/build/index.html.

<html>
+  <head></head>
+  <body>
+    <div id="app"></div>
+    <script type="text/javascript" src="/dashboard/vendor.dll.js"></script>
+    <script type="text/javascript" src="/dashboard/main.js"></script>
+    <script src="/dashboard/plugins/content-manager/main.js"></script>
+    <script src="/dashboard/plugins/settings-manager/main.js"></script>
+    <script src="/dashboard/plugins/content-type-builder/main.js"></script>
+  </body>
+</html>
+

The plugins are injected using the ./admin/admin/build/config/plugins.json. To see the plugins URLs in the index.html, you need to launch the administration panel in the browser.

+ + + diff --git a/docs/.vuepress/dist/3.x.x/advanced/hooks.html b/docs/.vuepress/dist/3.x.x/advanced/hooks.html new file mode 100644 index 0000000000..a996c7aff1 --- /dev/null +++ b/docs/.vuepress/dist/3.x.x/advanced/hooks.html @@ -0,0 +1,110 @@ + + + + + + Hooks | Strapi Docs + + + + + + + +

Hooks

The hooks are modules that add functionality to the core. They are loaded during the server boot. For example, if your project needs to work with a SQL database, your will have to add the hook strapi-hook-bookshelf to be able to connect your app with your database.

Path — ./hooks/documentation/lib/index.js.

const fs = require('fs');
+const path = require('path');
+
+module.exports = strapi => {
+  const hook = {
+
+    /**
+     * Default options
+     */
+
+    defaults: {
+      documentation: {
+        path: '/public/documentation'
+      }
+    },
+
+    /**
+     * Initialize the hook
+     */
+
+    initialize: cb => {
+      try {
+        // Check if documentation folder exist.
+        fs.accessSync(path.resolve(process.cwd(), this.defaults.documentation.path));
+      } catch (e) {
+        // Otherwise, create the folder.
+        fs.mkdirSync(path.resolve(process.cwd(), this.defaults.documentation.path));
+      }
+
+      // This function doesn't really exist,
+      // it's just an example to tell you that you
+      // run your business logic and when it's done
+      // you just need to call the callback `cb`
+      generateDocumentation(path.resolve(process.cwd(), this.defaults.documentation.path), function(err) {
+        if (err) {
+          // Error: it will display the error to the user
+          // and the hook won't be loaded.
+          return cb(err);
+        }
+
+        // Success.
+        cb();
+      });
+    }
+  };
+
+  return hook;
+};
+
  • defaults (object): Contains the defaults configurations. This object is merged to strapi.config.hook.settings.**.
  • initialize (function): Called during the server boot. The callback cb needs to be called. Otherwise, the hook won't be loaded.

Every folder that follows this name pattern strapi-* in your ./node_modules folder will be loaded as a hook. The hooks are accessible through the strapi.hook variable.

Structure

A hook needs to follow the structure below:

/hook
+└─── lib
+     - index.js
+- LICENSE.md
+- package.json
+- README.md
+

The index.js is the entry point to your hook. It should look like the example above.

Dependencies

It happens that a hook has a dependency to another one. For example, the strapi-hook-bookshelf has a dependency to strapi-hook-knex. Without it, the strapi-hook-bookshelf can't work correctly. It also means that the strapi-hook-knex hook has to be loaded before.

To handle this case, you need to update the package.json at the root of your hook.

{
+  "name": "strapi-hook-bookshelf",
+  "version": "x.x.x",
+  "description": "Bookshelf hook for the Strapi framework",
+  "dependencies": {
+    ...
+  },
+  "strapi": {
+    "dependencies": [
+      "strapi-hook-knex"
+    ]
+  }
+}
+

Custom hooks

The framework allows to load hooks from the project directly without having to install them from npm. It's great way to take advantage of the features of the hooks system for code that doesn't need to be shared between apps. To achieve this, you have to create a ./hooks folder at the root of your project and put the hooks into it.

/project
+└─── admin
+└─── api
+└─── config
+└─── hooks
+│   └─── strapi-documentation
+│        - index.js
+│   └─── strapi-server-side-rendering
+│        - index.js
+└─── plugins
+└─── public
+- favicon.ico
+- package.json
+- server.js
+
+ + + diff --git a/docs/.vuepress/dist/3.x.x/advanced/logging.html b/docs/.vuepress/dist/3.x.x/advanced/logging.html new file mode 100644 index 0000000000..3c6e126eb2 --- /dev/null +++ b/docs/.vuepress/dist/3.x.x/advanced/logging.html @@ -0,0 +1,66 @@ + + + + + + Logging | Strapi Docs + + + + + + + +

Logging

Strapi relies on an extremely fast Node.js logger called Pino that includes a shell utility to pretty-print its log files. It provides great performances and doesn't slow down your app. The logger is accessible through the global variable strapi.log or the request's context ctx.log if enabled.

// Folder.js controller
+const fs = require('fs');
+const path = require('path');
+
+module.exports = {
+
+  /**
+   * Retrieve app's folders.
+   *
+   * @return {Object|Array}
+   */
+
+  findFolders: async (ctx) => {
+    try {
+      const folders = fs.readdirSync(path.resolve(process.cwd()));
+
+      strapi.log.info(folders); // ctx.log.info(folders);
+
+      ctx.send(folders);
+    } catch (error) {
+      strapi.log.fatal(error); // ctx.log.fatal(error);
+      ctx.badImplementation(error.message);
+    }
+  }
+}
+

Global logger configuration

The global logger is configured by environment variables.

STRAPI_LOG_LEVEL: Can be 'fatal', 'error', 'warn', 'info', 'debug' or 'trace'. +STRAPI_LOG_TIMESTAMP: Can be true/false +STRAPI_LOG_PRETTY_PRINT: Can be true/false +STRAPI_LOG_FORCE_COLOR: Can be true/false

Request logging middleware

To configure the request-logger middleware, you have to edit the following file ./config/environments/*/request.json.

{
+  ...
+  "logger": {
+    "level": "debug",
+    "exposeInContext": true,
+    "requests": true
+  },
+  ...
+}
+
  • level: defines the desired logging level (fatal, error, warn, info, debug, trace).
  • exposeInContext: allows access to the logger through the context.
  • requests: incoming HTTP requests will be logged.

To find more details about the logger API, please refer to the Pino documentation.

+ + + diff --git a/docs/.vuepress/dist/3.x.x/advanced/middlewares.html b/docs/.vuepress/dist/3.x.x/advanced/middlewares.html new file mode 100644 index 0000000000..8a78b828e6 --- /dev/null +++ b/docs/.vuepress/dist/3.x.x/advanced/middlewares.html @@ -0,0 +1,142 @@ + + + + + + Middlewares | Strapi Docs + + + + + + + +

Middlewares

The middlewares are functions which are composed and executed in a stack-like manner upon request. If you are not familiar with the middleware stack in Koa, we highly recommend you to read the Koa's documentation introduction.

Enable the middleware in environments settings

Path — [config/environments/**]

  "urlReader": {
+      "enabled": true
+  }
+

Path — strapi/lib/middlewares/responseTime/index.js.

module.exports = strapi => {
+  return {
+    initialize: function(cb) {
+      strapi.app.use(async (ctx, next) => {
+        const start = Date.now();
+
+        await next();
+
+        const delta = Math.ceil(Date.now() - start);
+
+        // Set X-Response-Time header
+        ctx.set('X-Response-Time', delta + 'ms');
+      });
+
+      cb();
+    }
+  };
+};
+
  • initialize (function): Called during the server boot. The callback cb needs to be called. Otherwise, the middleware won't be loaded into the stack.

The core of Strapi embraces a small list of middlewares for performances, security and great error handling.

  • boom
  • cors
  • cron
  • csp
  • csrf
  • favicon
  • gzip
  • hsts
  • ip
  • language
  • logger
  • p3p
  • parser
  • public
  • responses
  • responseTime
  • router
  • session
  • xframe
  • xss

The following middlewares cannot be disabled: responses, router, logger and boom.

Structure

A middleware needs to follow the structure below:

/middleware
+└─── lib
+     - index.js
+- LICENSE.md
+- package.json
+- README.md
+

The index.js is the entry point to your middleware. It should look like the example above.

Custom middlewares

The framework allows the application to override the default middlewares and add new ones. You have to create a ./middlewares folder at the root of your project and put the middlewares into it.

/project
+└─── admin
+└─── api
+└─── config
+└─── middlewares
+│   └─── responseTime // It will override the core default responseTime middleware
+│        - index.js
+│   └─── views // It will be added into the stack of middleware
+│        - index.js
+└─── plugins
+└─── public
+- favicon.ico
+- package.json
+- server.js
+

Every middleware will be injected into the Koa stack. To manage the load order, please refer to the Middleware order section.

Load order

The middlewares are injected into the Koa stack asynchronously. Sometimes it happens that some of these middlewares need to be loaded in a specific order. To define a load order, we created a dedicated file located in ./config/middleware.json.

Path — ./config/middleware.json.

{
+  "timeout": 100,
+  "load": {
+    "before": [
+      "responseTime",
+      "logger",
+      "cors",
+      "responses"
+    ],
+    "order": [
+      "Define the middlewares' load order by putting their name in this array in the right order"
+    ],
+    "after": [
+      "parser",
+      "router"
+    ]
+  }
+}
+
  • timeout: defines the maximum allowed milliseconds to load a middleware.
  • load: +
    • before: array of middlewares that need to be loaded in the first place. The order of this array matters.
    • order: array of middlewares that need to be loaded in a specific order.
    • after: array of middlewares that need to be loaded at the end of the stack. The order of this array matters.

Examples

Load a middleware at the very first place

Path — ./config/middleware.json

  {
+    "timeout": 100,
+    "load": {
+      "before": [
+        "responseTime",
+        "logger"
+      ],
+      "order": [],
+      "after": []
+    }
+  }
+

The responseTime middleware will be loaded first. Immediately followed by the logger middleware. Then, the others middlewares will be loaded asynchronously.

Load a middleware after another one

Path — ./config/middleware.json.

  {
+    "timeout": 100,
+    "load": {
+      "before": [],
+      "order": [
+        "p3p",
+        "gzip"
+      ],
+      "after": []
+    }
+  }
+

The gzip middleware will be loaded after the p3p middleware. All the others will be loaded asynchronously.

Load a middleware at the very end

Path — ./config/middleware.json.

  {
+    "timeout": 100,
+    "load": {
+      "before": [
+        ...
+      ],
+      "order": [],
+      "after": [
+        "parser",
+        "router"
+      ]
+    }
+  }
+

The router middleware will be loaded at the very end. The parser middleware will be loaded after all the others and just before the router middleware.

Complete example

For this example, we are going to imagine that we have 10 middlewares to load:

  • cors
  • cron
  • favicon
  • gzip
  • logger
  • p3p
  • parser
  • response
  • responseTime
  • router

We assume that we set the ./config/middleware.json file like this:

  {
+    "timeout": 100,
+    "load": {
+      "before": [
+        "responseTime",
+        "logger",
+        "cors",
+      ],
+      "order": [
+        "p3p",
+        "gzip"
+      ],
+      "after": [
+        "parser",
+        "router"
+      ]
+    }
+  }
+

Here is the loader order:

  1. responseTime (loaded at the very first place)
  2. logger
  3. cors
  4. favicon (position order not guarantee)
  5. p3p
  6. cron
  7. gzip (loaded after the p3p middlewares)
  8. response (position order not guarantee)
  9. parser
  10. router (loaded at the very end place)
+ + + diff --git a/docs/.vuepress/dist/3.x.x/advanced/usage-tracking.html b/docs/.vuepress/dist/3.x.x/advanced/usage-tracking.html new file mode 100644 index 0000000000..5b6fd14906 --- /dev/null +++ b/docs/.vuepress/dist/3.x.x/advanced/usage-tracking.html @@ -0,0 +1,30 @@ + + + + + + Usage tracking | Strapi Docs + + + + + + + +

Usage tracking

In order to improve the product and understand how the community is using it, we are collecting non-sensitive data.

Collected data

Here is the list of the collected data and why we need them.

  • UUID Identify the app with a unique identifier.
  • Model names and attributes names Understand what kind of APIs are built with Strapi (content or product or service?)
  • Environment state (development, staging, production) Understand how the developers are using the different configurations? How many projects are started in production mode?
  • Node modules names Are developers integrating Strapi with Stripe? It means that we should develop a plugin to simplify the development process with Stripe. +Are developers using Strapi with strapi-hook-bookshelf or strapi-hook-mongoose? It helps us prioritize the issues.
  • OS Is the community using Windows, Linux or Mac? It helps us prioritize the issues.
  • Build configurations How many people are deploying the admin on another server?

We are not collecting sensitive data such as databases configurations, environment or custom variables. The data are encrypted and anonymised.

GDPR

The collected data are non-sensitive or personal data. We are compliant with the European recommendations (see our Privacy Policy).

Disable

You can disable the tracking by removing the uuid property in the package.json file at the root of your project.

+ + + diff --git a/docs/.vuepress/dist/3.x.x/api-reference/reference.html b/docs/.vuepress/dist/3.x.x/api-reference/reference.html new file mode 100644 index 0000000000..f278044250 --- /dev/null +++ b/docs/.vuepress/dist/3.x.x/api-reference/reference.html @@ -0,0 +1,30 @@ + + + + + + API Reference | Strapi Docs + + + + + + + +

API Reference

strapi.admin

This object contains the controllers, models, services and configurations contained in the ./admin folder.

strapi.app

Returns the Koa instance.

strapi.bootstrap

Returns a Promise. When resolved, it means that the ./config/functions/bootstrap.js has been executed. Otherwise, it throws an error.

You can also access to the bootstrap function through strapi.config.functions.boostrap.

strapi.config

Returns an object that represents the configurations of the project. Every JavaScript or JSON file located in the ./config folder will be parsed into the strapi.config object.

strapi.controllers

Returns an object of the controllers wich is available in the project. Every JavaScript file located in the ./api/**/controllers folder will be parsed into the strapi.controllers object. Thanks to this object, you can access to every controller's actions everywhere in the project.

This object doesn't include the admin's controllers and plugin's controllers.

strapi.hook

Returns an object of the hooks available in the project. Every folder that follows this pattern strapi-* and located in the ./node_modules or /hooks folder will be mounted into the strapi.hook object.

strapi.koaMiddlewares

Returns an object of the Koa middlewares found in the ./node_modules folder of the project. This reference is very useful for the Strapi's core.

strapi.load

Returns a function that parses the configurations, hooks, middlewares and APIs of your app. It also loads the middlewares and hooks with the previously loaded configurations. This method could be useful to update references available through the strapi global variable without having to restart the server. However, without restarting the server, the new configurations will not be taken in account.

strapi.log

Returns the Logger (Pino) instance.

strapi.middleware

Returns an object of the middlewares available in the project. Every folder in the ./middlewares folder will be also mounted into the strapi.middleware object.

strapi.models

Returns an object of models available in the project. Every JavaScript or JSON file located in the ./api/**/models folders will be parsed into the strapi.models object. Also every strapi.models.** object is merged with the model's instance returned by the ORM (Mongoose, Bookshelf). It allows to call the ORM methods through the strapi.models.** object (ex: strapi.models.users.find()).

strapi.plugins

Returns an object of plugins available in the project. Each plugin object contains the associated controllers, models, services and configurations contained in the ./plugins/**/ folder.

strapi.query

Returns a function that will returns the available queries for this model. This feature is only available inside the plugin's files (controllers, services, custom functions). For more details, see the [ORM queries section](../plugin-development/backend-development.md#ORM queries).

strapi.reload

Returns a function that reloads the entire app (with downtime).

strapi.router

Returns the Router (Joi router) instance.

strapi.server

Returns the http.Server instance.

strapi.services

Returns an object of services available in the project. Every JavaScript file located in the ./api/**/services folders will be parsed into the strapi.services object.

strapi.start

Returns a function that loads the configurations, middlewares and hooks. Then, it executes the bootstrap file, freezes the global variable and listens the configured port.

strapi.stop

Returns a function that shuts down the server and destroys the current connections.

strapi.utils

Returns a set of utils.

+ + + diff --git a/docs/.vuepress/dist/3.x.x/cli/CLI.html b/docs/.vuepress/dist/3.x.x/cli/CLI.html new file mode 100644 index 0000000000..2a067d6180 --- /dev/null +++ b/docs/.vuepress/dist/3.x.x/cli/CLI.html @@ -0,0 +1,79 @@ + + + + + + Command Line Interface (CLI) | Strapi Docs + + + + + + + +

Command Line Interface (CLI)

Strapi comes with a full featured Command Line Interface (CLI) which lets you scaffold and manage your project in seconds.


strapi new

Create a new project

strapi new <name>
+
+options: [--dev|--dbclient=<dbclient> --dbhost=<dbhost> --dbport=<dbport> --dbname=<dbname> --dbusername=<dbusername> --dbpassword=<dbpassword> --dbssl=<dbssl> --dbauth=<dbauth>]
+
  • strapi new <name>
    +Generates a new project called <name> and installs the default plugins through the npm registry.

  • strapi new <name> --dev
    +Generates a new project called <name> and creates symlinks for the ./admin folder and each plugin inside the ./plugin folder. It means that the Strapi's development workflow has been set up on the machine earlier.

  • strapi new <name> --dbclient=<dbclient> --dbhost=<dbhost> --dbport=<dbport> --dbname=<dbname> --dbusername=<dbusername> --dbpassword=<dbpassword> --dbssl=<dbssl> --dbauth=<dbauth>
    +Generates a new project called <name> and skip the interactive database configuration and initilize with these options. <dbclient> can be mongo, postgres, mysql, sqlite3 or redis. <dbssl> and <dbauth> are optional.

    See the CONTRIBUTING guide for more details.


strapi generate:api

Scaffold a complete API with its configurations, controller, model and service.

strapi generate:api <name> [<attribute:type>]
+
+options: [--tpl <name>|--plugin <name>]
+
  • strapi generate:api <name>
    +Generates an API called <name> in the ./api folder at the root of your project.

  • strapi generate:api <name> <attribute:type>
    +Generates an API called <name> in the ./api folder at the root of your project. The model will already contain an attribute called <attribute> with the type property set to <type>.

    Example: strapi generate:api product name:string description:text price:integer

  • strapi generate:api <name> --plugin <plugin>
    +Generates an API called <name> in the ./plugins/<plugin> folder.

    Example: strapi generate:api product --plugin content-manager

  • strapi generate:api <name> --tpl <template>
    +Generates an API called <name> in the ./api folder which works with the given <template>. By default, the generated APIs are based on Mongoose.

    Example: strapi generate:api product --tpl bookshelf

The first letter of the filename will be uppercased.

strapi generate:controller

Create a new controller

strapi generate:controller <name>
+
+options: [--api <name>|--plugin <name>]
+
  • strapi generate:controller <name>
    +Generates an empty controller called <name> in the ./api/<name>/controllers folder.

    Example: strapi generate:controller category will create the controller at ./api/category/controllers/Category.js.

  • strapi generate:controller <name> --api <api>
    +Generates an empty controller called <name> in the ./api/<api>/controllers folder.

    Example: strapi generate:controller category --api product will create the controller at ./api/product/controllers/Category.js.

  • strapi generate:controller <name> --plugin <plugin>
    +Generates an empty controller called <name> in the ./plugins/<plugin>/controllers folder.

The first letter of the filename will be uppercased.

strapi generate:model

Create a new model

strapi generate:model <name> [<attribute:type>]
+
+options: [--api <name>|--plugin <name>]
+
  • strapi generate:model <name>
    +Generates an empty model called <name> in the ./api/<name>/models folder. It will create two files. +The first one will be <name>.js which contains your lifecycle callbacks and another <name>.settings.json that will list your attributes and options.

    Example: strapi generate:model category will create these two files ./api/category/models/Category.js and ./api/category/models/Category.settings.json.

  • strapi generate:model <name> <attribute:type>
    +Generates an empty model called <name> in the ./api/<name>/models folder. The file <name>.settings.json will already contain a list of attribute with their associated <type>.

    Example: strapi generate:model category name:string description:text will create these two files ./api/category/models/Category.js and ./api/category/models/Category.settings.json. This last file will contain two attributes name with the type string and description with type text.

  • strapi generate:model <name> --api <api>
    +Generates an empty model called <name> in the ./api/<api>/models folder.

    Example: strapi generate:model category --api product will create these two files:

    • ./api/product/models/Category.js
    • ./api/product/models/Category.settings.json.
  • strapi generate:model <name> --plugin <plugin>
    +Generates an empty model called <name> in the ./plugins/<plugin>/models folder.

The first letter of the filename will be uppercased.

strapi generate:service

Create a new service

strapi generate:service <name>
+
+options: [--api <name>|--plugin <name>]
+
  • strapi generate:service <name>
    +Generates an empty service called <name> in the ./api/<name>/services folder.

    Example: strapi generate:service category will create the service at ./api/category/services/Category.js.

  • strapi generate:service <name> --api <api>
    +Generates an empty service called <name> in the ./api/<api>/services folder.

    Example: strapi generate:service category --api product will create the service at ./api/product/services/Category.js.

  • strapi generate:service <name> --plugin <plugin>
    +Generates an empty service called <name> in the ./plugins/<plugin>/services folder.

The first letter of the filename will be uppercased.

strapi generate:policy

Create a new policy

strapi generate:policy <name>
+
+options: [--api <name>|--plugin <name>]
+
  • strapi generate:policy <name>
    +Generates an empty policy called <name> in the ./config/policies folder.

    Example: strapi generate:policy isAuthenticated will create the policy at ./config/policies/isAuthenticated.js.

  • strapi generate:policy <name> --api <api>
    +Generates an empty policy called <name> in the ./api/<api>/config/policies folder. This policy will be scoped and only accessible by the <api> routes.

    Example: strapi generate:policy isAuthenticated --api product will create the policy at ./api/product/config/policies/isAuthenticated.js.

  • strapi generate:policy <name> --plugin <plugin>
    +Generates an empty policy called <name> in the ./plugins/<plugin>/config/policies folder. This policy will be scoped and accessible only by the <plugin> routes.

strapi generate:plugin

Create a new plugin skeleton.

strapi generate:plugin <name>
+
  • strapi generate:plugin <name>
    +Generates an empty plugin called <name> in the ./plugins folder.

    Example: strapi generate:plugin user will create the plugin at ./plugins/user.

Please refer to the plugin develoment documentation to know more.


strapi install

Install a plugin in the project.

strapi install <name>
+
+options: [--dev]
+
  • strapi install <name>
    +Installs a plugin called <name> in the ./plugins folder.

    Example: strapi install content-type-builder will install the plugin at ./plugins/content-type-builder.

  • strapi install <name> --dev
    +It will create a symlink from the local Strapi repository plugin folder called <name> in the ./plugins folder.

    Example: strapi install content-type-builder --dev will create a symlink from /path/to/the/repository/packages/strapi-plugin-content-type-builder to ./plugins/content-type-builder.

Checkout the CONTRIBUTING guide for more details about the local Strapi development workflow.

WARNING

You have to restart the server to load the plugin into your project.

Please refer to the plugins documentation to know more.


strapi uninstall

Uninstall a plugin from the project.

strapi uninstall <name>
+
  • strapi uninstall <name>
    +Uninstalls a plugin called <name> in the ./plugins folder.

    Example: strapi uninstall content-type-builder will remove the folder at ./plugins/content-type-builder.

Please refer to the plugins documentation to know more.


strapi version

Print the current globally installed Strapi version.

strapi version
+

strapi help

List CLI commands.

strapi help
+
+ + + diff --git a/docs/.vuepress/dist/3.x.x/concepts/concepts.html b/docs/.vuepress/dist/3.x.x/concepts/concepts.html new file mode 100644 index 0000000000..f68bd1b90b --- /dev/null +++ b/docs/.vuepress/dist/3.x.x/concepts/concepts.html @@ -0,0 +1,70 @@ + + + + + + Concepts | Strapi Docs + + + + + + + +

Concepts


Files structure

By default, your project's structure will look like this:

  • /admin: contains the vast majority of the admin's front-end and back-end logic.
  • /api: contains the business logic of your project will be in this folder split in sub-folder per API. +
  • /node_modules: contains the npm's packages used by the project.
  • /config
    • /environments: contains the project's configurations per environment. +
      • /**
        • /development
          • custom.json: contains the custom configurations for this environment.
          • database.json: contains the database connections for this environment.
          • request.json: contains the request settings for this environment.
          • response.json: contains the response settings for this environment.
          • server.json: contains the server settings for this environment.
        • /production
        • /staging
    • /functions: contains lifecycle or generic functions of the project.
    • /locales: contains the translation files used by the built-in i18n feature.
    • application.json: contains the general configurations of the project.
    • custom.json: contains the custom configurations of the project.
    • hook.json: contains the hook settings of the project.
    • language.json: contains the language settings of the project.
    • middleware.json: contains the middleware settings of the project.
  • /hooks: contains the custom hooks of the project.
  • /middlewares: contains the custom middlewares of the project.
  • /plugins: contains the installed plugins in the project.
  • /public: contains the file accessible to the outside world.

Inside the /config folder, every folder will be parsed and injected into the global object strapi.config. Let's say, you added a folder named credentials with two files stripe.json and paypal.json into it. The content of these files will be accessible through strapi.config.credentials.stripe and strapi.config.credentials.paypal.


Controllers

Controllers are JavaScript files which contain a set of methods called actions reached by the client according to the requested route. It means that every time a client requests the route, the action performs the business logic coded and sends back the response. They represent the C in the MVC pattern. In most cases, the controllers will contain the bulk of a project's business logic.

module.exports = {
+  // GET /hello
+  index: async (ctx) => {
+    ctx.send('Hello World!');
+  }
+};
+

In this example, any time a web browser is pointed to the /hello URL on your app, the page will display the text: Hello World!.

Where are the controllers defined?

The controllers are defined in each ./api/**/controllers/ folders. Every JavaScript file put in these folders will be loaded as a controller. They are also available through the strapi.controllers and strapi.api.**.controllers global variables. By convention, controllers' names should be Pascal-cased, so that every word in the file (include the first one) is capitalized User.js, LegalEntity.js.

Please refer to the controllers' guide for more informations.


Filters

Filters are a handy way to request data according to generic parameters. It makes filtering, sorting and paginating easy and reusable (eg. GET /user?_limit=30&name=John).

Please refer to the filters' guide for more informations.


Models

Models are a representation of the database's structure and lifecyle. They are split into two separate files. A JavaScript file that contains the lifecycle callbacks, and a JSON one that represents the data stored in the database and their format. The models also allow you to define the relationships between them.

Path — ./api/user/models/User.js.

module.exports = {
+  // Before saving a value.
+  // Fired before an `insert` or `update` query.
+  beforeSave: (next) => {
+    // Use `this` to get your current object
+    next();
+  },
+
+  // After saving a value.
+  // Fired after an `insert` or `update` query.
+  afterSave: (doc, next) => {
+    next();
+  },
+
+  // ... and more
+};
+

Path — ./api/user/models/User.settings.json.

{
+  "connection": "default",
+  "info": {
+    "name": "user",
+    "description": "This represents the User Model"
+  },
+  "attributes": {
+    "firstname": {
+      "type": "string"
+    },
+    "lastname": {
+      "type": "string"
+    }
+  }
+}
+

In this example, there is a User model which contains two attributes firstname and lastname.

Where are the models defined?

The models are defined in each ./api/**/models/ folder. Every JavaScript or JSON file in these folders will be loaded as a model. They are also available through the strapi.models and strapi.api.**.models global variables. Usable every where in the project, they contain the ORM model object that they are refer to. By convention, models' names should be written in lowercase.

Attributes

A model must contain a list of attributes, and each of these attributes must have a type.

Please refer to the models' guide for more informations about the attributes.

Relations

Many-to-many

Many-to-many associations allow to link an entry to many entry.

Please refer to the many-to-many guide

One-to-many

One-way relationships are useful to link an entry to another.

Please refer to the one-to-many guide

One-to-one

One-way relationships are useful to link an entry to another.

Please refer to the one-to-one guide.

One-way

One-way relationships are useful to link an entry to another. However, only one of the models can be queried with its populated items.

Please refer to the one-way guide.

Lifecycle callbacks

Lifecycle callbacks are functions triggered at specific moments of the queries.

Please refer to the lifecycle callbacks guide.


Internationalization and localization

Internationalization and localization (i18n) allows to adapt the project to different languages and serve the right content to the users. This feature is deeply integrated into the Strapi's core. It will detect the user language preference (locale) and translate the requested content using the translation files.

Please refer to the internationalization's guide.


Plugin

A plugin is like a fully independent sub-application. It has its own business logic with dedicated models, controllers, services, middlewares or hooks. It can also contain an UI integrated into the admin panel to use it easily. It allows to develop or plugin features in a project in a short time span.

Please refer to the plugins documentation for more informations.


Plugin styles

The admin panel uses Bootstrap to be styled on top of solid conventions and reusable CSS classes. It is also using PostCSS and PostCSS SCSS to keep the code maintainable.

Please refer to the plugin front-end development for detailed informations.


Policies

Policies are functions which have the ability to execute specific logic on each request before it reaches the controller's action. They are mostly used for securing business logic easily. +Each route of the project can be associated to an array of policies. For example, you can create a policy named isAdmin, which obviously checks that the request is sent by an admin user, and use it for critical routes.

Policies can be:

  • global: so they can be used within the entire project.
  • scoped: used by single API or plugin.

Where are the policies defined?

The API and plugins policies (scoped) are defined in each ./api/**/config/policies/ folders and plugins. They are respectively exposed through strapi.api.**.config.policies and strapi.plugins.**.config.policies. The global policies are defined at ./config/policies/ and accessible via strapi.config.policies.

Please refer to the policy guide

Global policies

Global policies are reusable through the entire app.

Please refer to the global policy guide

Scoped policies

A policy defined in an API or plugin is usable only from this API or plugin. You don't need any prefix to use it.

Please refer to the scoped policy guide.

Plugin policies

Plugin policies are usable from any app API.

Please refer to the plugin policy guide.

Public Assets

Public assets are static files such as images, video, css, etc that you want to make accessible to the outside world. Every new project includes by default, a folder named ./public.

Please refer to the public configuration for more informations.


Requests

The context object (ctx) contains all the request's related informations.

Please refer to the requests guide for more informations.


Responses

The context object (ctx) contains a list of values and functions useful to manage server responses.

Please refer to the responses guide for more informations.


Routing

./api/**/config/routes.json files define all available routes for the clients.

Please refer to the routing guide for more informations.


Services

Services are a set of reusable functions. They are particularly useful to respect the DRY (don’t repeat yourself) programming concept and to simplify controllers logic.

Please refer to the services guide for more informations.


+ + + diff --git a/docs/.vuepress/dist/3.x.x/configurations/configurations.html b/docs/.vuepress/dist/3.x.x/configurations/configurations.html new file mode 100644 index 0000000000..2ef2b4240a --- /dev/null +++ b/docs/.vuepress/dist/3.x.x/configurations/configurations.html @@ -0,0 +1,185 @@ + + + + + + Configurations | Strapi Docs + + + + + + + +

Configurations

The main configurations of the project are located in the ./config directory. Additional configs can be added in the ./api/**/config folder of each API and plugin by creating JavaScript or JSON files.

Application

Contains the main configurations relative to your project.

Path — ./config/application.json.

{
+  "favicon": {
+    "path": "favicon.ico",
+    "maxAge": 86400000
+  },
+  "public": {
+    "path": "./public",
+    "maxAge": 60000
+  }
+}
+
  • favicon
    • path (string): Path to the favicon file. Default value: favicon.ico.
    • maxAge (integer): Cache-control max-age directive in ms. Default value: 86400000.
  • public
    • path (string): Path to the public folder. Default value: ./public.
    • maxAge (integer): Cache-control max-age directive in ms. Default value: 60000.

Custom

Add custom configurations to the project. The content of this file is available through the strapi.config object.

Example

Path — ./config/custom.json.

{
+  "backendURL": "http://www.strapi.io",
+  "mainColor": "blue"
+}
+

These configurations are accessible through strapi.config.backendURL and strapi.config.mainColor.


Language

As described in the i18n documentation, Strapi includes an internationalization system. This is especially useful to translate API messages (errors, etc.).

Path — ./config/language.json.

{
+  "enabled": true,
+  "defaultLocale": "en_us",
+  "modes": [
+    "query",
+    "subdomain",
+    "cookie",
+    "header",
+    "url",
+    "tld"
+  ],
+  "cookieName": "locale"
+}
+
  • enabled (boolean): Enable or disable i18n. Default value: true.
  • defaultLocale (string): Default locale used by the application. Default value: en_us.
  • modes (array): Methods used to detect client language. Default value: ["query", "subdomain", "cookie", "header", "url", "tld"].
  • cookieName (string): Name of the cookie used to store the locale name. Default value: locale.

Functions

The ./config/functions/ folder contains a set of JavaScript files in order to add dynamic and logic based configurations.

Bootstrap

Path — ./config/functions/bootstrap.js.

The bootstrap function is called at every server start. You can use it to add a specific logic at this moment of your server's lifecycle.

Here are some use cases:

  • Create an admin user if there isn't.
  • Fill the database with some necessary data.
  • Check that the database is up-and-running.

CRON tasks

CRON tasks allow you to schedule jobs (arbitrary functions) for execution at specific dates, with optional recurrence rules. It only uses a single timer at any given time (rather than reevaluating upcoming jobs every second/minute).

Make sure the enabled cron config is set to true in your environment's variables.

The cron format consists of:

*    *    *    *    *    *
+┬    ┬    ┬    ┬    ┬    ┬
+│    │    │    │    │    |
+│    │    │    │    │    └ day of week (0 - 7) (0 or 7 is Sun)
+│    │    │    │    └───── month (1 - 12)
+│    │    │    └────────── day of month (1 - 31)
+│    │    └─────────────── hour (0 - 23)
+│    └──────────────────── minute (0 - 59)
+└───────────────────────── second (0 - 59, OPTIONAL)
+

To define a CRON job, add your logic like bellow:

Path — ./config/functions/cron.js.

module.exports = {
+
+  /**
+   * Simple example.
+   * Every monday at 1am.
+   */
+
+  '0 0 1 * * 1': () => {
+    // Add your own logic here (eg. send a queue of email, create a database backup, etc.).
+  }
+};
+

Locales

The locales directory contains the translations of your API.

Each JSON file located in the folder must have the name of its corresponding translation (eg. en_US.json, fr_FR.json, etc.). Each line defines a translation key and its corresponding value.

Example

Path — ./config/locales/en_US.json.

{
+  "welcome": "Welcome"
+}
+

Take a look at the internationalization's guide for more details.


Environments

Most of the application's configurations are defined by environment. It means that you can specify settings for each environment (development, production, test, etc.).

You can access the config of the current environment through strapi.config.currentEnvironment.


Database

Path — ./config/environments/**/database.json.

  • defaultConnection (string): Connection by default for models which are not related to a specific connection. Default value: default.
  • connections List of all available connections. +
    • default
      • connector (string): Connector used by the current connection. Default value: strapi-hook-mongoose.
      • client (string): Client used to store session. Default value: cookie.
      • key (string): Cookie key name. Default value: strapi.sid
      • maxAge (integer): Time in milliseconds before the session expire. Default value: 86400000.
      • rolling (boolean): Force a session identifier cookie to be set on every response. Default value: false.
      • signed (boolean): httpOnly or not. Default value: true.
      • overwrite (boolean): Can overwrite or not. Default value: true.
      • settings Useful for external session stores such as Redis. +
        • host (string): Database host name. Default value: localhost.
        • port (integer): Database port. Default value: 27017.
        • database (string): Database name. Default value: development.
        • username (string): Username used to establish the connection.
        • password (string): Password used to establish the connection.
        • options (object): List of additional options used by the connector.
        • timezone (string): Set the default behavior for local time (used only for a SQL database). Default value: utc.
    • options Options used for database connection. +
      • ssl (boolean): For ssl database connection.
      • debug (boolean): Show database exchanges and errors.
      • autoMigration (boolean): To disable auto tables/columns creation for SQL database.

Example

Path — ./config/environments/**/database.json.

{
+  "defaultConnection": "default",
+  "connections": {
+    "default": {
+      "connector": "strapi-hook-mongoose",
+      "settings": {
+        "client": "mongo",
+        "host": "localhost",
+        "port": 27017,
+        "database": "development",
+        "username": "fooUsername",
+        "password": "fooPwd"
+      },
+      "options": {
+        "authenticationDatabase": "",
+        "ssl": true,
+        "minimize": true
+      }
+    },
+    "postgres": {
+      "connector": "strapi-hook-bookshelf",
+      "settings": {
+        "client": "postgres",
+        "host": "localhost",
+        "port": 5432,
+        "username": "${process.env.USERNAME}",
+        "password": "${process.env.PWD}",
+        "database": "strapi",
+        "schema": "public"
+      },
+      "options": {
+        "debug": true
+      }
+    },
+    "mysql": {
+      "connector": "strapi-hook-bookshelf",
+      "settings": {
+        "client": "mysql",
+        "host": "localhost",
+        "port": 5432,
+        "username": "strapi",
+        "password": "root",
+        "database": ""
+      },
+      "options": {}
+    },
+    "redis": {
+      "connector": "strapi-redis",
+      "settings": {
+        "port": 6379,
+        "host": "localhost",
+        "password": ""
+      },
+      "options": {
+        "debug": false
+      }
+    }
+  }
+}
+

Please refer to the dynamic configurations section to use global environment variable to configure the databases.


Request

Path — ./config/environments/**/request.json.

  • session
    • enabled (boolean): Enable or disable sessions. Default value: false.
    • client (string): Client used to persist sessions. Default value: redis.
    • settings
      • host (string): Client host name. Default value: localhost.
      • port (integer): Client port. Default value: 6379.
      • database(integer)|String - Client database name. Default value: 10.
      • password (string): Client password. Default value: .
  • logger
    • level (string): Default log level. Default value: debug.
    • exposeInContext (boolean): Expose logger in context so it can be used through strapi.log.info(‘my log’). Default value: true.
    • requests (boolean): Enable or disable requests logs. Default value: false.
  • parser
  • enabled(boolean): Enable or disable parser. Default value: true.
  • multipart (boolean): Enable or disable multipart bodies parsing. Default value: true.
  • router
  • prefix (string): API url prefix (eg. /v1).

The session doesn't work with mongo as a client. The package that we should use is broken for now.


Response

Path — ./config/environments/**/response.json.

  • gzip
  • enabled (boolean): Enable or not GZIP response compression.
  • responseTime
  • enabled (boolean): Enable or not X-Response-Time header to response. Default value: false.

Security

Path — ./config/environments/**/security.json.

  • csrf
    • enabled (boolean): Enable or disable CSRF. Default value: depends on the environment.
    • key (string): The name of the CSRF token added to the model. Default value: _csrf.
    • secret (string): The key to place on the session object which maps to the server side token. Default value: _csrfSecret.
  • csp
    • enabled (boolean): Enable or disable CSP to avoid Cross Site Scripting (XSS) and data injection attacks.
  • p3p
  • enabled (boolean): Enable or disable p3p.
  • hsts
  • enabled (boolean): Enable or disable HSTS.
  • maxAge (integer): Number of seconds HSTS is in effect. Default value: 31536000.
  • includeSubDomains (boolean): Applies HSTS to all subdomains of the host. Default value: true.
  • xframe
    • enabled (boolean): Enable or disable X-FRAME-OPTIONS headers in response.
    • value (string): The value for the header, e.g. DENY, SAMEORIGIN or ALLOW-FROM uri. Default value: SAMEORIGIN.
  • xss
  • enabled (boolean): Enable or disable XSS to prevent Cross Site Scripting (XSS) attacks in older IE browsers (IE8).
  • cors
  • enabled (boolean): Enable or disable CORS to prevent your server to be requested from another domain.
  • origin (string): Allowed URLs (http://example1.com, http://example2.com or allows everyone *). Default value: http://localhost.
  • expose (array): Configures the Access-Control-Expose-Headers CORS header. If not specified, no custom headers are exposed. Default value: ["WWW-Authenticate", "Server-Authorization"].
  • maxAge (integer): Configures the Access-Control-Max-Age CORS header. Default value: 31536000.
  • credentials (boolean): Configures the Access-Control-Allow-Credentials CORS header. Default value: true.
  • methods (array)|String - Configures the Access-Control-Allow-Methods CORS header. Default value: ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD"].
  • headers (array): Configures the Access-Control-Allow-Headers CORS header. If not specified, defaults to reflecting the headers specified in the request's Access-Control-Request-Headers header. Default value: ["Content-Type", "Authorization", "X-Frame-Options"].
  • ip
    • enabled (boolean): Enable or disable IP blocker. Default value: false.
    • whiteList (array): Whitelisted IPs. Default value: [].
    • blackList (array): Blacklisted IPs. Default value: [].

Server

Path — ./config/environments/**/server.json.

  • host (string): Host name. Default value: localhost.
  • port (integer): Port on which the server should be running. Default value: 1337.
  • autoReload (boolean): Enable or disabled server reload on files update. Default value: depends on the environment.
  • cron
  • enabled (boolean): Enable or disable CRON tasks to schedule jobs at specific dates. Default value: false.
  • admin
  • path (string): Allow to change the URL to access the admin (default: /admin).
  • build
    • host (string): URL to access the admin panel (default: http://localhost:1337/admin).
    • backend (string): URL that the admin panel and plugins will request (default: http://localhost:1337). +
      • plugins
        • source (string): Define the source mode (origin, host, custom).
        • folder (string): Indicate what the plugins folder in host source mode.

Dynamic configurations

For security reasons, sometimes it's better to set variables through the server environment. It's also useful to push dynamics values into configurations files. To enable this feature into JSON files, Strapi embraces a JSON-file interpreter into his core to allow dynamic value in the JSON configurations files.

Syntax

The syntax is inspired by the template literals ES2015 specifications. These dynamic values are indicated by the Dollar sign and curly braces (${expression}).

Usage

In any JSON configurations files in your project, you can inject dynamic values like this:

Path — ./config/environments/production/database.json.

{
+  "defaultConnection": "default",
+  "connections": {
+    "default": {
+      "connector": "strapi-hook-mongoose",
+      "settings": {
+        "client": "mongo",
+        "uri": "${process.env.DATABASE_URI || ''}",
+        "host": "${process.env.DATABASE_HOST || '127.0.0.1'}",
+        "port": "${process.env.DATABASE_PORT || 27017}",
+        "database": "${process.env.DATABASE_NAME || 'production'}",
+        "username": "${process.env.DATABASE_USERNAME || ''}",
+        "password": "${process.env.DATABASE_PASSWORD || ''}"
+      },
+      "options": {}
+    }
+  }
+}
+

You can't execute functions inside the curly braces. Only strings are allowed.


Database configuration

Configuration files are not multi server friendly. So we create a data store for config you will want to update in production.

Usage

Get settings:

  • environment (string): Sets the environment you want to store the data in. By default it's current environment (can be an empty string if your config is environment agnostic).
  • type (string): Sets if your config is for an api, plugin or core. By default it's core.
  • name (string): You have to set the plugin or api name if type is api or plugin.
  • key (string, required): The name of the key you want to store.
// strapi.store(object).get(object);
+
+// create reusable plugin store variable
+const pluginStore = strapi.store({
+  environment: strapi.config.environment,
+  type: 'plugin',
+  name: 'users-permissions'
+});
+
+await pluginStore.get({key: 'grant'});
+

Set settings:

  • value (any, required): The value you want to store.
// strapi.store(object).set(object);
+
+// create reusable plugin store variable
+const pluginStore = strapi.store({
+  environment: strapi.config.environment,
+  type: 'plugin',
+  name: 'users-permissions'
+});
+
+await pluginStore.set({
+  key: 'grant',
+  value: {
+    ...
+  }
+});
+
+ + + diff --git a/docs/.vuepress/dist/3.x.x/getting-started/installation.html b/docs/.vuepress/dist/3.x.x/getting-started/installation.html new file mode 100644 index 0000000000..5ac3f059bf --- /dev/null +++ b/docs/.vuepress/dist/3.x.x/getting-started/installation.html @@ -0,0 +1,31 @@ + + + + + + Installation | Strapi Docs + + + + + + + +

Installation

Installation is very easy and only takes a few seconds.

Requirements

Please make sure your computer/server meets the following requirements:

  • Node.js >= 9: Node.js is a server platform which runs JavaScript. Installation guide here.
  • MongoDB >= 2.6: MongoDB is a powerful document store. Installation guide here.

Setup

Time to install Strapi!

npm install strapi@alpha -g
+

If you encounter npm permissions issues, change the permissions to npm default directory.

It takes about 20 seconds with a good Internet connection. You can take a coffee ☕️ if you have a slow one.

Having troubles during the installation? Check if someone already had the same issue https://github.com/strapi/strapi/issues. If not, you can post one, or ask for help https://strapi.io/support.

Check installation

Once completed, please check that the installation went well, by running:

strapi -v
+

That should print 3.0.0-alpha.x.

Strapi is installed globally on your computer. Type strapi in your terminal you will have access to every available command lines.


Congrats! Now that Strapi is installed you can create your first API.

+ + + diff --git a/docs/.vuepress/dist/3.x.x/getting-started/quick-start.html b/docs/.vuepress/dist/3.x.x/getting-started/quick-start.html new file mode 100644 index 0000000000..8fb722d0f1 --- /dev/null +++ b/docs/.vuepress/dist/3.x.x/getting-started/quick-start.html @@ -0,0 +1,91 @@ + + + + + + Quick start | Strapi Docs + + + + + + + +

Quick start

This section explains how to handle Strapi for the first time, (check out our tutorial video).

Table of contents:


Create your first project

Creating your first project with Strapi is easy:

#1 — Open your terminal

Open your terminal in the directory you want to create your application in.

#2 — Run the following command line in your terminal:

strapi new my-project
+

Generate a Strapi project

This action creates a new folder named my-project with the entire files structure of a Strapi application.

#3 — Go to your project and launch the server:

In your terminal run the following commands:

cd my-project
+strapi start
+

Start Strapi

Now that your app is running let's see how to create your first user.


Create your first user

In order to use the admin panel and to consume your API you first need to register your first user. This process only happens once if you don't have any user table created and is made to create the admin user that has all the permissions granted for your API.

If your using MongoDB for your database you don't have to create your table manually (it's already handled by Strapi) otherwise you'll have to create your user table first.

To create your first user, start your server (strapi start) and go to : http://localhost:1337/admin.

Register View

Now that your first user is registered let's see how to create your first api.


Create your first API

To create your first API, start your server (strapi start) and go to : http://localhost:1337/admin.

At this point, your application is empty. To create your first API is to use the Content Type Builder plugin: a powerful UI to help you create an API in a few clicks. Let's take the example of an e-commerce API, which manages products.

#1 — Go to the Content Type Builder plugin.

Content Type Builder - Home

#2 — Create a Content Type named Product and submit the form.

Content Type Builder - Create a new Content Type

#3 — Add three fields in this Content Type.

  • A string field named name.
  • A text field named description.
  • A number field named price (with float as number format).

Content Type Builder - List fields in Product

#4 — Save. That's it!

See the CLI documentation for more information on how to do it the hacker way.

Files structure

A new directory has been created in the ./api folder of your application which contains all the needed stuff for your Product Content Type: routes, controllers, services and models. Take a look at the API structure documentation for more informations.

Well done, you created your first API using Strapi!


Manage your data

After creating your first Content Type, it would be great to be able to create, edit or delete entries.

#1 — Go to the Product list by clicking on the link in the left menu (generated by the Content Manager plugin).

Content Type Builder - Home

#2 — Click on the button Add New Product and fill the form.

Content Type Builder - Home

#3 — Save! You can edit or delete this entry by clicking on the icons at the right of the row.

Content Type Builder - Home


Consume your API

Your API is now ready and contains data. At this point, you'll probably want to use this data in mobile or desktop applications. +In order to do so, you'll need to allow access to other users (identified as 'Guest').

1 - Go to the Auth & Permissions View by clicking on Auth & Permissions link in the left menu and click on the Guest Role item.

Auth & Permissions - Home

2 - Manage your APIs permissions in the Permissions section of the Edit Guest Role view by enabling or disabling specific actions.

Auth & Permissions - Edit Guest

List entries (GET)

To retrieve the list of products, use the GET /your-content-type route.

Generated APIs provide a handy way to filter and order queries. In that way, ordering products by price is as easy as GET http://localhost:1337/product?_sort=price:asc. For more informations, read the filters documentation

Here is an example using jQuery.

$.ajax({
+  type: 'GET',
+  url: 'http://localhost:1337/product?_sort=price:asc', // Order by price.
+  done: function(products) {
+    console.log('Well done, here is the list of products: ', products);
+  },
+  fail: function(error) {
+    console.log('An error occurred:', error);
+  }
+});
+

Get a specific entry (GET)

If you want to get a specific entry, add the id of the wanted product at the end of the url.

$.ajax({
+  type: 'GET',
+  url: 'http://localhost:1337/product/123', // Where `123` is the `id` of the product.
+  done: function(product) {
+    console.log('Well done, here is the product having the `id` 123: ', product);
+  },
+  fail: function(error) {
+    console.log('An error occurred:', error);
+  }
+});
+

Create data (POST)

Use the POST route to create a new entry.

jQuery example:

$.ajax({
+  type: 'POST',
+  url: 'http://localhost:1337/product',
+  data: {
+    name: 'Cheese cake',
+    description: 'Chocolate cheese cake with ice cream',
+    price: 5
+  },
+  done: function(product) {
+    console.log('Congrats, your product has been successfully created: ', product); // Remember the product `id` for the next steps.
+  },
+  fail: function(error) {
+    console.log('An error occurred:', error);
+  }
+});
+

Update data (PUT)

Use the PUT route to update an existing entry.

jQuery example:

$.ajax({
+  type: 'PUT',
+  url: 'http://localhost:1337/product/123', // Where `123` is the `id` of the product.
+  data: {
+    description: 'This is the new description'
+  },
+  done: function(product) {
+    console.log('Congrats, your product has been successfully updated: ', product.description);
+  },
+  fail: function(error) {
+    console.log('An error occurred:', error);
+  }
+});
+

Delete data (DELETE)

Use the DELETE route to delete an existing entry.

jQuery example:

$.ajax({
+  type: 'DELETE',
+  url: 'http://localhost:1337/product/123', // Where `123` is the `id` of the product.
+  done: function(product) {
+    console.log('Congrats, your product has been successfully deleted: ', product);
+  },
+  fail: function(error) {
+    console.log('An error occurred:', error);
+  }
+});
+

Congratulations! You successfully finished the Getting Started guide! Read the concepts to understand more advanced concepts.

Also, feel free to join the community thanks to the different channels listed in the community page: team members, contributors and developers will be happy to help you.

+ + + diff --git a/docs/.vuepress/dist/3.x.x/guides/authentication.html b/docs/.vuepress/dist/3.x.x/guides/authentication.html new file mode 100644 index 0000000000..202238f319 --- /dev/null +++ b/docs/.vuepress/dist/3.x.x/guides/authentication.html @@ -0,0 +1,126 @@ + + + + + + Authentication | Strapi Docs + + + + + + + +

Authentication

WARNING

This feature requires the Users & Permissions plugin (installed by default).

Register a new user

This route lets you create new users.

Usage

$.ajax({
+  type: 'POST',
+  url: 'http://localhost:1337/auth/local/register',
+  data: {
+    username: 'Strapi user',
+    email: 'user@strapi.io',
+    password: 'strapiPassword'
+  },
+  done: function(auth) {
+    console.log('Well done!');
+    console.log('User profile', auth.user);
+    console.log('User token', auth.jwt);
+  },
+  fail: function(error) {
+    console.log('An error occurred:', error);
+  }
+});
+

Login.

This route lets you login your users by getting an authentication token.

Local

  • The identifier param can either be an email or a username.
$.ajax({
+  type: 'POST',
+  url: 'http://localhost:1337/auth/local',
+  data: {
+    identifier: 'user@strapi.io',
+    password: 'strapiPassword'
+  },
+  done: function(auth) {
+    console.log('Well done!');
+    console.log('User profile', auth.user);
+    console.log('User token', auth.jwt);
+  },
+  fail: function(error) {
+    console.log('An error occurred:', error);
+  }
+});
+

Providers

Thanks to Grant and Purest, you can easily use OAuth and OAuth2 +providers to enable authentication in your application. By default, +Strapi comes with the following providers:

👀   See our complete example with detailed tutorials for each provider (with React)


To use the providers authentication, set your credentials in the admin interface (Plugin Users & Permissions > Providers). +Then update and enable the provider you want use.

Redirect your user to: GET /connect/:provider. eg: GET /connect/facebook

After his approval, he will be redirected to /auth/:provider/callback. The jwt and user data will be available in the body response.

Response payload:

{
+  "user": {},
+  "jwt": ""
+}
+

Use your token to be identified as a user.

By default, each API request is identified as guest role (see permissions of guest's role in your admin dashboard). To make a request as a user, you have to set the Authorization token in your request headers. You receive a 401 error if you are not authorized to make this request or if your authorization header is not correct.

Usage

  • The token variable is the data.jwt received when login in or registering.
$.ajax({
+  type: 'GET',
+  url: 'http://localhost:1337/article',
+  headers: {
+    Authorization: `Bearer ${token}`
+  },
+  done: function(data) {
+    console.log('Your data', data);
+  },
+  fail: function(error) {
+    console.log('An error occurred:', error);
+  }
+});
+

Send forgot password request.

This action sends an email to a user with the link of you reset password page. This link contains an URL param code which is required to reset user password.

Usage

  • email is your user email.
  • url is the url link that user will receive.
$.ajax({
+  type: 'POST',
+  url: 'http://localhost:1337/auth/forgot-password',
+  data: {
+    email: 'user@strapi.io',
+    url: 'http://mon-site.com/rest-password'
+  },
+  done: function() {
+    console.log('Your user received an email');
+  },
+  fail: function(error) {
+    console.log('An error occurred:', error);
+  }
+});
+

Received link url format http://mon-site.com/rest-password?code=privateCode

Reset user password.

This action will reset the user password.

Usage

  • code is the url params received from the email link (see forgot password)
$.ajax({
+  type: 'POST',
+  url: 'http://localhost:1337/auth/reset-password',
+  data: {
+    code: 'privateCode',
+    password: 'myNewPassword',
+    passwordConfirmation: 'myNewPassword'
+  },
+  done: function() {
+    console.log('Your user password is reset');
+  },
+  fail: function(error) {
+    console.log('An error occurred:', error);
+  }
+});
+

User Object In Strapi Context

The User object is available to successfully authenticated requests.

Usage

  • The authenticated user object is a property of ctx.state.
  create: async (ctx) => {
+
+    const { _id } = ctx.state.user
+
+    const depositObj = {
+      ...ctx.request.body,
+      depositor: _id
+    }
+
+    const data = await strapi.services.deposit.add(depositObj);
+
+    // Send 201 `created`
+    ctx.created(data);
+  }
+
+

Email templates

See the documentation on GitHub

+ + + diff --git a/docs/.vuepress/dist/3.x.x/guides/controllers.html b/docs/.vuepress/dist/3.x.x/guides/controllers.html new file mode 100644 index 0000000000..e7a9c30b50 --- /dev/null +++ b/docs/.vuepress/dist/3.x.x/guides/controllers.html @@ -0,0 +1,44 @@ + + + + + + Controllers | Strapi Docs + + + + + + + +

Controllers

See the controllers' concepts for details.

How to create a controller?

There are two ways to create a controller:

  • Using the CLI strapi generate:controller user. Read the CLI documentation for more information.
  • Manually create a JavaScript file named User.js in ./api/**/controllers which contains at least one endpoint.

Adding Endpoints

Each controller’s action must be an async function and receives the context (ctx) object as first parameter containing the request context and the response context. The action has to be bounded by a route.

Example

In this example, we are defining a specific route in ./api/hello/config/routes.json that takes Hello.index as handler. It means that every time a web browser is pointed to the /hello URL, the server will called the index action in the Hello.js controller. Our index action will return Hello World!. You can also return a JSON object.

Path — ./api/hello/config/routes.json.

{
+  "routes": [
+    {
+      "method": "GET",
+      "path": "/hello",
+      "handler": "Hello.index"
+    }
+  ]
+}
+

Path — ./api/hello/controllers/Hello.js.

module.exports = {
+  // GET /hello
+  index: async (ctx) => {
+    ctx.send('Hello World!');
+  }
+};
+

A route handler can only access the controllers defined in the ./api/**/controllers folders.

+ + + diff --git a/docs/.vuepress/dist/3.x.x/guides/deployment.html b/docs/.vuepress/dist/3.x.x/guides/deployment.html new file mode 100644 index 0000000000..85785f54db --- /dev/null +++ b/docs/.vuepress/dist/3.x.x/guides/deployment.html @@ -0,0 +1,42 @@ + + + + + + Deployment | Strapi Docs + + + + + + + +

Deployment

#1 - Configurate

Update the production settings with the IP and domain name where the project will be running.

Path — ./config/environments/production/server.json.

{
+  "host": "domain.io", // IP or domain
+  "port": 1337,
+  "autoReload": {
+    "enabled": false
+  },
+  "admin": {
+    "path": "/dashboard" // We highly recommend to change the default `/admin` path for security reasons.
+  }
+}
+

⚠️ If you changed the path to access to the administration, the step #2 is required.

#2 - Setup (optional)

Run this following command to install the dependencies and build the project with your custom configurations.

cd /path/to/the/project
+npm run setup
+

To display the build logs use the --debug option npm run setup --debug.

#3 - Launch the server

Run the server with the production settings.

NODE_ENV=production npm start
+

WARNING

We highly recommend to use pm2 to manage your process.

Advanced configurations

If you want to host the administration on another server than the API, please take a look at this dedicated section.

+ + + diff --git a/docs/.vuepress/dist/3.x.x/guides/email.html b/docs/.vuepress/dist/3.x.x/guides/email.html new file mode 100644 index 0000000000..c9e8325261 --- /dev/null +++ b/docs/.vuepress/dist/3.x.x/guides/email.html @@ -0,0 +1,38 @@ + + + + + + Email | Strapi Docs + + + + + + + +

Email

WARNING

This feature requires the Email plugin (installed by default).

Thanks to the plugin Email, you can send email on your server or externals providers such as Sendgrid.

Usage

await strapi.plugins['email'].services.email.send({
+  to: 'admin@strapi.io',
+  from: 'robbot@strapi.io',
+  replyTo: 'no-reply@strapi.io',
+  subject: 'Use strapi email provider successfully',
+  text: 'Hello world!',
+  html: 'Hello world!'
+});
+

Install providers

By default Strapi provides a local email system. You might want to send email with Sendgrid or another provider.

To install a new provider run:

$ npm install strapi-email-sendgrid@alpha --save
+

We have two providers available strapi-email-sendgrid and strapi-upload-mailgun, use the alpha tag to install one of them. Then, visit /admin/plugins/email/configurations/development on your web browser and configure the provider.

If you want to create your own, make sure the name starts with strapi-email- (duplicating an existing one will be easier to create), modify the auth config object and customize the send functions.

Check all community providers available on npmjs.org - Providers list

+ + + diff --git a/docs/.vuepress/dist/3.x.x/guides/filters.html b/docs/.vuepress/dist/3.x.x/guides/filters.html new file mode 100644 index 0000000000..1ecdff7294 --- /dev/null +++ b/docs/.vuepress/dist/3.x.x/guides/filters.html @@ -0,0 +1,55 @@ + + + + + + Filters | Strapi Docs + + + + + + + +

Filters

See the filters' concepts for details.

by default, the filters can only be used from find endpoints generated by the Content Type Builder and the CLI. If you need to implement a filters system somewhere else, read the programmatic usage section.

Available operators

The available operators are separated in four different categories:

Filters

Easily filter results according to fields values.

  • =: Equals
  • _ne: Not equals
  • _lt: Lower than
  • _gt: Greater than
  • _lte: Lower than or equal to
  • _gte: Greater than or equal to
  • _contains: Contains
  • _containss: Contains case sensitive

Examples

Find users having John as first name.

GET /user?firstName=John

Find products having a price equal or greater than 3.

GET /product?price_gte=3

Sort

Sort according to a specific field.

Example

Sort users by email.

  • ASC: GET /user?_sort=email:asc
  • DESC: GET /user?_sort=email:desc

Limit

Limit the size of the returned results.

Example

Limit the result length to 30.

GET /user?_limit=30

Start

Skip a specific number of entries (especially useful for pagination).

Example

Get the second page of results.

GET /user?_start=10&_limit=10

Programmatic usage

Requests system can be implemented in custom code sections.

Extracting requests filters

To extract the filters from an JavaScript object or a request, you need to call the strapi.utils.models.convertParams helper.

The returned objects is formatted according to the ORM used by the model.

Example

Path — ./api/user/controllers/User.js.

// Define a list of params.
+const params = {
+  '_limit': 20,
+  '_sort': 'email'
+};
+
+// Convert params.
+const formattedParams = strapi.utils.models.convertParams('user', params); // { limit: 20, sort: 'email' }
+

Query usage

Example

Path — ./api/user/controllers/User.js.

module.exports = {
+
+  find: async (ctx) => {
+    // Convert params.
+    const formattedParams = strapi.utils.models.convertParams('user', ctx.request.query);
+
+    // Get the list of users according to the request query.
+    const filteredUsers = await User
+      .find()
+      .where(formattedParams.where)
+      .sort(formattedParams.sort)
+      .skip(formattedParams.start)
+      .limit(formattedParams.limit);
+
+    // Finally, send the results to the client.
+    ctx.body = filteredUsers;
+  };
+};
+
+ + + diff --git a/docs/.vuepress/dist/3.x.x/guides/graphql.html b/docs/.vuepress/dist/3.x.x/guides/graphql.html new file mode 100644 index 0000000000..5e4fb65a55 --- /dev/null +++ b/docs/.vuepress/dist/3.x.x/guides/graphql.html @@ -0,0 +1,414 @@ + + + + + + GraphQL | Strapi Docs + + + + + + + +

GraphQL

WARNING

This feature requires the GraphQL plugin (not installed by default).

Usage

To get started with GraphQL in your app, please install the plugin first. To do that, open your terminal and run the following command:

strapi install graphql
+

Then, start your app and open your browser at http://localhost:1337/playground. You should see the interface (GraphQL Playground) that will help you to write GraphQL query to explore your data.

Install the ModHeader extension to set the Authorization header in your request

Configurations

By default, the Shadow CRUD feature is enabled and the GraphQL is set to /graphql. You can edit these configurations in the following files.

Path — ./plugins/graphql/config/settings.json.

{
+  "endpoint": "/graphql",
+  "shadowCRUD": true,
+  "depthLimit": 7
+}
+

Query API

In the section, we assume that the Shadow CRUD feature is enabled. For each model, the plugin auto-generates queries which just fit to your needs.

Fetch a single entry
  • id: String
query {
+  user(id: "5aafe871ad624b7380d7a224") {
+    username
+    email
+  }
+}
+
Fetch multiple entries
query {
+  users {
+    username
+    email
+  }
+}
+

Filters

You can also apply different parameters to the query to make more complex queries.

  • limit (integer): Define the number of returned entries.
  • start (integer): Define the amount of entries to skip.
  • sort (string): Define how the data should be sorted.
  • where (object): Define the filters to apply in the query. +
    • <field>: Equals.
    • <field>_ne: Not equals.
    • <field>_lt: Lower than.
    • <field>_lte: Lower than or equal to.
    • <field>_gt: Greater than.
    • <field>_gte: Lower than or equal to.
    • <field>_contains: Contains.
    • <field>_containss: Contains sensitive.

Return the second decade of users which have an email that contains @strapi.io ordered by username.

query {
+  users(limit: 10, start: 10, sort: "username:asc", where: {
+    email_contains: "@strapi.io"
+  }) {
+    username
+    email
+  }
+}
+

Return the users which have been created after the March, 19th 2018 4:21 pm.

query {
+  users(where: {
+    createdAt_gt: "2018-03-19 16:21:07.161Z"
+  }) {
+    username
+    email
+  }
+}
+

Shadow CRUD

To simplify and automate the build of the GraphQL schema, we introduced the Shadow CRUD feature. It automatically generates the type definition, queries and resolvers based on your models. The feature also lets you make complex query with many arguments such as limit, sort, start and where.

Example

If you've generated an API called Post using the CLI strapi generate:api post or the administration panel, your model looks like this:

Path — ./api/post/models/Post.settings.json.

{
+  "connection": "default",
+  "options": {
+    "timestamps": true
+  },
+  "attributes": {
+    "title": {
+      "type": "string"
+    }
+    "content": {
+      "type": "text"
+    },
+    "published": {
+      "type": "boolean"
+    }
+  }
+}
+

The generated GraphQL type and queries will be:

type Post {
+  _id: String
+  created_at: String
+  updated_at: String
+  title: String
+  content: String
+  published: Boolean
+}
+
+type Query {
+  posts(sort: String, limit: Int, start: Int, where: JSON): [Post]
+  post(id: String!): Post
+}
+

The query will use the generated controller's actions as resolvers. It means that the posts query will execute the Post.find action and the post query will use the Post.findOne action.

Aggregation & Grouping

This feature is only available on Mongoose ORM.

Strapi now supports Aggregation & Grouping. +Let's consider again the model mentioned above:

type Post {
+  _id: ID
+  createdAt: String
+  updatedAt: String
+  title: String
+  content: String
+  nb_likes: Int,
+  published: Boolean
+}
+
+

Strapi will generate automatically for you the following queries & types:

Aggregation

type PostConnection {
+  values: [Post]
+  groupBy: PostGroupBy
+  aggregate: PostAggregator
+}
+
+type PostGroupBy {
+  _id: [PostConnection_id]
+  createdAt: [PostConnectionCreatedAt]
+  updatedAt: [PostConnectionUpdatedAt]
+  title: [PostConnectionTitle]
+  content: [PostConnectionContent]
+  nb_likes: [PostConnectionNbLikes],
+  published: [PostConnectionPublished]
+}
+
+type PostConnectionPublished {
+  key: Boolean
+  connection: PostConnection
+}
+
+type PostAggregator {
+  count: Int
+  sum: PostAggregatorSum
+  avg: PostAggregatorAvg
+  min: PostAggregatorMin
+  max: PostAggregatorMax
+}
+
+type PostAggregatorAvg {
+  nb_likes: Float
+}
+
+type PostAggregatorMin { // Same for max and sum
+  nb_likes: Int
+}
+
+type Query {
+  postsConnection(sort: String, limit: Int, start: Int, where: JSON): PostConnection
+}
+

Getting the total count and the average likes of posts:

postsConnection {
+  aggregate {
+    count
+    avg {
+      nb_likes
+    }
+  }
+
+}
+

Let's say we want to do the same query but for only published posts

postsConnection(where: { published: true }) {
+  aggregate {
+    count
+    avg {
+      nb_likes
+    }
+  }
+
+}
+

Gettings the average likes of published and unpublished posts

postsConnection {
+  groupBy {
+    published: {
+      key
+      connection {
+        aggregate {
+          avg {
+            nb_likes
+          }
+        }
+      }
+    }
+  }
+}
+

Result

{
+  data: {
+    postsConnection: {
+      groupBy: {
+        published: [
+          {
+            key: true,
+            connection: {
+              aggregate: {
+                avg {
+                  nb_likes: 10
+                }
+              }
+            }
+          },
+          {
+            key: false,
+            connection: {
+              aggregate: {
+                avg {
+                  nb_likes: 0
+                }
+              }
+            }
+          }
+        ]
+      }
+    }
+  }
+}
+

Customise the GraphQL schema

If you want to define a new scalar, input or enum types, this section is for you. To do so, you will have to create a schema.graphql file. This file has to be placed into the config folder of each API ./api/*/config/schema.graphql or plugin ./plugins/*/config/schema.graphql.

Structure — schema.graphql.

module.exports = {
+  definition: ``,
+  query: ``,
+  type: {},
+  resolver: {
+    Query: {}
+  }
+};
+
  • definition (string): let's you define new type, input, etc.
  • query (string): where you add custom query.
  • type (object): allows you to add description, deprecated field or disable the Shadow CRUD feature on a specific type.
  • resolver (object): +
    • Query (object): let's you define custom resolver, policies for a query.

Example

Let say we are using the same previous Post model.

Path — ./api/post/config/schema.graphql.

module.exports = {
+  definition: `
+    enum PostStatusInput {
+      draft
+      reviewing
+      reviewed
+      published
+      deleted
+    }
+  `,
+  query: `
+    postsByAuthor(id: String, status: PostStatusInput, limit: Int): [Post]!
+  `,
+  resolver: {
+    Query: {
+      post: {
+        description: 'Return a single post',
+        policy: ['plugins.users-permissions.isAuthenticated', 'isOwner'], // Apply the 'isAuthenticated' policy of the `Users & Permissions` plugin, then the 'isOwner' policy before executing the resolver.
+      },
+      posts: {
+        description: 'Return a list of posts', // Add a description to the query.
+        deprecated: 'This query should not be used anymore. Please consider using postsByAuthor instead.'
+      },
+      postsByAuthor: {
+        description: 'Return the posts published by the author',
+        resolver: 'Post.findByAuthor'
+      },
+      postsByTags: {
+        description: 'Return the posts published by the author',
+        resolverOf: 'Post.findByTags', // Will apply the same policy on the custom resolver than the controller's action `findByTags`.
+        resolver: (obj, options, ctx) => {
+          // ctx is the context of the Koa request.
+          await strapi.controllers.posts.findByTags(ctx);
+
+          return ctx.body.posts || `There is no post.`;
+        }
+      }
+    }
+  }
+};
+

Define a new type

Edit the definition attribute in one of the schema.graphql files of your project by using the GraphQL Type language string.

The easiest way is to create a new model using the CLI strapi generate:model category --api post, so you don't need to customise anything.

module.exports = {
+  definition: `
+    type Person {
+      id: Int!
+      firstname: String!
+      lastname: String!
+      age: Int
+      children: [Person]
+    }
+  `
+};
+

To explore the data of the new type Person, you need to define a query and associate a resolver to this query.

module.exports = {
+  definition: `
+    type Person {
+      id: Int!
+      firstname: String!
+      lastname: String!
+      age: Int
+      children: [Person]
+    }
+  `,
+  query: `
+    person(id: Int!): Person
+  `,
+  type: {
+    Person: {
+      _description: 'The Person type description', // Set the description for the type itself.
+      firstname: 'The firstname of the person',
+      lastname: 'The lastname of the person',
+      age: {
+        description: 'The age of the person',
+        deprecated: 'We are not using the age anymore, we can find it thanks to our powerful AI'
+      },
+      children: 'The children of the person'
+    }
+  }
+  resolver: {
+    Query: {
+      person: {
+        description: 'Return a single person',
+        resolver: 'Person.findOne' // It will use the action `findOne` located in the `Person.js` controller*.
+      }
+    }
+  }
+};
+

The resolver parameter also accepts an object as a value to target a controller located in a plugin.

module.exports = {
+  ...
+  resolver: {
+    Query: {
+      person: {
+        description: 'Return a single person',
+        resolver: {
+          plugin: 'users-permissions',
+          handler: 'User.findOne' // It will use the action `findOne` located in the `Person.js` controller inside the plugin `Users & Permissions`.
+        }
+      }
+    }
+  }
+};
+

Add description and deprecated reason

One of the most powerful features of GraphQL is the auto-documentation of the schema. The GraphQL plugin allows you to add a description to a type, a field and a query. You can also deprecate a field or a query.

Path — ./api/post/models/Post.settings.json.

{
+  "connection": "default",
+  "info": {
+    "description": "The Post type description"
+  },
+  "options": {
+    "timestamps": true
+  },
+  "attributes": {
+    "title": {
+      "type": "string",
+      "description": "The title of the post",
+      "deprecated": "We are not using the title anymore, it is auto-generated thanks to our powerful AI"
+    },
+    "content": {
+      "type": "text",
+      "description": "The content of the post."
+    },
+    "published": {
+      "type": "boolean",
+      "description": "Is the post published or not. Yes = true."
+    }
+  }
+}
+

It might happens that you want to add a description to a query or deprecate it. To do that, you need to use the schema.graphql file.

Remember: The schema.graphql file has to be placed into the config folder of each API ./api/*/config/schema.graphql or plugin ./plugins/*/config/schema.graphql.

Path — ./api/post/config/schema.graphql.

module.exports = {
+  resolver: {
+    Query: {
+      posts: {
+        description: 'Return a list of posts', // Add a description to the query.
+        deprecated: 'This query should not be used anymore. Please consider using postsByAuthor instead.' // Deprecate the query and explain the reason why.
+      }
+    }
+  }
+};
+

Execute a policy before a resolver

Sometimes a query needs to be only accessible to authenticated user. To handle this, Strapi provides a solid policy system. A policy is a function executed before the final action (the resolver). You can define an array of policy that will be executed in order.

module.exports = {
+  resolver: {
+    Query: {
+      posts: {
+        description: 'Return a list of posts',
+        policy: ['plugins.users-permissions.isAuthenticated', 'isOwner', 'global.logging']
+      }
+    }
+  }
+};
+

In this example, the policy isAuthenticated located in ./plugins/users-permissions/config/policies/isAuthenticated.js will be executed first. Then, the isOwner policy located in the Post API ./api/post/config/policies/isOwner.js. Next, it will execute the logging policy located in ./config/policies/logging.js. Finally, the resolver will be executed.

There is no custom resolver in that case, so it will execute the default resolver (Post.find) provided by the Shadow CRUD feature.

By default, the plugin will execute the actions located in the controllers that has been generated via the Content-Type Builder plugin or the CLI. For example, the query posts is going to execute the logic inside the find action in the Post.js controller. It might happens that you want to execute another action or a custom logic for one of your query.

module.exports = {
+  resolver: {
+    Query: {
+      posts: {
+        description: 'Return a list of posts by author',
+        resolver: 'Post.findByAuthor'
+      }
+    }
+  }
+};
+

In this example, it will execute the findByAuthor action of the Post controller. It also means that the resolver will apply on the posts query the permissions defined on the findByAuthor action (through the administration panel).

The obj parameter is available via ctx.params and the options are available via ctx.query in the controller's action.

Define a custom resolver

module.exports = {
+  resolver: {
+    Query: {
+      posts: {
+        description: 'Return a list of posts by author',
+        resolver: (obj, options, context) => {
+          // You can return a raw JSON object or a promise.
+
+          return [{
+            title: 'My first blog post',
+            content: 'Whatever you want...'
+          }];
+        }
+      }
+    }
+  }
+};
+

You can also execute a custom logic like above. However, the roles and permissions layers won't work.

Apply permissions on a query

It might happens that you want apply our permissions layer on a query. That's why, we created the resolverOf attribute. This attribute defines which are the permissions that should be applied to this resolver. By targeting an action it means that you're able to edit permissions for this resolver directly from the administration panel.

module.exports = {
+  resolver: {
+    Query: {
+      posts: {
+        description: 'Return a list of posts by author',
+        resolverOf: 'Post.find', // Will apply the same policy on the custom resolver than the controller's action `find` located in `Post.js`.
+        resolver: (obj, options, context) => {
+          // You can return a raw JSON object or a promise.
+
+          return [{
+            title: 'My first blog post',
+            content: 'Whatever you want...'
+          }];
+        }
+      }
+    }
+  }
+};
+

Disable a query or a type

To do that, we need to use the schema.graphql like below:

module.exports = {
+  type: {
+    Post: false // The Post type won't be "queriable".
+  }
+  resolver: {
+    Query: {
+      posts: false // The `posts` query will no longer be in the GraphQL schema.
+    }
+  }
+};
+

FAQ

How are the types name defined?

The type name is the global ID of the model. You can find the global ID of a model like that strapi.models[xxx].globalId or strapi.plugins[xxx].models[yyy].globalId.

Where should I put the field description and deprecated reason?

We recommend to put the field description and deprecated reason in the model. Right now, the GraphQL plugin is the only which uses these fields. Another plugin could use this description in the future as well. However, sometimes you don't have the choice, especially when you're defining a custom type.

It's not a bad practice to put the description and deprecated attribute in the schema.graphql, though.

Why are the "createdAt" and "updatedAt" field added to my type?

The plugin detects if the timestamps option is set to true in the model. By default, when you generate an API this option is checked. Set it to false in your model to remove these fields.

+ + + diff --git a/docs/.vuepress/dist/3.x.x/guides/i18n.html b/docs/.vuepress/dist/3.x.x/guides/i18n.html new file mode 100644 index 0000000000..3036998ebc --- /dev/null +++ b/docs/.vuepress/dist/3.x.x/guides/i18n.html @@ -0,0 +1,50 @@ + + + + + + Internationalization | Strapi Docs + + + + + + + +

Internationalization

See the internationalization' concepts for details.

Because an API may need to send different data based on the language of the user, Strapi provides a built-in strategy to handle the internationalization (i18n).

Usage

The i18n method that will allow you to retrieve the right string based on the language is accessible through the request's context.

There are many strategies to define the language that the server should use to return the correct translation. It can be based on the locale query parameter, the cookie or the Accept-Language header.

  • Query: Add the locale parameter in the URL GET /hello/John?locale=en_US.
  • Cookie: Set the locale field in the cookie locale=en\-US;.
  • Header: Set the Accept-Language header with the value en_US.

Please refer to the language configuration

Example

Let's say we want to say Hello John in english and Bonjour Tom in french. We need to use the built-in i18n feature and replace the string based on the received name.

Path — ./api/hello/config/routes.json.

{
+  "routes": [
+    {
+      "method": "GET",
+      "path": "/hello/:name",
+      "handler": "Hello.sayHello"
+    }
+  ]
+}
+

Path — ./api/hello/controllers/Hello.js.

module.exports = {
+  // GET /hello/:name
+  sayHello: async (ctx) => {
+    ctx.send(ctx.i18n.__('Hello %s', ctx.params.name));
+  }
+};
+

You need to define the english and french translation for this key.

Path — ./config/locales/en_US.json.

{
+  "Hello %s": "Hello %s"
+}
+

Path — ./config/locales/fr_FR.json.

{
+  "Hello %s": "Bonjour %s"
+}
+

That's all! The request GET /hello/John?locale=en_US will return Hello John and GET /hello/Tom?locale=fr_FR will return Bonjour Tom.

+ + + diff --git a/docs/.vuepress/dist/3.x.x/guides/models.html b/docs/.vuepress/dist/3.x.x/guides/models.html new file mode 100644 index 0000000000..cd36715488 --- /dev/null +++ b/docs/.vuepress/dist/3.x.x/guides/models.html @@ -0,0 +1,408 @@ + + + + + + Models | Strapi Docs + + + + + + + +

Models

See the models' concepts for details.

How to create a model?

If you are just starting out it is very convenient to generate some models with the Content Type Builder, directly in the admin interface. You can then review the generated model mappings on the code level. The UI takes over a lot of validation tasks and gives you a fast feeling for available features.

Use the CLI, and run the following command strapi generate:model user firstname:string lastname:string. Read the CLI documentation for more informations.

This will create two files located at ./api/user/models:

  • User.settings.json: contains the list of attributes and settings. The JSON format makes the file easily editable.
  • User.js: imports User.settings.json and extends it with additional settings and lifecycle callbacks.

when you create a new API using the CLI (strapi generate:api <name>), a model is automatically created.

Model Information

The info key on the model-json states information about the model. This information is used in the admin interface, when showing the model.

  • name: The name of the model, as shown in admin interface.
  • description: The description of the model.
  • mainField: Determines which model-attribute is shown when displaying the model.

Model options

The options key on the model-json states.

  • idAttribute: This tells the model which attribute to expect as the unique identifier for each database row (typically an auto-incrementing primary key named 'id').
  • idAttributeType: Data type of idAttribute, accepted list of value bellow:

Define the attributes

The following types are currently available:

  • string
  • text
  • integer
  • biginteger
  • float
  • decimal
  • password
  • date
  • time
  • datetime
  • timestamp
  • boolean
  • binary
  • uuid
  • enumeration
  • json
  • email

Validations

You can apply basic validations to the attributes. The following supported validations are only supported by MongoDB connection. +If you're using SQL databases, you should use the native SQL constraints to apply them.

  • required (boolean) — if true adds a required validator for this property.
  • unique (boolean) — whether to define a unique index on this property.
  • max (integer) — checks if the value is greater than or equal to the given minimum.
  • min (integer) — checks if the value is less than or equal to the given maximum.

Security validations +To improve the Developer eXperience when developing or using the administration panel, the framework enhances the attributes with these "security validations":

  • private (boolean) — if true, the attribute will be removed from the server response (it's useful to hide sensitive data).
  • configurable (boolean) - if false, the attribute isn't configurable from the Content Type Builder plugin.

Example

Path — User.settings.json.

{
+  "connection": "default",
+  "info": {
+    "name": "user",
+    "description": "This represents the User Model",
+    "mainField": "email"
+  },
+  "attributes": {
+    "firstname": {
+      "type": "string"
+    },
+    "lastname": {
+      "type": "string"
+    },
+    "email": {
+      "type": "email",
+      "required": true,
+      "unique": true
+    },
+    "password": {
+      "type": "password",
+      "required": true,
+      "private": true
+    },
+    "about": {
+      "type": "description"
+    },
+    "age": {
+      "type": "integer",
+      "min": 18,
+      "max": 99
+    },
+    "birthday": {
+      "type": "date"
+    }
+  }
+}
+

Relations

Refer to the relations concept for more informations about relations type.

One-way

Refer to the one-way concept for informations.

Example

A pet can be owned by someone (a user).

Path — ./api/pet/models/Pet.settings.json.

{
+  "attributes": {
+    "owner": {
+      "model": "user"
+    }
+  }
+}
+

Path — ./api/pet/controllers/Pet.js.

// Mongoose example
+module.exports = {
+  findPetsWithOwners: async (ctx) => {
+    // Retrieve the list of pets with their owners.
+    const pets = Pet
+      .find()
+      .populate('owner');
+
+    // Send the list of pets.
+    ctx.body = pets;
+  }
+}
+

One-to-one

Refer to the one-to-one concept for informations.

Example

A user can have one address. And this address is only related to this user.

Path — ./api/user/models/User.settings.json.

{
+  "attributes": {
+    "address": {
+      "model": "address",
+      "via": "user"
+    }
+  }
+}
+

Path — ./api/address/models/Address.settings.json.

{
+  "attributes": {
+    "user": {
+      "model": "user"
+    }
+  }
+}
+

Path — ./api/user/controllers/User.js.

// Mongoose example
+module.exports = {
+  findUsersWithAddresses: async (ctx) => {
+    // Retrieve the list of users with their addresses.
+    const users = User
+      .find()
+      .populate('address');
+
+    // Send the list of users.
+    ctx.body = users;
+  }
+}
+

Path — ./api/adress/controllers/Address.js.

// Mongoose example
+module.exports = {
+  findArticlesWithUsers: async (ctx) => {
+    // Retrieve the list of addresses with their users.
+    const articles = Address
+      .find()
+      .populate('user');
+
+    // Send the list of addresses.
+    ctx.body = addresses;
+  }
+}
+

One-to-many

Refer to the one-to-many concept for more informations.

Example

A user can have many articles, and an article can be related to one user (author).

Path — ./api/user/models/User.settings.json.

{
+  "attributes": {
+    "articles": {
+      "collection": "article",
+      "via": "author"
+    }
+  }
+}
+

Path — ./api/article/models/Article.settings.json.

{
+  "attributes": {
+    "author": {
+      "model": "user"
+    }
+  }
+}
+

Path — ./api/user/controllers/User.js.

// Mongoose example
+module.exports = {
+  findUsersWithArticles: async (ctx) => {
+    // Retrieve the list of users with their articles.
+    const users = User
+      .find()
+      .populate('articles');
+
+    // Send the list of users.
+    ctx.body = users;
+  }
+}
+

Path — ./api/article/controllers/Article.js.

// Mongoose example
+module.exports = {
+  findArticlesWithAuthors: async (ctx) => {
+    // Retrieve the list of articles with their authors.
+    const articles = Article
+      .find()
+      .populate('author');
+
+    // Send the list of users.
+    ctx.body = users;
+  }
+}
+

Many-to-many

Refer to the many-to-many concept.

Example

A product can be related to many categories, so a category can have many products.

Path — ./api/product/models/Product.settings.json.

{
+  "attributes": {
+    "categories": {
+      "collection": "category",
+      "via": "products",
+      "dominant": true
+    }
+  }
+}
+

The dominant key allows you to define in which table/collection (only for NoSQL databases) should be stored the array that defines the relationship. Because there is no join table in NoSQL, this key is required for NoSQL databases (ex: MongoDB).

Path — ./api/category/models/Category.settings.json.

{
+  "attributes": {
+    "products": {
+      "collection": "product",
+      "via": "categories"
+    }
+  }
+}
+

Path — ./api/product/controllers/Product.js.

// Mongoose example
+module.exports = {
+  findProductsWithCategories: async (ctx) => {
+    // Retrieve the list of products.
+    const products = Product
+      .find()
+      .populate('categories');
+
+    // Send the list of products.
+    ctx.body = products;
+  }
+}
+

Path — ./api/category/controllers/Category.js.

// Mongoose example
+module.exports = {
+  findCategoriesWithProducts: async (ctx) => {
+    // Retrieve the list of categories.
+    const categories = Category
+      .find()
+      .populate('products');
+
+    // Send the list of categories.
+    ctx.body = categories;
+  }
+}
+

Polymorphic

The polymorphic relationships are the solution when you don't know which kind of model will be associated to your entry. A common use case is an Image model that can be associated to many others kind of models (Article, Product, User, etc).

Refer to the upload plugin polymorphic implementation for more informations.

Single vs Many

Let's stay with our Image model which might belongs to a single Article or Product entry.

In other words, it means that a Image entry can be associated to one entry. This entry can be a Article or Product entry.

Path — ./api/image/models/Image.settings.json.

{
+  "attributes": {
+    "related": {
+      "model": "*",
+      "filter": "field"
+    }
+  }
+}
+

Also, our Image model which might belongs to many Article or Product entries.

In other words, it means that a Article entry can relate to the same image than a Product entry.

Path — ./api/image/models/Image.settings.json.

{
+  "attributes": {
+    "related": {
+      "collection": "*",
+      "filter": "field"
+    }
+  }
+}
+

Filter

The filter attribute is optional (but we highly recommend to use every time). If it's provided it adds a new match level to retrieve the related data.

For example, the Product model might have two attributes which are associated to the Image model. To distinguish which image is attached to the cover field and which images are attached to the pictures field, we need to save and provide this to the database.

Path — ./api/article/models/Product.settings.json.

{
+  "attributes": {
+    "cover": {
+      "model": "image",
+      "via": "related",
+    },
+    "pictures": {
+      "collection": "image",
+      "via": "related"
+    }
+  }
+}
+

The value is the filter attribute is the name of the column where the information is stored.

Example

A Image model might belongs to many either Article models or a Product models.

Path — ./api/image/models/Image.settings.json.

{
+  "attributes": {
+    "related": {
+      "collection": "*",
+      "filter": "field"
+    }
+  }
+}
+

Path — ./api/article/models/Article.settings.json.

{
+  "attributes": {
+    "avatar": {
+      "model": "image",
+      "via": "related"
+    }
+  }
+}
+

Path — ./api/article/models/Product.settings.json.

{
+  "attributes": {
+    "pictures": {
+      "collection": "image",
+      "via": "related"
+    }
+  }
+}
+

Path — ./api/image/controllers/Image.js.

// Mongoose example
+module.exports = {
+  findFiles: async (ctx) => {
+    // Retrieve the list of images with the Article or Product entries related to them.
+    const images = Images
+      .find()
+      .populate('related');
+
+    /*
+    [{
+      "_id": "5a81b0fa8c063a53298a934a",
+      "url": "http://....",
+      "name": "john_doe_avatar.png",
+      "related": [{
+        "_id": "5a81b0fa8c063a5393qj934a",
+        "title": "John Doe is awesome",
+        "description": "..."
+      }, {
+        "_id": "5a81jei389ns5abd75f79c",
+        "name": "A simple chair",
+        "description": "..."
+      }]
+    }]
+    */
+
+    // Send the list of files.
+    ctx.body = images;
+  }
+}
+

Path — ./api/article/controllers/Article.js.

// Mongoose example
+module.exports = {
+  findArticlesWithAvatar: async (ctx) => {
+    // Retrieve the list of articles with the avatar (image).
+    const articles = Article
+      .find()
+      .populate('avatar');
+
+    /*
+    [{
+      "_id": "5a81b0fa8c063a5393qj934a",
+      "title": "John Doe is awesome",
+      "description": "...",
+      "avatar": {
+        "_id": "5a81b0fa8c063a53298a934a",
+        "url": "http://....",
+        "name": "john_doe_avatar.png"
+      }
+    }]
+    */
+
+    // Send the list of users.
+    ctx.body = articles;
+  }
+}
+

Path — ./api/product/controllers/Product.js.

// Mongoose example
+module.exports = {
+  findProductWithPictures: async (ctx) => {
+    // Retrieve the list of products with the pictures (images).
+    const products = Product
+      .find()
+      .populate('pictures');
+
+    /*
+    [{
+      "_id": "5a81jei389ns5abd75f79c",
+      "name": "A simple chair",
+      "description": "...",
+      "pictures": [{
+        "_id": "5a81b0fa8c063a53298a934a",
+        "url": "http://....",
+        "name": "chair_position_1.png"
+      }, {
+        "_id": "5a81d22bee1ad45abd75f79c",
+        "url": "http://....",
+        "name": "chair_position_2.png"
+      }, {
+        "_id": "5a81d232ee1ad45abd75f79e",
+        "url": "http://....",
+        "name": "chair_position_3.png"
+      }]
+    }]
+    */
+
+    // Send the list of users.
+    ctx.body = products;
+  }
+}
+

Database implementation

If you're using MongoDB as a database, you don't need to do anything. Everything is natively handled by Strapi. However, to implement a polymorphic relationship with SQL databases, you need to create two tables.

Path — ./api/image/models/Image.settings.json.

{
+  "attributes": {
+    "name": {
+      "type": "string"
+    },
+    "url": {
+      "type": "string"
+    },
+    "related": {
+      "collection": "*",
+      "filter": "field"
+    }
+  }
+}
+

The first table to create is the table which has the same name as your model.

CREATE TABLE `image` (
+  `id` int(11) NOT NULL,
+  `name` text NOT NULL,
+  `text` text NOT NULL
+)
+

If you've overrided the default table name given by Strapi by using the collectionName attribute. Use the value set in the collectionName to name the table.

The second table will allow us to associate one or many others entries to the Image model. The name of the table is the same as the previous one with the suffix _morph.

CREATE TABLE `image_morph` (
+  `id` int(11) NOT NULL,
+  `image_id` int(11) NOT NULL,
+  `related_id` int(11) NOT NULL,
+  `related_type` text NOT NULL,
+  `field` text NOT NULL
+)
+
  • image_id is using the name of the first table with the suffix _id. +
    • Attempted value: It correspond to the id of an Image entry.
  • related_id is using the attribute name where the relation happens with the suffix _id. +
    • Attempted value: It correspond to the id of an Article or Product entry.
  • related_type is using the attribute name where the relation happens with the suffix _type. +
    • Attempted value: It correspond to the table name where the Article or Product entry is stored.
  • field is using the filter property value defined in the model. If you change the filter value, you have to change the name of this column as well. +
    • Attempted value: It correspond to the attribute of a Article, Product with which the Image entry is related.
id image_id related_id related_type field
1 1738 39 product cover
2 4738 58 article avatar
3 1738 71 article avatar

Lifecycle callbacks

Refer to the lifecycle callbacks concepts for informations.

The following events are available by default:

Callbacks on save:

  • beforeSave
  • afterSave

Callbacks on fetch:

  • beforeFetch
  • afterFetch

Callbacks on fetchAll:

  • beforeFetchAll
  • afterFetchAll

Callbacks on create:

  • beforeCreate
  • afterCreate

Callbacks on update:

  • beforeUpdate
  • afterUpdate

Callbacks on destroy:

  • beforeDestroy
  • afterDestroy

Mongoose

The entry is available through the model parameter

Path — ./api/user/models/User.js.

module.exports = {
+ /**
+  * Triggered before user creation.
+  */
+ beforeCreate: async (model) => {
+   // Hash password.
+   const passwordHashed = await strapi.api.user.services.user.hashPassword(model.password);
+
+   // Set the password.
+   model.password = passwordHashed;
+ }
+}
+

Bookshelf

Each of these functions receives a three parameters model, attrs and options. You have to return a Promise.

Path — ./api/user/models/User.js.

module.exports = {
+
+  /**
+   * Triggered before user creation.
+   */
+  beforeCreate: async (model, attrs, options) => {
+    // Hash password.
+    const passwordHashed = await strapi.api.user.services.user.hashPassword(model.attributes.password);
+
+    // Set the password.
+    model.set('password', passwordHashed);
+  }
+}
+

Settings

Additional settings can be set on models:

  • connection (string) - Connection's name which must be used. Default value: default.
  • collectionName (string) - Collection's name (or table's name) in which the data should be stored.
  • globalId (string) -Global variable name for this model (case-sensitive).

Path — User.settings.json.

{
+  "connection": "mongo",
+  "collectionName": "Users_v1",
+  "globalId": "Users",
+  "attributes": {
+
+  }
+}
+

In this example, the model User will be accessible through the Users global variable. The data will be stored in the Users_v1 collection or table and the model will use the mongo connection defined in ./config/environments/**/database.json

The connection value can be changed whenever you want, but you should be aware that there is no automatic data migration process. Also if the new connection doesn't use the same ORM you will have to rewrite your queries.

+ + + diff --git a/docs/.vuepress/dist/3.x.x/guides/policies.html b/docs/.vuepress/dist/3.x.x/guides/policies.html new file mode 100644 index 0000000000..98ef634a91 --- /dev/null +++ b/docs/.vuepress/dist/3.x.x/guides/policies.html @@ -0,0 +1,97 @@ + + + + + + Policies | Strapi Docs + + + + + + + +

Policies

See the policies' concepts for details.

How to create a policy?

There are several ways to create a policy.

  • Using the CLI strapi generate:policy isAuthenticated. Read the CLI documentation for more information.
  • Manually create a JavaScript file named isAuthenticated.js in ./config/policies/.

Path — ./config/policies/isAuthenticated.js.

module.exports = async (ctx, next) => {
+  if (ctx.state.user) {
+    // Go to next policy or will reach the controller's action.
+    return await next();
+  }
+
+  ctx.unauthorized(`You're not logged in!`);
+};
+

In this example, we are verifying that a session is open. If it is the case, we call the next() method that will execute the next policy or controller's action. Otherwise, a 401 error is returned.

You can access to any controllers, services or models thanks to the global variable strapi in a policy.

Usage

To apply policies to a route, you need to associate an array of policies to it. As explained in the policies' concepts, there are two kinds of policies: global or scoped.

Global policies

Refer to the concept for details.

The global policies can be associated to any routes in your project.

Path — ./api/car/routes.json.

{
+  "routes": [
+    {
+      "method": "GET",
+      "path": "/car",
+      "handler": "Car.find",
+      "config": {
+        "policies": [
+          "global.isAuthenticated"
+        ]
+      }
+    }
+  ]
+}
+

Before executing the find action in the Car.js controller, the global policy isAuthenticated located in ./config/policies/isAuthenticated.js will be called.

You can put as much policy you want in this array. However be careful about the performance impact.

Plugins policies

Plugins can add and expose policies into your app. For example, the plugin Auth (COMING SOON) comes with several useful policies to ensure that the user is well authenticated or has the rights to perform an action.

Path — ./api/car/config/routes.json.

{
+  "routes": [
+    {
+      "method": "GET",
+      "path": "/car",
+      "handler": "Car.find",
+      "config": {
+        "policies": [
+          "plugins.auth.isAuthenticated"
+        ]
+      }
+    }
+  ]
+}
+

The policy isAuthenticated located in ./plugins/auth/config/policies/isAuthenticated.js will be executed before the find action in the Car.js controller.

Scoped Policies

The scoped policies can only be associated to the routes defined in the API where they have been declared.

Path — ./api/car/config/policies/isAdmin.js.

module.exports = async (ctx, next) => {
+  if (ctx.state.user.role.name === 'Administrator') {
+    // Go to next policy or will reach the controller's action.
+    return await next();
+  }
+
+  ctx.unauthorized(`You're not allowed to perform this action!`);
+};
+

Path — ./api/car/config/routes.json.

{
+  "routes": [
+    {
+      "method": "GET",
+      "path": "/car",
+      "handler": "Car.find",
+      "config": {
+        "policies": [
+          "isAdmin"
+        ]
+      }
+    }
+  ]
+}
+

The policy isAdmin located in ./api/car/config/policies/isAdmin.js will be executed before the find action in the Car.js controller.

The policy isAdmin can only be applied to the routes defined in the /api/car folder.

Advanced usage

As it's explained above, the policies are executed before the controller's action. It looks like an action that you can make before the controller's action. You can also execute a logic after.

Path — ./config/policies/custom404.js.

module.exports = async (ctx, next) => {
+  // Indicate to the server to go to
+  // the next policy or to the controller's action.
+  await next();
+
+  // The code below will be executed after the controller's action.
+  if (ctx.status === 404) {
+    ctx.body = 'We cannot find the ressource.';
+  }
+};
+
+ + + diff --git a/docs/.vuepress/dist/3.x.x/guides/public-assets.html b/docs/.vuepress/dist/3.x.x/guides/public-assets.html new file mode 100644 index 0000000000..8029752ab9 --- /dev/null +++ b/docs/.vuepress/dist/3.x.x/guides/public-assets.html @@ -0,0 +1,29 @@ + + + + + + Public Assets | Strapi Docs + + + + + + + +

Public Assets

See the public assets concepts for details.

Because an API may need to serve static assets, every new Strapi project includes by default, a folder named /public. Any file located in this directory is accessible if the request's path doesn't match any other defined route and if it matches a public file name.

Example

An image named company-logo.png in ./public/ is accessible through /company-logo.png URL.

index.html files are served if the request corresponds to a folder name (/pictures url will try to serve public/pictures/index.html file).

WARNING

The dotfiles are not exposed. It means that every files with the names start by . such as .htaccess or .gitignore are not served.

Refer to the public assets configurations for more informations.

+ + + diff --git a/docs/.vuepress/dist/3.x.x/guides/requests.html b/docs/.vuepress/dist/3.x.x/guides/requests.html new file mode 100644 index 0000000000..9fb485a8f9 --- /dev/null +++ b/docs/.vuepress/dist/3.x.x/guides/requests.html @@ -0,0 +1,164 @@ + + + + + + Request | Strapi Docs + + + + + + + +

Request

See the requests concepts for details.

The context object (ctx) contains all the requests related informations. They are accessible through ctx.request, from controllers and policies.

API Reference

For more information, please refer to the Koa request documentation.

request.header

Request header object.

request.header=

Set request header object.

request.headers

Request header object. Alias as request.header.

request.headers=

Set request header object. Alias as request.header=.

request.method

Request method.

request.method=

Set request method, useful for implementing middleware +such as methodOverride().

request.length

Return request Content-Length as a number when present, or undefined.

request.url

Get request URL.

request.url=

Set request URL, useful for url rewrites.

request.originalUrl

Get request original URL.

request.origin

Get origin of URL, include protocol and host.

ctx.request.origin
+// => http://example.com
+

request.href

Get full request URL, include protocol, host and url.

ctx.request.href;
+// => http://example.com/foo/bar?q=1
+

request.path

Get request pathname.

request.path=

Set request pathname and retain query-string when present.

request.querystring

Get raw query string void of ?.

request.querystring=

Set raw query string.

Get raw query string with the ?.

request.search=

Set raw query string.

request.host

Get host (hostname:port) when present. Supports X-Forwarded-Host +when app.proxy is true, otherwise Host is used.

request.hostname

Get hostname when present. Supports X-Forwarded-Host +when app.proxy is true, otherwise Host is used.

If host is IPv6, Koa delegates parsing to +WHATWG URL API, +Note This may impact performance.

request.URL

Get WHATWG parsed URL object.

request.type

Get request Content-Type void of parameters such as "charset".

const ct = ctx.request.type;
+// => "image/png"
+

request.charset

Get request charset when present, or undefined:

ctx.request.charset;
+// => "utf-8"
+

request.query

Get parsed query-string, returning an empty object when no +query-string is present. Note that this getter does not +support nested parsing.

For example "color=blue&size=small":

{
+  color: 'blue',
+  size: 'small'
+}
+

request.query=

Set query-string to the given object. Note that this +setter does not support nested objects.

ctx.query = { next: '/login' };
+

request.fresh

Check if a request cache is "fresh", aka the contents have not changed. This +method is for cache negotiation between If-None-Match / ETag, and If-Modified-Since and Last-Modified. It should be referenced after setting one or more of these response headers.

// freshness check requires status 20x or 304
+ctx.status = 200;
+ctx.set('ETag', '123');
+
+// cache is ok
+if (ctx.fresh) {
+  ctx.status = 304;
+  return;
+}
+
+// cache is stale
+// fetch new data
+ctx.body = await db.find('something');
+

request.stale

Inverse of request.fresh.

request.protocol

Return request protocol, "https" or "http". Supports X-Forwarded-Proto +when app.proxy is true.

request.secure

Shorthand for ctx.protocol == "https" to check if a request was +issued via TLS.

request.ip

Request remote address. Supports X-Forwarded-For when app.proxy +is true.

request.ips

When X-Forwarded-For is present and app.proxy is enabled an array +of these ips is returned, ordered from upstream -> downstream. When disabled +an empty array is returned.

request.subdomains

Return subdomains as an array.

Subdomains are the dot-separated parts of the host before the main domain of +the app. By default, the domain of the app is assumed to be the last two +parts of the host. This can be changed by setting app.subdomainOffset.

For example, if the domain is "tobi.ferrets.example.com": +If app.subdomainOffset is not set, ctx.subdomains is ["ferrets", "tobi"]. +If app.subdomainOffset is 3, ctx.subdomains is ["tobi"].

request.is(types...)

Check if the incoming request contains the "Content-Type" +header field, and it contains any of the give mime types. +If there is no request body, null is returned. +If there is no content type, or the match fails false is returned. +Otherwise, it returns the matching content-type.

// With Content-Type: text/html; charset=utf-8
+ctx.is('html'); // => 'html'
+ctx.is('text/html'); // => 'text/html'
+ctx.is('text/*', 'text/html'); // => 'text/html'
+
+// When Content-Type is application/json
+ctx.is('json', 'urlencoded'); // => 'json'
+ctx.is('application/json'); // => 'application/json'
+ctx.is('html', 'application/*'); // => 'application/json'
+
+ctx.is('html'); // => false
+

For example if you want to ensure that +only images are sent to a given route:

if (ctx.is('image/*')) {
+// process
+} else {
+  ctx.throw(415, 'images only!');
+}
+

Content Negotiation

Koa's request object includes helpful content negotiation utilities powered by accepts and negotiator. These utilities are:

  • request.accepts(types)
  • request.acceptsEncodings(types)
  • request.acceptsCharsets(charsets)
  • request.acceptsLanguages(langs)

If no types are supplied, all acceptable types are returned.

If multiple types are supplied, the best match will be returned. If no matches are found, a false is returned, and you should send a 406 "Not Acceptable" response to the client.

In the case of missing accept headers where any type is acceptable, the first type will be returned. Thus, the order of types you supply is important.

request.accepts(types)

Check if the given type(s) is acceptable, returning the best match when true, otherwise false. The type value may be one or more mime type string +such as "application/json", the extension name +such as "json", or an array ["json", "html", "text/plain"].

// Accept: text/html
+ctx.accepts('html');
+// => "html"
+
+// Accept: text/*, application/json
+ctx.accepts('html');
+// => "html"
+ctx.accepts('text/html');
+// => "text/html"
+ctx.accepts('json', 'text');
+// => "json"
+ctx.accepts('application/json');
+// => "application/json"
+
+// Accept: text/*, application/json
+ctx.accepts('image/png');
+ctx.accepts('png');
+// => false
+
+// Accept: text/*;q=.5, application/json
+ctx.accepts(['html', 'json']);
+ctx.accepts('html', 'json');
+// => "json"
+
+// No Accept header
+ctx.accepts('html', 'json');
+// => "html"
+ctx.accepts('json', 'html');
+// => "json"
+

You may call ctx.accepts() as many times as you like, +or use a switch:

switch (ctx.accepts('json', 'html', 'text')) {
+  case 'json': break;
+  case 'html': break;
+  case 'text': break;
+  default: ctx.throw(406, 'json, html, or text only');
+}
+

request.acceptsEncodings(encodings)

Check if encodings are acceptable, returning the best match when true, otherwise false. Note that you should include identity as one of the encodings!

// Accept-Encoding: gzip
+ctx.acceptsEncodings('gzip', 'deflate', 'identity');
+// => "gzip"
+
+ctx.acceptsEncodings(['gzip', 'deflate', 'identity']);
+// => "gzip"
+

When no arguments are given all accepted encodings +are returned as an array:

// Accept-Encoding: gzip, deflate
+ctx.acceptsEncodings();
+// => ["gzip", "deflate", "identity"]
+

Note that the identity encoding (which means no encoding) could be unacceptable if the client explicitly sends identity;q=0. Although this is an edge case, you should still handle the case where this method returns false.

request.acceptsCharsets(charsets)

Check if charsets are acceptable, returning +the best match when true, otherwise false.

// Accept-Charset: utf-8, iso-8859-1;q=0.2, utf-7;q=0.5
+ctx.acceptsCharsets('utf-8', 'utf-7');
+// => "utf-8"
+
+ctx.acceptsCharsets(['utf-7', 'utf-8']);
+// => "utf-8"
+

When no arguments are given all accepted charsets +are returned as an array:

// Accept-Charset: utf-8, iso-8859-1;q=0.2, utf-7;q=0.5
+ctx.acceptsCharsets();
+// => ["utf-8", "utf-7", "iso-8859-1"]
+

request.acceptsLanguages(langs)

Check if langs are acceptable, returning +the best match when true, otherwise false.

// Accept-Language: en;q=0.8, es, pt
+ctx.acceptsLanguages('es', 'en');
+// => "es"
+
+ctx.acceptsLanguages(['en', 'es']);
+// => "es"
+

When no arguments are given all accepted languages +are returned as an array:

// Accept-Language: en;q=0.8, es, pt
+ctx.acceptsLanguages();
+// => ["es", "pt", "en"]
+

request.idempotent

Check if the request is idempotent.

request.socket

Return the request socket.

request.get(field)

Return request header.

+ + + diff --git a/docs/.vuepress/dist/3.x.x/guides/responses.html b/docs/.vuepress/dist/3.x.x/guides/responses.html new file mode 100644 index 0000000000..23997ca95b --- /dev/null +++ b/docs/.vuepress/dist/3.x.x/guides/responses.html @@ -0,0 +1,365 @@ + + + + + + Responses | Strapi Docs + + + + + + + +

Responses

See the responses concepts for details.

API Reference

For more information, please refer to the Koa response documentation.

Context Response

The context object (ctx) contains a list of values and functions useful to manage server responses. They are accessible through ctx.response, from controllers and policies.

response.header

Response header object.

response.headers

Response header object. Alias as response.header.

response.socket

Request socket.

response.status

Get response status. By default, response.status is set to 404 unlike node's res.statusCode which defaults to 200.

response.status=

Set response status via numeric code:

  • 100 "continue"
  • 101 "switching protocols"
  • 102 "processing"
  • 200 "ok"
  • 201 "created"
  • 202 "accepted"
  • 203 "non-authoritative information"
  • 204 "no content"
  • 205 "reset content"
  • 206 "partial content"
  • 207 "multi-status"
  • 208 "already reported"
  • 226 "im used"
  • 300 "multiple choices"
  • 301 "moved permanently"
  • 302 "found"
  • 303 "see other"
  • 304 "not modified"
  • 305 "use proxy"
  • 307 "temporary redirect"
  • 308 "permanent redirect"
  • 400 "bad request"
  • 401 "unauthorized"
  • 402 "payment required"
  • 403 "forbidden"
  • 404 "not found"
  • 405 "method not allowed"
  • 406 "not acceptable"
  • 407 "proxy authentication required"
  • 408 "request timeout"
  • 409 "conflict"
  • 410 "gone"
  • 411 "length required"
  • 412 "precondition failed"
  • 413 "payload too large"
  • 414 "uri too long"
  • 415 "unsupported media type"
  • 416 "range not satisfiable"
  • 417 "expectation failed"
  • 418 "I'm a teapot"
  • 422 "unprocessable entity"
  • 423 "locked"
  • 424 "failed dependency"
  • 426 "upgrade required"
  • 428 "precondition required"
  • 429 "too many requests"
  • 431 "request header fields too large"
  • 500 "internal server error"
  • 501 "not implemented"
  • 502 "bad gateway"
  • 503 "service unavailable"
  • 504 "gateway timeout"
  • 505 "http version not supported"
  • 506 "variant also negotiates"
  • 507 "insufficient storage"
  • 508 "loop detected"
  • 510 "not extended"
  • 511 "network authentication required"

NOTE: don't worry too much about memorizing these strings, +if you have a typo an error will be thrown, displaying this list +so you can make a correction.

response.message

Get response status message. By default, response.message is +associated with response.status.

response.message=

Set response status message to the given value.

response.length=

Set response Content-Length to the given value.

response.length

Return response Content-Length as a number when present, or deduce +from ctx.body when possible, or undefined.

response.body

Get response body.

response.body=

Set response body to one of the following:

  • string written
  • Buffer written
  • Stream piped
  • Object || Array json-stringified
  • null no content response

If response.status has not been set, Koa will automatically set the status to 200 or 204.

String

The Content-Type is defaulted to text/html or text/plain, both with +a default charset of utf-8. The Content-Length field is also set.

Buffer

The Content-Type is defaulted to application/octet-stream, and Content-Length +is also set.

Stream

The Content-Type is defaulted to application/octet-stream.

Whenever a stream is set as the response body, .onerror is automatically added as a listener to the error event to catch any errors. +In addition, whenever the request is closed (even prematurely), the stream is destroyed. +If you do not want these two features, do not set the stream as the body directly. +For example, you may not want this when setting the body as an HTTP stream in a proxy as it would destroy the underlying connection.

See: https://github.com/koajs/koa/pull/612 for more information.

Here's an example of stream error handling without automatically destroying the stream:

const PassThrough = require('stream').PassThrough;
+
+app.use(async ctx => {
+  ctx.body = someHTTPStream.on('error', ctx.onerror).pipe(PassThrough());
+});
+
Object

The Content-Type is defaulted to application/json. This includes plain objects { foo: 'bar' } and arrays ['foo', 'bar'].

response.get(field)

Get a response header field value with case-insensitive field.

const etag = ctx.response.get('ETag');
+

response.set(field, value)

Set response header field to value:

ctx.set('Cache-Control', 'no-cache');
+

response.append(field, value)

Append additional header field with value val.

ctx.append('Link', '<http://127.0.0.1/>');
+

response.set(fields)

Set several response header fields with an object:

ctx.set({
+  'Etag': '1234',
+  'Last-Modified': date
+});
+

response.remove(field)

Remove header field.

response.type

Get response Content-Type void of parameters such as "charset".

const ct = ctx.type;
+// => "image/png"
+

response.type=

Set response Content-Type via mime string or file extension.

ctx.type = 'text/plain; charset=utf-8';
+ctx.type = 'image/png';
+ctx.type = '.png';
+ctx.type = 'png';
+

when appropriate a charset is selected for you, for +example response.type = 'html' will default to "utf-8". If you need to overwrite charset, +use ctx.set('Content-Type', 'text/html') to set response header field to value directly.

response.is(types...)

Very similar to ctx.request.is(). +Check whether the response type is one of the supplied types. +This is particularly useful for creating middleware that +manipulate responses.

For example, this is a middleware that minifies +all HTML responses except for streams.

const minify = require('html-minifier');
+
+app.use(async (ctx, next) => {
+await next();
+
+if (!ctx.response.is('html')) return;
+
+let body = ctx.body;
+if (!body || body.pipe) return;
+
+if (Buffer.isBuffer(body)) body = body.toString();
+  ctx.body = minify(body);
+});
+

response.redirect(url, [alt])

Perform a [302] redirect to url.

The string "back" is special-cased +to provide Referrer support, when Referrer +is not present alt or "/" is used.

ctx.redirect('back');
+ctx.redirect('back', '/index.html');
+ctx.redirect('/login');
+ctx.redirect('http://google.com');
+

To alter the default status of 302, simply assign the status +before or after this call. To alter the body, assign it after this call:

ctx.status = 301;
+ctx.redirect('/cart');
+ctx.body = 'Redirecting to shopping cart';
+

response.attachment([filename])

Set Content-Disposition to "attachment" to signal the client +to prompt for download. Optionally specify the filename of the +download.

response.headerSent

Check if a response header has already been sent. Useful for seeing +if the client may be notified on error.

response.lastModified

Return the Last-Modified header as a Date, if it exists.

response.lastModified=

Set the Last-Modified header as an appropriate UTC string. +You can either set it as a Date or date string.

ctx.response.lastModified = new Date();
+

response.etag=

Set the ETag of a response including the wrapped "s. +Note that there is no corresponding response.etag getter.

ctx.response.etag = crypto.createHash('md5').update(ctx.body).digest('hex');
+

response.vary(field)

Vary on field.

response.flushHeaders()

Flush any set headers, and begin the body.

Response

A Koa Response object is an abstraction on top of node's vanilla response object, +providing additional functionality that is useful for every day HTTP server +development.

API

response.header

Response header object.

response.headers

Response header object. Alias as response.header.

response.socket

Request socket.

response.status

Get response status. By default, response.status is set to 404 unlike node's res.statusCode which defaults to 200.

response.status=

Set response status via numeric code:

  • 100 "continue"
  • 101 "switching protocols"
  • 102 "processing"
  • 200 "ok"
  • 201 "created"
  • 202 "accepted"
  • 203 "non-authoritative information"
  • 204 "no content"
  • 205 "reset content"
  • 206 "partial content"
  • 207 "multi-status"
  • 208 "already reported"
  • 226 "im used"
  • 300 "multiple choices"
  • 301 "moved permanently"
  • 302 "found"
  • 303 "see other"
  • 304 "not modified"
  • 305 "use proxy"
  • 307 "temporary redirect"
  • 308 "permanent redirect"
  • 400 "bad request"
  • 401 "unauthorized"
  • 402 "payment required"
  • 403 "forbidden"
  • 404 "not found"
  • 405 "method not allowed"
  • 406 "not acceptable"
  • 407 "proxy authentication required"
  • 408 "request timeout"
  • 409 "conflict"
  • 410 "gone"
  • 411 "length required"
  • 412 "precondition failed"
  • 413 "payload too large"
  • 414 "uri too long"
  • 415 "unsupported media type"
  • 416 "range not satisfiable"
  • 417 "expectation failed"
  • 418 "I'm a teapot"
  • 422 "unprocessable entity"
  • 423 "locked"
  • 424 "failed dependency"
  • 426 "upgrade required"
  • 428 "precondition required"
  • 429 "too many requests"
  • 431 "request header fields too large"
  • 500 "internal server error"
  • 501 "not implemented"
  • 502 "bad gateway"
  • 503 "service unavailable"
  • 504 "gateway timeout"
  • 505 "http version not supported"
  • 506 "variant also negotiates"
  • 507 "insufficient storage"
  • 508 "loop detected"
  • 510 "not extended"
  • 511 "network authentication required"

NOTE: don't worry too much about memorizing these strings, +if you have a typo an error will be thrown, displaying this list +so you can make a correction.

response.message

Get response status message. By default, response.message is +associated with response.status.

response.message=

Set response status message to the given value.

response.length=

Set response Content-Length to the given value.

response.length

Return response Content-Length as a number when present, or deduce +from ctx.body when possible, or undefined.

response.body

Get response body.

response.body=

Set response body to one of the following:

  • string written
  • Buffer written
  • Stream piped
  • Object || Array json-stringified
  • null no content response

If response.status has not been set, Koa will automatically set the status to 200 or 204.

String

The Content-Type is defaulted to text/html or text/plain, both with +a default charset of utf-8. The Content-Length field is also set.

Buffer

The Content-Type is defaulted to application/octet-stream, and Content-Length +is also set.

Stream

The Content-Type is defaulted to application/octet-stream.

Whenever a stream is set as the response body, .onerror is automatically added as a listener to the error event to catch any errors. +In addition, whenever the request is closed (even prematurely), the stream is destroyed. +If you do not want these two features, do not set the stream as the body directly. +For example, you may not want this when setting the body as an HTTP stream in a proxy as it would destroy the underlying connection.

See: https://github.com/koajs/koa/pull/612 for more information.

Here's an example of stream error handling without automatically destroying the stream:

const PassThrough = require('stream').PassThrough;
+
+app.use(async ctx => {
+  ctx.body = someHTTPStream.on('error', ctx.onerror).pipe(PassThrough());
+});
+
Object

The Content-Type is defaulted to application/json. This includes plain objects { foo: 'bar' } and arrays ['foo', 'bar'].

response.get(field)

Get a response header field value with case-insensitive field.

const etag = ctx.response.get('ETag');
+

response.set(field, value)

Set response header field to value:

ctx.set('Cache-Control', 'no-cache');
+

response.append(field, value)

Append additional header field with value val.

ctx.append('Link', '<http://127.0.0.1/>');
+

response.set(fields)

Set several response header fields with an object:

ctx.set({
+  'Etag': '1234',
+  'Last-Modified': date
+});
+

response.remove(field)

Remove header field.

response.type

Get response Content-Type void of parameters such as "charset".

const ct = ctx.type;
+// => "image/png"
+

response.type=

Set response Content-Type via mime string or file extension.

ctx.type = 'text/plain; charset=utf-8';
+ctx.type = 'image/png';
+ctx.type = '.png';
+ctx.type = 'png';
+

when appropriate a charset is selected for you, for +example response.type = 'html' will default to "utf-8". If you need to overwrite charset, +use ctx.set('Content-Type', 'text/html') to set response header field to value directly.

response.is(types...)

Very similar to ctx.request.is(). +Check whether the response type is one of the supplied types. +This is particularly useful for creating middleware that +manipulate responses.

For example, this is a middleware that minifies +all HTML responses except for streams.

const minify = require('html-minifier');
+
+app.use(async (ctx, next) => {
+await next();
+
+if (!ctx.response.is('html')) return;
+
+let body = ctx.body;
+if (!body || body.pipe) return;
+
+if (Buffer.isBuffer(body)) body = body.toString();
+  ctx.body = minify(body);
+});
+

response.redirect(url, [alt])

Perform a [302] redirect to url.

The string "back" is special-cased +to provide Referrer support, when Referrer +is not present alt or "/" is used.

ctx.redirect('back');
+ctx.redirect('back', '/index.html');
+ctx.redirect('/login');
+ctx.redirect('http://google.com');
+

To alter the default status of 302, simply assign the status +before or after this call. To alter the body, assign it after this call:

ctx.status = 301;
+ctx.redirect('/cart');
+ctx.body = 'Redirecting to shopping cart';
+

response.attachment([filename])

Set Content-Disposition to "attachment" to signal the client +to prompt for download. Optionally specify the filename of the +download.

response.headerSent

Check if a response header has already been sent. Useful for seeing +if the client may be notified on error.

response.lastModified

Return the Last-Modified header as a Date, if it exists.

response.lastModified=

Set the Last-Modified header as an appropriate UTC string. +You can either set it as a Date or date string.

ctx.response.lastModified = new Date();
+

response.etag=

Set the ETag of a response including the wrapped "s. +Note that there is no corresponding response.etag getter.

ctx.response.etag = crypto.createHash('md5').update(ctx.body).digest('hex');
+

response.vary(field)

Vary on field.

response.flushHeaders()

Flush any set headers, and begin the body.

Advanced responses

Strapi integrates Boom: a set of utilities for returning HTTP errors. Every Boom’s functions are accessible through the ctx.response.

You can also override responses based on them status. Please read the configuration responses for that.

Every Boom's functions is delegated to the context. It means that ctx.notFound is a shortcut to ctx.response.notFound.

API Reference

For more information, please refer to the Boom documentation.

HTTP 4xx Errors

ctx.response.badRequest([message], [data])

Returns a 400 Bad Request error where:

  • message - optional message.
  • data - optional additional error data.
ctx.response.badRequest('invalid query');
+

Generates the following response payload:

{
+  "statusCode": 400,
+  "error": "Bad Request",
+  "message": "invalid query"
+}
+

ctx.response.unauthorized([message], [scheme], [attributes])

Returns a 401 Unauthorized error where:

  • message - optional message.
  • scheme can be one of the following: +
    • an authentication scheme name
    • an array of string values. These values will be separated by ', ' and set to the 'WWW-Authenticate' header.
  • attributes - an object of values to use while setting the 'WWW-Authenticate' header. This value is only used +when scheme is a string, otherwise it is ignored. Every key/value pair will be included in the +'WWW-Authenticate' in the format of 'key="value"' as well as in the response payload under the attributes key. Alternatively value can be a string which is use to set the value of the scheme, for example setting the token value for negotiate header. If string is used message parameter must be null. +null and undefined will be replaced with an empty string. If attributes is set, message will be used as +the 'error' segment of the 'WWW-Authenticate' header. If message is unset, the 'error' segment of the header +will not be present and isMissing will be true on the error object.

If either scheme or attributes are set, the resultant Boom object will have the 'WWW-Authenticate' header set for the response.

ctx.response.unauthorized('invalid password');
+

Generates the following response:

"payload": {
+  "statusCode": 401,
+  "error": "Unauthorized",
+  "message": "invalid password"
+},
+"headers" {}
+
ctx.response.unauthorized('invalid password', 'sample');
+

Generates the following response:

"payload": {
+  "statusCode": 401,
+  "error": "Unauthorized",
+  "message": "invalid password",
+  "attributes": {
+    "error": "invalid password"
+  }
+},
+"headers" {
+  "WWW-Authenticate": "sample error=\"invalid password\""
+}
+
ctx.response.unauthorized(null, 'Negotiate', 'VGhpcyBpcyBhIHRlc3QgdG9rZW4=');
+

Generates the following response:

"payload": {
+  "statusCode": 401,
+  "error": "Unauthorized",
+  "attributes": "VGhpcyBpcyBhIHRlc3QgdG9rZW4="
+},
+"headers" {
+  "WWW-Authenticate": "Negotiate VGhpcyBpcyBhIHRlc3QgdG9rZW4="
+}
+
ctx.response.unauthorized('invalid password', 'sample', { ttl: 0, cache: null, foo: 'bar' });
+

Generates the following response:

"payload": {
+  "statusCode": 401,
+  "error": "Unauthorized",
+  "message": "invalid password",
+  "attributes": {
+    "error": "invalid password",
+    "ttl": 0,
+    "cache": "",
+    "foo": "bar"
+  }
+},
+"headers" {
+  "WWW-Authenticate": "sample ttl=\"0\", cache=\"\", foo=\"bar\", error=\"invalid password\""
+}
+

ctx.response.paymentRequired([message], [data])

Returns a 402 Payment Required error where:

  • message - optional message.
  • data - optional additional error data.
ctx.response.paymentRequired('bandwidth used');
+

Generates the following response payload:

{
+  "statusCode": 402,
+  "error": "Payment Required",
+  "message": "bandwidth used"
+}
+

ctx.response.forbidden([message], [data])

Returns a 403 Forbidden error where:

  • message - optional message.
  • data - optional additional error data.
ctx.response.forbidden('try again some time');
+

Generates the following response payload:

{
+  "statusCode": 403,
+  "error": "Forbidden",
+  "message": "try again some time"
+}
+

ctx.response.notFound([message], [data])

Returns a 404 Not Found error where:

  • message - optional message.
  • data - optional additional error data.
ctx.response.notFound('missing');
+

Generates the following response payload:

{
+  "statusCode": 404,
+  "error": "Not Found",
+  "message": "missing"
+}
+

ctx.response.methodNotAllowed([message], [data], [allow])

Returns a 405 Method Not Allowed error where:

  • message - optional message.
  • data - optional additional error data.
  • allow - optional string or array of strings (to be combined and separated by ', ') which is set to the 'Allow' header.
ctx.response.methodNotAllowed('that method is not allowed');
+

Generates the following response payload:

{
+  "statusCode": 405,
+  "error": "Method Not Allowed",
+  "message": "that method is not allowed"
+}
+

ctx.response.notAcceptable([message], [data])

Returns a 406 Not Acceptable error where:

  • message - optional message.
  • data - optional additional error data.
ctx.response.notAcceptable('unacceptable');
+

Generates the following response payload:

{
+  "statusCode": 406,
+  "error": "Not Acceptable",
+  "message": "unacceptable"
+}
+

ctx.response.proxyAuthRequired([message], [data])

Returns a 407 Proxy Authentication Required error where:

  • message - optional message.
  • data - optional additional error data.
ctx.response.proxyAuthRequired('auth missing');
+

Generates the following response payload:

{
+  "statusCode": 407,
+  "error": "Proxy Authentication Required",
+  "message": "auth missing"
+}
+

ctx.response.clientTimeout([message], [data])

Returns a 408 Request Time-out error where:

  • message - optional message.
  • data - optional additional error data.
ctx.response.clientTimeout('timed out');
+

Generates the following response payload:

{
+  "statusCode": 408,
+  "error": "Request Time-out",
+  "message": "timed out"
+}
+

ctx.response.conflict([message], [data])

Returns a 409 Conflict error where:

  • message - optional message.
  • data - optional additional error data.
ctx.response.conflict('there was a conflict');
+

Generates the following response payload:

{
+  "statusCode": 409,
+  "error": "Conflict",
+  "message": "there was a conflict"
+}
+

ctx.response.resourceGone([message], [data])

Returns a 410 Gone error where:

  • message - optional message.
  • data - optional additional error data.
ctx.response.resourceGone('it is gone');
+

Generates the following response payload:

{
+  "statusCode": 410,
+  "error": "Gone",
+  "message": "it is gone"
+}
+

ctx.response.lengthRequired([message], [data])

Returns a 411 Length Required error where:

  • message - optional message.
  • data - optional additional error data.
ctx.response.lengthRequired('length needed');
+

Generates the following response payload:

{
+  "statusCode": 411,
+  "error": "Length Required",
+  "message": "length needed"
+}
+

ctx.response.preconditionFailed([message], [data])

Returns a 412 Precondition Failed error where:

  • message - optional message.
  • data - optional additional error data.
ctx.response.preconditionFailed();
+

Generates the following response payload:

{
+  "statusCode": 412,
+  "error": "Precondition Failed"
+}
+

ctx.response.entityTooLarge([message], [data])

Returns a 413 Request Entity Too Large error where:

  • message - optional message.
  • data - optional additional error data.
ctx.response.entityTooLarge('too big');
+

Generates the following response payload:

{
+  "statusCode": 413,
+  "error": "Request Entity Too Large",
+  "message": "too big"
+}
+

ctx.response.uriTooLong([message], [data])

Returns a 414 Request-URI Too Large error where:

  • message - optional message.
  • data - optional additional error data.
ctx.response.uriTooLong('uri is too long');
+

Generates the following response payload:

{
+  "statusCode": 414,
+  "error": "Request-URI Too Large",
+  "message": "uri is too long"
+}
+

ctx.response.unsupportedMediaType([message], [data])

Returns a 415 Unsupported Media Type error where:

  • message - optional message.
  • data - optional additional error data.
ctx.response.unsupportedMediaType('that media is not supported');
+

Generates the following response payload:

{
+  "statusCode": 415,
+  "error": "Unsupported Media Type",
+  "message": "that media is not supported"
+}
+

ctx.response.rangeNotSatisfiable([message], [data])

Returns a 416 Requested Range Not Satisfiable error where:

  • message - optional message.
  • data - optional additional error data.
ctx.response.rangeNotSatisfiable();
+

Generates the following response payload:

{
+  "statusCode": 416,
+  "error": "Requested Range Not Satisfiable"
+}
+

ctx.response.expectationFailed([message], [data])

Returns a 417 Expectation Failed error where:

  • message - optional message.
  • data - optional additional error data.
ctx.response.expectationFailed('expected this to work');
+

Generates the following response payload:

{
+  "statusCode": 417,
+  "error": "Expectation Failed",
+  "message": "expected this to work"
+}
+

ctx.response.teapot([message], [data])

Returns a 418 I'm a Teapot error where:

  • message - optional message.
  • data - optional additional error data.
ctx.response.teapot('sorry, no coffee...');
+

Generates the following response payload:

{
+  "statusCode": 418,
+  "error": "I'm a Teapot",
+  "message": "Sorry, no coffee..."
+}
+

ctx.response.badData([message], [data])

Returns a 422 Unprocessable Entity error where:

  • message - optional message.
  • data - optional additional error data.
ctx.response.badData('your data is bad and you should feel bad');
+

Generates the following response payload:

{
+  "statusCode": 422,
+  "error": "Unprocessable Entity",
+  "message": "your data is bad and you should feel bad"
+}
+

ctx.response.locked([message], [data])

Returns a 423 Locked error where:

  • message - optional message.
  • data - optional additional error data.
ctx.response.locked('this resource has been locked');
+

Generates the following response payload:

{
+  "statusCode": 423,
+  "error": "Locked",
+  "message": "this resource has been locked"
+}
+

ctx.response.preconditionRequired([message], [data])

Returns a 428 Precondition Required error where:

  • message - optional message.
  • data - optional additional error data.
ctx.response.preconditionRequired('you must supply an If-Match header');
+

Generates the following response payload:

{
+  "statusCode": 428,
+  "error": "Precondition Required",
+  "message": "you must supply an If-Match header"
+}
+

ctx.response.tooManyRequests([message], [data])

Returns a 429 Too Many Requests error where:

  • message - optional message.
  • data - optional additional error data.
ctx.response.tooManyRequests('you have exceeded your request limit');
+

Generates the following response payload:

{
+  "statusCode": 429,
+  "error": "Too Many Requests",
+  "message": "you have exceeded your request limit"
+}
+

ctx.response.illegal([message], [data])

Returns a 451 Unavailable For Legal Reasons error where:

  • message - optional message.
  • data - optional additional error data.
ctx.response.illegal('you are not permitted to view this resource for legal reasons');
+

Generates the following response payload:

{
+  "statusCode": 451,
+  "error": "Unavailable For Legal Reasons",
+  "message": "you are not permitted to view this resource for legal reasons"
+}
+

HTTP 5xx Errors

All 500 errors hide your message from the end user. Your message is recorded in the server log.

ctx.response.badImplementation([message], [data]) - (alias: internal)

Returns a 500 Internal Server Error error where:

  • message - optional message.
  • data - optional additional error data.
ctx.response.badImplementation('terrible implementation');
+

Generates the following response payload:

{
+  "statusCode": 500,
+  "error": "Internal Server Error",
+  "message": "An internal server error occurred"
+}
+

ctx.response.notImplemented([message], [data])

Returns a 501 Not Implemented error where:

  • message - optional message.
  • data - optional additional error data.
ctx.response.notImplemented('method not implemented');
+

Generates the following response payload:

{
+  "statusCode": 501,
+  "error": "Not Implemented",
+  "message": "method not implemented"
+}
+

ctx.response.badGateway([message], [data])

Returns a 502 Bad Gateway error where:

  • message - optional message.
  • data - optional additional error data.
ctx.response.badGateway('that is a bad gateway');
+

Generates the following response payload:

{
+  "statusCode": 502,
+  "error": "Bad Gateway",
+  "message": "that is a bad gateway"
+}
+

ctx.response.serverUnavailable([message], [data])

Returns a 503 Service Unavailable error where:

  • message - optional message.
  • data - optional additional error data.
ctx.response.serverUnavailable('unavailable');
+

Generates the following response payload:

{
+  "statusCode": 503,
+  "error": "Service Unavailable",
+  "message": "unavailable"
+}
+

ctx.response.gatewayTimeout([message], [data])

Returns a 504 Gateway Time-out error where:

  • message - optional message.
  • data - optional additional error data.
ctx.response.gatewayTimeout();
+

Generates the following response payload:

{
+  "statusCode": 504,
+  "error": "Gateway Time-out"
+}
+
+ + + diff --git a/docs/.vuepress/dist/3.x.x/guides/restapi.html b/docs/.vuepress/dist/3.x.x/guides/restapi.html new file mode 100644 index 0000000000..1416a63274 --- /dev/null +++ b/docs/.vuepress/dist/3.x.x/guides/restapi.html @@ -0,0 +1,49 @@ + + + + + + Filters | Strapi Docs + + + + + + + +

Filters

See the filters' concepts for details.

by default, the filters can only be used from find endpoints generated by the Content Type Builder and the CLI. If you need to implement a filters system somewhere else, read the programmatic usage section.

Available operators

The available operators are separated in four different categories:

Filters

Easily filter results according to fields values.

  • =: Equals
  • _ne: Not equals
  • _lt: Lower than
  • _gt: Greater than
  • _lte: Lower than or equal to
  • _gte: Greater than or equal to
  • _contains: Contains
  • _containss: Contains case sensitive

Examples

Find users having John as first name.

GET /user?firstName=John

Find products having a price equal or greater than 3.

GET /product?price_gte=3

Sort

Sort according to a specific field.

Example

Sort users by email.

  • ASC: GET /user?_sort=email:asc
  • DESC: GET /user?_sort=email:desc

Limit

Limit the size of the returned results.

Example

Limit the result length to 30.

GET /user?_limit=30

Start

Skip a specific number of entries (especially useful for pagination).

Example

Get the second page of results.

GET /user?_start=10&_limit=10

Programmatic usage

Requests system can be implemented in custom code sections.

Extracting requests filters

To extract the filters from an JavaScript object or a request, you need to call the strapi.utils.models.convertParams helper.

The returned objects is formatted according to the ORM used by the model.

Example

Path — ./api/user/controllers/User.js.

// Define a list of params.
+const params = {
+  '_limit': 20,
+  '_sort': 'email'
+};
+
+// Convert params.
+const formattedParams = strapi.utils.models.convertParams('user', params); // { limit: 20, sort: 'email' }
+

Query usage

Example

Path — ./api/user/controllers/User.js.

module.exports = {
+
+  find: async (ctx) => {
+    // Convert params.
+    const formattedParams = strapi.utils.models.convertParams('user', ctx.request.query);
+
+    // Get the list of users according to the request query.
+    const filteredUsers = await User
+      .find()
+      .where(formattedParams.where)
+      .sort(formattedParams.sort)
+      .skip(formattedParams.start)
+      .limit(formattedParams.limit);
+
+    // Finally, send the results to the client.
+    ctx.body = filteredUsers;
+  };
+};
+
+ + + diff --git a/docs/.vuepress/dist/3.x.x/guides/routing.html b/docs/.vuepress/dist/3.x.x/guides/routing.html new file mode 100644 index 0000000000..3be7631845 --- /dev/null +++ b/docs/.vuepress/dist/3.x.x/guides/routing.html @@ -0,0 +1,68 @@ + + + + + + Routing | Strapi Docs + + + + + + + +

Routing

See the routing's concept for details.

How to create a route?

You have to edit the routes.json file in one of your APIs folders (./api/**/config/routes.json) and manually add a new route object into the routes array.

Path — ./api/**/config/routes.json.

{
+  "routes": [
+    {
+      "method": "GET",
+      "path": "/product",
+      "handler": "Product.find",
+    },
+    {
+      "method": ["POST", "PUT"],
+      "path": "/product/:id",
+      "handler": "Product.createOrUpdate",
+    },
+    {
+      "method": "POST",
+      "path": "/product/:id/buy",
+      "handler": "Product.buy",
+      "config": {
+        "policies": ["isAuthenticated", "hasCreditCard"]
+      }
+    }
+  ]
+}
+
  • method (string): Method or array of methods to hit the route (ex: GET, POST, PUT, HEAD, DELETE, PATCH)
  • path (string): URL starting with / (ex: /product)
  • handler (string): Action to executed when the route is hit following this syntax <Controller>.<action>
  • config
    • policies (array): Array of policies names or path (see more)
    • prefix (string): Set a prefix to this route. Also, it will be loaded into the main router (useful feature for plugin)

Dynamic parameters

The router used by Strapi allows you to create dynamic routes where you can use parameters and simple regular expressions. These parameters will be exposed in the ctx.params object. For more details, please refer to the PathToRegex documentation.

{
+  "routes": [
+    {
+      "method": "GET",
+      "path": "/product/:category/:id",
+      "handler": "Product.findOneByCategory",
+    },
+    {
+      "method": "GET",
+      "path": "/product/:region(\\d{2}|\\d{3})/:id", // Only match when the first parameter contains 2 or 3 digits.
+      "handler": "Product.findOneByRegion",
+    }
+  ]
+}
+

Override default route

By default, the main route of the server / is pointed to the /public/index.html file. To override this behavior, you need to create a route with an empty path / in one of your API folder (/api/**/config/routes.json).

{
+  "routes": [
+    {
+      "method": "GET",
+      "path": "/",
+      "handler": "Controller.name",
+    }
+  ]
+}
+
+ + + diff --git a/docs/.vuepress/dist/3.x.x/guides/services.html b/docs/.vuepress/dist/3.x.x/guides/services.html new file mode 100644 index 0000000000..2767e4f3df --- /dev/null +++ b/docs/.vuepress/dist/3.x.x/guides/services.html @@ -0,0 +1,63 @@ + + + + + + Services | Strapi Docs + + + + + + + +

Services

See the services concept for details.

How to create a service?

There is two ways to create a service.

  • Using the CLI strapi generate:service user. Read the CLI documentation for more information.
  • Manually create a JavaScript file named User.js in ./api/**/services/.

Example

The goal of a service is to store reusable functions. An email service could be useful, if we plan to send emails from different functions in our codebase:

Path — ./api/email/services/Email.js.

const nodemailer = require('nodemailer');
+
+// Create reusable transporter object using SMTP transport.
+const transporter = nodemailer.createTransport({
+  service: 'Gmail',
+  auth: {
+    user: 'user@gmail.com',
+    pass: 'password'
+  }
+});
+
+module.exports = {
+  send: (from, to, subject, text) => {  
+    // Setup e-mail data.
+    const options = {
+      from,
+      to,
+      subject,
+      text
+    };
+
+    // Return a promise of the function that sends the email.
+    return transporter.sendMail(options);
+  }
+};
+

please make sure you installed nodemailer (npm install nodemailer) for this example.

The service is now available through the strapi.services global variable. We can use it in another part of our codebase. For example a controller like below:

Path — ./api/user/controllers/User.js.

module.exports = {
+  // GET /hello
+  signup: async (ctx) => {
+    // Store the new user in database.
+    const user = await User.create(ctx.params);
+
+    // Send an email to validate his subscriptions.
+    strapi.services.email.send('welcome@mysite.com', user.email, 'Welcome', '...');
+
+    // Send response to the server.
+    ctx.send({
+      ok: true
+    });
+  }
+};
+
+ + + diff --git a/docs/.vuepress/dist/3.x.x/guides/upload.html b/docs/.vuepress/dist/3.x.x/guides/upload.html new file mode 100644 index 0000000000..538e67dc5c --- /dev/null +++ b/docs/.vuepress/dist/3.x.x/guides/upload.html @@ -0,0 +1,90 @@ + + + + + + File Upload | Strapi Docs + + + + + + + +

File Upload

WARNING

This feature requires the Upload plugin (installed by default).

Thanks to the plugin Upload, you can upload any kind of files on your server or externals providers such as AWS S3.

Usage

The plugin exposes a single route POST /upload to upload one or multiple files in a single request.

WARNING

Please send the request using multipart/form-data encoding

Parameters

  • files: The file(s) to upload. The value(s) can be a Buffer or Stream.
  • refId: (optional): The ID of the entry which the file(s) will be linked to.
  • ref: (optional): The name of the model which the file(s) will be linked to (see more below).
  • source: (optional): The name of the plugin where the model is located.
  • field: (optional): The field of the entry which the file(s) will be precisely linked to.

Models

To add a new file attribute in your models, it's like adding a new association. In the first example, you will be able to upload and attach one file to the avatar attribute. Whereas, in our second example, you can upload and attach multiple pictures to the product.

Path — User.settings.json.

{
+  "connection": "default",
+  "attributes": {
+    "pseudo": {
+      "type": "string",
+      "required": true
+    },
+    "email": {
+      "type": "email",
+      "required": true,
+      "unique": true
+    },
+    "avatar": {
+      "model": "file",
+      "via": "related",
+      "plugin": "upload"
+    }
+  }
+}
+

Path — Product.settings.json.

{
+  "connection": "default",
+  "attributes": {
+    "name": {
+      "type": "string",
+      "required": true
+    },
+    "price": {
+      "type": "integer",
+      "required": true
+    },
+    "pictures": {
+      "collection": "file",
+      "via": "related",
+      "plugin": "upload"
+    }
+  }
+}
+

Examples

Single file

curl -X POST -F 'files=@/path/to/pictures/file.jpg' http://localhost:1337/upload
+

Multiple files

curl -X POST -F 'files[]=@/path/to/pictures/fileX.jpg' -F 'files[]=@/path/to/pictures/fileY.jpg' http://localhost:1337/upload
+

Linking files to an entry

Let's say that you want to have a User model provided by the plugin Users & Permissions and you want to upload an avatar for a specific user.

{
+  "connection": "default",
+  "attributes": {
+    "pseudo": {
+      "type": "string",
+      "required": true
+    },
+    "email": {
+      "type": "email",
+      "required": true,
+      "unique": true
+    },
+    "avatar": {
+      "model": "file",
+      "via": "related",
+      "plugin": "upload"
+    }
+  }
+}
+
{
+  "files": "...", // Buffer or stream of file(s)
+  "refId": "5a993616b8e66660e8baf45c", // User's Id.
+  "ref": "user", // Model name.
+  "source": "users-permissions", // Plugin name.
+  "field": "avatar" // Field name in the User model.
+}
+

Here the request to make to associate the file (/path/to/pictures/avatar.jpg) to the user (id: 5a993616b8e66660e8baf45c) when the User model is provided by the Users & Permissions plugin.

curl -X POST -F 'files=@/path/to/pictures/avatar.jpg&refId=5a993616b8e66660e8baf45c&ref=user&source=users-permissions&field=avatar' http://localhost:1337/upload
+

Install providers

By default Strapi provides a local file upload system. You might want to upload your files on AWS S3 or another provider.

To install a new provider run:

$ npm install strapi-upload-aws-s3@alpha --save
+

We have two providers available strapi-upload-aws-s3 and strapi-upload-cloudinary, use the alpha tag to install one of them. Then, visit /admin/plugins/upload/configurations/development on your web browser and configure the provider.

If you want to create your own, make sure the name starts with strapi-upload- (duplicating an existing one will be easier to create), modify the auth config object and customize the upload and delete functions.

Check all community providers available on npmjs.org - Providers list

+ + + diff --git a/docs/.vuepress/dist/3.x.x/index.html b/docs/.vuepress/dist/3.x.x/index.html new file mode 100644 index 0000000000..8fa90ef7f6 --- /dev/null +++ b/docs/.vuepress/dist/3.x.x/index.html @@ -0,0 +1,32 @@ + + + + + + API creation made simple, secure and fast. | Strapi Docs + + + + + + + +

Logo

API creation made simple, secure and fast.

The most advanced open-source Content Management Framework to build powerful API with no effort.

npm version npm downloads Build status Slack status Heroku Deploy

v3@alpha.13 is available!

We've been working on a major update for Strapi during the past months, rewriting the core framework and the dashboard.

This documentation is only related to Strapi v3@alpha.13 (v1 documentation is still available).

Get Started
+Learn how to install Strapi and start developing your API.

Command Line Interface
+Get to know our CLI to make features the hacker way!

Concepts
+Get to know more about Strapi and how it works.

Guides
+Get familiar with Strapi. Discover concrete examples on how to develop the features you need.

Plugin Development
+Understand how to develop your own plugin.

API Reference
+Learn about Strapi's API, the strapi object that is available in your backend.

Migration guide

+ + + diff --git a/docs/.vuepress/dist/3.x.x/migration/migration-guide-alpha-10-to-alpha-11.html b/docs/.vuepress/dist/3.x.x/migration/migration-guide-alpha-10-to-alpha-11.html new file mode 100644 index 0000000000..71be5d3882 --- /dev/null +++ b/docs/.vuepress/dist/3.x.x/migration/migration-guide-alpha-10-to-alpha-11.html @@ -0,0 +1,30 @@ + + + + + + Migrating from 3.0.0-alpha.10 to 3.0.0-alpha.11 | Strapi Docs + + + + + + + +

Migrating from 3.0.0-alpha.10 to 3.0.0-alpha.11

Here are the major changes:

  • Add plugin upload

Feel free to join us on Slack and ask questions about the migration process.

Getting started

Install Strapi alpha.11.1 globally on your computer. To do so run npm install strapi@3.0.0-alpha.11.1 -g.

When it's done, generate a new empty project strapi new myNewProject (don't pay attention to the database configuration).

Configurations

You will have to update just 1 file: package.json

  • Edit the Strapi's dependencies version: (move Strapi's dependencies to 3.0.0-alpha.11.1 version) in package.json file
{
+  "dependencies": {
+    "lodash": "4.x.x",
+    "strapi": "3.0.0-alpha.11.1",
+    "strapi-mongoose": "3.0.0-alpha.11.1"
+  }
+}
+

Update the Admin

Delete your old admin folder and replace it by the new one.

Update the Plugins

Copy this file /plugins/users-permissions/config/jwt.json from your old project and paste it in the corresponding one in your new project.

Copy the fields and relations you had in your /plugins/users-permissions/models/User.settings.json file in the new one.

Then, delete your old plugins folder and replace it by the new one.

Update the Dependencies

Now let's update the dependencies in your package.json we edited earlier. Simply run npm install:

That's all, you have now upgraded to Strapi alpha.11.

+ + + diff --git a/docs/.vuepress/dist/3.x.x/migration/migration-guide-alpha-11-to-alpha-12.html b/docs/.vuepress/dist/3.x.x/migration/migration-guide-alpha-11-to-alpha-12.html new file mode 100644 index 0000000000..1e2a8060de --- /dev/null +++ b/docs/.vuepress/dist/3.x.x/migration/migration-guide-alpha-11-to-alpha-12.html @@ -0,0 +1,57 @@ + + + + + + Migrating from 3.0.0-alpha.11 to 3.0.0-alpha.12.1.3 | Strapi Docs + + + + + + + +

Migrating from 3.0.0-alpha.11 to 3.0.0-alpha.12.1.3

This migration guide is a mix of migrations from 3.0.0-alpha.11.1 to 3.0.0-alpha.11.2, 3.0.0-alpha.11.2 to 3.0.0-alpha.11.3 and from 3.0.0-alpha.11.3 to 3.0.0-alpha.12.1.3.

Feel free to join us on Slack and ask questions about the migration process.

Getting started

Install Strapi alpha.12.1.3 globally on your computer. To do so run npm install strapi@3.0.0-alpha.12.1.3 -g.

When it's done, generate a new empty project strapi new myNewProject (don't pay attention to the database configuration).

Configurations

You will have to update just 1 file: package.json

  • Edit the Strapi's dependencies version: (move Strapi's dependencies to 3.0.0-alpha.12.1.3 version) in package.json file
{
+  "dependencies": {
+    "lodash": "4.x.x",
+    "strapi": "3.0.0-alpha.12.1.3",
+    "strapi-mongoose": "3.0.0-alpha.12"
+  }
+}
+

Update the Admin

Delete your old admin folder and replace it by the new one.

Update the Plugins

Copy the fields and relations you had in your /plugins/users-permissions/models/User.settings.json file in the new one.

Then, delete your old plugins folder and replace it by the new one.

Update roles

This update is if you come from version before alpha-11.2

Update type of Guest role to public in your database. You can also update name and description:

{
+  "name": "Public",
+  "description": "Default role given to unauthenticated user.",
+  "type": "public"
+}
+

Create Authenticated role:

{
+  "name": "Authenticated",
+  "description": "Default role given to authenticated user.",
+  "type": "authenticated"
+}
+

In Users & Permissions > Advanced in admin panel update default role to Authenticated

You also will have to reset your roles permissions.

Update bookshelf filters

WARNING

This update is if you come from version before alpha-11.3

You will have to replace your fetchAll services queries of your generated API:

_.forEach(convertedParams.where, (where, key) => {
+   if (_.isArray(where.value)) {
+     for (const value in where.value) {
+       qb[value ? 'where' : 'orWhere'](key, where.symbol, where.value[value])
+     }
+   } else {
+     qb.where(key, where.symbol, where.value);
+   }
+ });
+
+ if (convertedParams.sort) {
+   qb.orderBy(convertedParams.sort.key, convertedParams.sort.order);
+ }
+
+ qb.offset(convertedParams.start);
+
+ qb.limit(convertedParams.limit);
+

That's all, you have now upgraded to Strapi alpha.12.1.3.

+ + + diff --git a/docs/.vuepress/dist/3.x.x/migration/migration-guide-alpha-7-4-to-alpha-8.html b/docs/.vuepress/dist/3.x.x/migration/migration-guide-alpha-7-4-to-alpha-8.html new file mode 100644 index 0000000000..fca6d2a4b2 --- /dev/null +++ b/docs/.vuepress/dist/3.x.x/migration/migration-guide-alpha-7-4-to-alpha-8.html @@ -0,0 +1,39 @@ + + + + + + Migrating from 3.0.0-alpha.7.3 to 3.0.0-alpha.8 | Strapi Docs + + + + + + + +

Migrating from 3.0.0-alpha.7.3 to 3.0.0-alpha.8

Here are the major changes:

  • Fix deployment process
  • Setup database connection on project creation
  • Helper for table creation for SQL database

Feel free to join us on Slack and ask questions about the migration process.

Getting started

Install Strapi alpha.8 globally on your computer. To do so run npm install strapi@3.0.0-alpha.8 -g.

When it's done, generate a new empty project strapi new myNewProject (don't pay attention to the database configuration).

Configurations

You will have to update just 1 file: package.json

  • Edit the scripts section: (only the setup line has changed)
{
+  "scripts": {
+    "setup": "cd admin && npm run setup",
+    "start": "node server.js",
+    "strapi": "node_modules/strapi/bin/strapi.js",
+    "lint": "node_modules/.bin/eslint api/**/*.js config/**/*.js plugins/**/*.js",
+    "postinstall": "node node_modules/strapi/lib/utils/post-install.js"
+  }
+}
+
  • Edit the Strapi's dependencies version: (move Strapi's dependencies to 3.0.0-alpha.8 version)
{
+  "dependencies": {
+    "lodash": "4.x.x",
+    "strapi": "3.0.0-alpha.8",
+    "strapi-mongoose": "3.0.0-alpha.8"
+  }
+}
+

Update the Admin

Delete your old admin folder and replace by the new one.

Update the Plugins

Copy these 3 files /plugins/users-permissions/config/jwt.json, /plugins/users-permissions/config/roles.json and /plugins/users-permissions/models/User.settings.json from your old project and paste them in the corresponding ones in your new project. It is important to save these files.

Then, delete your old plugins folder and replace it by the new one.

That's all, you have now upgraded to Strapi alpha.8.

+ + + diff --git a/docs/.vuepress/dist/3.x.x/migration/migration-guide-alpha-8-to-alpha-9.html b/docs/.vuepress/dist/3.x.x/migration/migration-guide-alpha-8-to-alpha-9.html new file mode 100644 index 0000000000..ae5ed87441 --- /dev/null +++ b/docs/.vuepress/dist/3.x.x/migration/migration-guide-alpha-8-to-alpha-9.html @@ -0,0 +1,35 @@ + + + + + + Migrating from 3.0.0-alpha.8 to 3.0.0-alpha.9 | Strapi Docs + + + + + + + +

Migrating from 3.0.0-alpha.8 to 3.0.0-alpha.9

Here are the major changes:

  • Put roles' permissions in database
  • Providers connection (Facebook, GitHub, ...)

Feel free to join us on Slack and ask questions about the migration process.

Getting started

Install Strapi alpha.9 globally on your computer. To do so run npm install strapi@3.0.0-alpha.9 -g.

When it's done, generate a new empty project strapi new myNewProject (don't pay attention to the database configuration).

Configurations

You will have to update just 2 files: package.json and request.json

  • Edit the Strapi's dependencies version: (move Strapi's dependencies to 3.0.0-alpha.9 version) in package.json file
{
+  "dependencies": {
+    "lodash": "4.x.x",
+    "strapi": "3.0.0-alpha.9",
+    "strapi-mongoose": "3.0.0-alpha.9"
+  }
+}
+
  • Edit the session.enabled settings to true in each environment file: /configs/environments/***/request.json
{
+  "session": {
+    "enabled": true
+  }
+}
+

Update the Admin

Delete your old admin folder and replace it by the new one.

Update the Plugins

Copy this file /plugins/users-permissions/config/jwt.json from your old project and paste it in the corresponding one in your new project.

Copy the fields and relations you had in your /plugins/users-permissions/models/User.settings.json file in the new one.

Then, delete your old plugins folder and replace it by the new one.

⚠️ Roles update

Roles are now stored in your database. You will have to re-create and configure them via the admin dashboard.

⚠️ User collection/table name has changed

If you have an existing set of users in your database you will have to rename the collection/table from user to users-permissions_user.

Then update all your users by changing the old role id by the new one which is in users-permissions_role collection/table.

That's all, you have now upgraded to Strapi alpha.9.

+ + + diff --git a/docs/.vuepress/dist/3.x.x/migration/migration-guide-alpha-9-to-alpha-10.html b/docs/.vuepress/dist/3.x.x/migration/migration-guide-alpha-9-to-alpha-10.html new file mode 100644 index 0000000000..f896eeb5da --- /dev/null +++ b/docs/.vuepress/dist/3.x.x/migration/migration-guide-alpha-9-to-alpha-10.html @@ -0,0 +1,30 @@ + + + + + + Migrating from 3.0.0-alpha.9 to 3.0.0-alpha.10 | Strapi Docs + + + + + + + +

Migrating from 3.0.0-alpha.9 to 3.0.0-alpha.10

Here are the major changes:

  • Add database store config
  • New lib input

Feel free to join us on Slack and ask questions about the migration process.

Getting started

Install Strapi alpha.10.1 globally on your computer. To do so run npm install strapi@3.0.0-alpha.10.1 -g.

When it's done, generate a new empty project strapi new myNewProject (don't pay attention to the database configuration).

Configurations

You will have to update just 1 file: package.json

  • Edit the Strapi's dependencies version: (move Strapi's dependencies to 3.0.0-alpha.10.1 version) in package.json file
{
+  "dependencies": {
+    "lodash": "4.x.x",
+    "strapi": "3.0.0-alpha.10.1",
+    "strapi-mongoose": "3.0.0-alpha.10.1"
+  }
+}
+

Update the Admin

Delete your old admin folder and replace it by the new one.

Update the Plugins

Copy this file /plugins/users-permissions/config/jwt.json from your old project and paste it in the corresponding one in your new project.

Copy the fields and relations you had in your /plugins/users-permissions/models/User.settings.json file in the new one.

Then, delete your old plugins folder and replace it by the new one.

⚠️ Config in database

To let you update your configurations when your application is deployed on multiple server instances, we have created a data store for settings. So we moved all the users-permissions plugin's configs in database.

You will have to reconfigure all your users-permissions configs from the admin panel. Then delete the advanced.json, email.json and grant.json files from plugins/users-permissions/config folder.

⚠️ Data type Number

We fixed how mongoose handles the model's Number type. Previously, mongoose stored Number type as String and now it's Integer. So you will have to update all your documents which have a type Number in its model and replace their String value with a Number one.

That's all, you have now upgraded to Strapi alpha.10.

+ + + diff --git a/docs/.vuepress/dist/3.x.x/migration/migration-guide.html b/docs/.vuepress/dist/3.x.x/migration/migration-guide.html new file mode 100644 index 0000000000..7558192003 --- /dev/null +++ b/docs/.vuepress/dist/3.x.x/migration/migration-guide.html @@ -0,0 +1,265 @@ + + + + + + Migrating from v1 to v3 | Strapi Docs + + + + + + + +

Migrating from v1 to v3

To be honest with all of you, the migration process won't be easy. The new version introduces a lot of breaking changes especially on the query part. Some features are still not available for the moment such as the authentication, users & permissions, email, media upload and GraphQL. If you're using one of theses features, you shouldn't be able to migrate unless you rewrite these features from scratch.

Here are the major changes:

  • Moved from Waterline to specialized ORMs such as Mongoose (MongoDB) and Bookshelf (Postgres, MySQL, Maria, SQLite).
  • New configurations structure.
  • Moved from Koa@1 (generators) to Koa@2 (async functions).
  • Removed middlewares from core (koa-graphql, koa-proxy, koa-ssl, koa-views).
  • Better error handling with Boom.

Feel free to join us on Slack and ask questions about the migration process.

Getting started

The best way to migrate your project is to generate a new empty project using the v3. Then, copy and paste your v1-app/api folder to the new app v3-app/api. The next step is to follow the categories one by one in order and you will be able to migrate your project without issues.

See the Quick start section to install the latest version of Strapi.

Configurations

The structure of the configurations has been harmonised and simplified. Files has been renamed or deleted, and some others has been added.

  • ./config/general.json renamed ./config/application.json
  • ./config/i18n.json renamed ./config/language.json
  • ./config/globals.json removed
  • ./config/studio.json removed
  • ./config/middleware.json added
  • ./config/hook.json added
  • ./config/custom.json added
  • ./config/environments/**/databases.json renamed ./config/environments/**/database.json
  • ./config/environments/**/response.json added
  • ./config/environments/**/custom.json added

Please refer to the new documentation to set the correct values in each file.

Don't forget that middlewares has been removed. Please refers to the right section of this document for more details.

Routes

The format of the routes has changed to easily integrate multiple strategies to hit the controllers' actions. It means that the routes are not REST-limited.

v1.x

{
+  "routes": {
+    "GET /post": {
+      "controller": "Post",
+      "action": "find"
+    },
+    "GET /post/:id": {
+      "controller": "Post",
+      "action": "findOne"
+    },
+    "POST /post": {
+      "controller": "Post",
+      "action": "create"
+    },
+    "PUT /post/:id": {
+      "controller": "Post",
+      "action": "update"
+    },
+    "DELETE /post/:id": {
+      "controller": "Post",
+      "action": "delete"
+    }
+  }
+}
+

v3.x

{
+  "routes": [
+    {
+      "method": "GET",
+      "path": "/post",
+      "handler": "Post.find",
+    },
+    {
+      "method": "GET",
+      "path": "/post/:id",
+      "handler": "Post.findOne",
+    },
+    {
+      "method": "POST",
+      "path": "/post",
+      "handler": "Post.create",
+    },
+    {
+      "method": "PUT",
+      "path": "/post/:id",
+      "handler": "Post.update",
+    },
+    {
+      "method": "DELETE",
+      "path": "/post/:id",
+      "handler": "Post.delete",
+    }
+  ]
+}
+

Controllers

Koa@1.x.x was based on generators whereas Koa@2.x.x is based on async functions. It means that the yield word has been replaced by the await word. Then the context was passed via the function context itself. Now, the context is passed through the function's parameters. Also, you don't need to apply the try/catch pattern in each controller's actions.

v1.x

module.exports = {
+  find: function *() {
+    try {
+      this.body = yield Post.find(this.params);
+    } catch (error) {
+      this.body = error;
+    }
+  }
+}
+

v3.x

module.exports = {
+  find: async (ctx) => {
+    ctx.body = await Post.find(this.params);
+  }
+}
+

Services

The services files should stay as they are. Your generator functions can be converted into async functions but it shouldn't be necessary.

Models

The models didn't change a lot. The autoCreatedAt, autoUpdatedAt and autoPK attributes have been removed and replaced by the hasTimestamps attribute.

The hasTimestamps options will only work with Bookshelf. Also you need to create the created_at and updated_at columns manually.

The enum type is not available for now. Also, the validations are not working properly. It means that most of the validations have to be done manually.

v1.x

{
+  "identity": "pet",
+  "connection": "mongoDBServer",
+  "schema": true,
+  "attributes": {
+    "name": {
+      "type": "string",
+      "required": true
+    },
+    "gender": {
+      "type": "string",
+      "enum": ["male", "female"]
+    },
+    "age": {
+      "type": "int",
+      "max": 100
+    },
+    "birthDate": {
+      "type": "date"
+    },
+    "breed": {
+      "type": "string"
+    }
+  },
+  "autoPK": true,
+  "autoCreatedAt": true,
+  "autoUpdatedAt": true
+}
+

v3.x

{
+  "connection": "mongoDBServer",
+  "options": {
+    "hasTimestamps": true
+  },
+  "attributes": {
+    "name": {
+      "type": "string",
+      "required": true
+    },
+    "gender": {
+      "type": "string"
+    },
+    "age": {
+      "type": "int",
+      "max": 100
+    },
+    "birthDate": {
+      "type": "date"
+    },
+    "breed": {
+      "type": "string"
+    }
+  }
+}
+

Query

We were based on the popular Waterline ORM. As we said in our blog posts, Waterline suffers from a lack of maintenance and we decided to move to more specific ORMs depending on the database. It increases the performances and unblock particular features for the users. Currently, we are supporting these databases:

  • MongoDB (through Mongoose).
  • Postgres, MySQL, SQLite3 and more (through Bookshelf).
  • Redis (through ioredis).

This major change means that you will have to rewrite every single query of your app. So, please to be sure that you need to switch to the new version of Strapi before starting the migration.

v1.x

module.exports = {
+  find: function *() {
+    try {
+      this.body = yield Post.find(this.params);
+    } catch (error) {
+      this.body = error;
+    }
+  },
+
+  findOne: function *() {
+    try {
+      this.body = yield Post.findOne(this.params);
+    } catch (error) {
+      this.body = error;
+    }
+  },
+
+  // POST request
+  create: function *() {
+    try {
+      this.body = yield Post.create(this.request.body);
+    } catch (error) {
+      this.body = error;
+    }
+  },
+
+  // PUT request
+  update: function *() {
+    try {
+      this.body = yield Post.update(this.params.id, this.request.body);
+    } catch (error) {
+      this.body = error;
+    }
+  },
+
+  // DELETE request
+  delete: function *() {
+    try {
+      this.body = yield Post.destroy(this.params);
+    } catch (error) {
+      this.body = error;
+    }
+  }
+};
+

v3.x

module.exports = {
+  find: async (ctx) => {
+    // Bookshelf
+    ctx.body = await Post.forge(this.params).fetchAll();
+    // Mongoose
+    ctx.body = await Post.find(this.params);
+  },
+
+  findOne: async (ctx) => {
+    // Bookshelf
+    ctx.body = await Post.forge(this.params).fetch();
+    // Mongoose
+    ctx.body = await Post.findOne(this.params);
+  },
+
+  create: async (ctx) => {
+    // Bookshelf
+    ctx.body = await Post.forge(this.request.body).save();
+    // Mongoose
+    ctx.body = await Post.create(this.request.body);
+  },
+
+  update: async (ctx) => {
+    // Bookshelf
+    ctx.body = await Post.forge({ id: 1234 }).save(this.request.body, {
+      patch: true
+    });
+    // Mongoose
+    ctx.body = await Post.update({ id: 1234 }, this.request.body);
+  },
+
+  delete: async (ctx) => {
+    // Bookshelf
+    ctx.body = await Post.forge({ id: 1234 }).destroy();
+    // Mongoose
+    ctx.body = await Post.findOneAndRemove({ id: 1234 });
+  }
+}
+

You will have more changes if your project is based on a SQL database. Waterline and Mongoose are almost sharing the same API.

Middlewares

We decided to reduce the core to the minimum. So, we removed middlewares with features that shouldn't be handled by a Node.js server such as:

  • GraphQL: We love GraphQL at Strapi and we will provide a strapi-plugin-graphql very soon.
  • Proxy: There are many great server solutions to handle a proxy feature such as nginx or Caddy.
  • SSL: Same as proxy.
  • Views: We are building APIs, not website. However, we know that sometimes you need to render views from your API server. That's why we created a strapi-views hook.

GraphQL

We are not able to give you a solution at the moment. As we said above, we will develop in the next weeks a dedicated plugin to integrate GraphQL into a project.

Proxy & SSL

You should take a look at these articles to configure SSL and proxy with nginx:

Views

It works exactly as before. You need to add strapi-views into your app's dependencies and configure the views as below:

Path — ./config/environments/**/custom.json

{
+  "views": {
+    "enabled": true,
+    "map": {
+      "ejs": "ejs"
+    },
+    "viewExt": "ejs",
+    "debug": true,
+    "cache": true
+  }
+}
+

You might have to install the template engine by your own (ex: npm install ejs --save).

Error handling

Boom is deeply integrated into the core which allows you to enjoy the entire Boom API through the context of your request. Every error throw in your project will be intercepted and decorated with Boom.

v1.x

module.exports = {
+  // GET request
+  find: function *() {
+    try {
+      const posts = yield Post.find(this.params);
+
+      if (posts.length === 0) {
+        ctx.status = 404;
+        ctx.body = 'There are no posts!';
+      } else {
+        ctx.body = posts;
+      }
+    } catch (error) {
+      this.body = error;
+    }
+  }
+};
+

v3.x

module.exports = {
+  // GET request
+  find: async (ctx) => {
+    const posts = await Post.find(this.params);
+
+    if (post.length === 0) {
+      ctx.notFound('There are no posts!'); // Set status to 404 and decorates error into a Boom object.
+    } else {
+      ctx.send(posts); // Set status to 200.
+    }
+  }
+};
+
+ + + diff --git a/docs/.vuepress/dist/3.x.x/plugin-development/backend-development.html b/docs/.vuepress/dist/3.x.x/plugin-development/backend-development.html new file mode 100644 index 0000000000..562d982688 --- /dev/null +++ b/docs/.vuepress/dist/3.x.x/plugin-development/backend-development.html @@ -0,0 +1,111 @@ + + + + + + Back-end Development | Strapi Docs + + + + + + + +

Back-end Development

This section explains how the 'back-end part' of your plugin works.

Routes

The plugin API routes are defined in the ./plugins/**/config/routes.json file.

Please refer to router documentation for informations.

Route prefix

Each route of a plugin is prefixed by the name of the plugin (eg: /my-plugin/my-plugin-route).

To disable the prefix, add the prefix attribute to each concerned route, like below:

{
+  "method": "GET",
+  "path": "/my-plugin-route",
+  "handler": "MyPlugin.action",
+  "prefix": false
+}
+

CLI

The CLI can be used to generate files in the plugins folders.

Please refer to the CLI documentation for more informations.

Controllers

Controllers contain functions executed according to the requested route.

Please refer to the Controllers documentation for more informations.

Models

A plugin can have its own models.

Table/Collection naming

Sometimes it happens that the plugins inject models that have the same name as yours. Let's take a quick example.

You already have User model defining in your ./api/user/models/User.settings.json API. And you decide to install the Users & Permissions plugin. This plugin also contains a User model. To avoid the conflicts, the plugins' models are not globally exposed which means you cannot access to the plugin's model like this:

module.exports = {
+  findUser: async function (params) {
+    // This `User` global variable will always make a reference the User model defining in your `./api/xxx/models/User.settings.json`.
+    return await User.find();
+  }
+}
+

Also, the table/collection name won't be users because you already have a User model. That's why, the framework will automatically prefix the table/collection name for this model with the name of the plugin. Which means in our example, the table/collection name of the User model of our plugin Users & Permissions will be users-permissions_users. If you want to force the table/collection name of the plugin's model, you can add the collectionName attribute in your model.

Please refer to the Models documentation for more informations.

Policies

Global policies

A plugin can also use a globally exposed policy in the current Strapi project.

{
+  "routes": [
+    {
+      "method": "GET",
+      "path": "/",
+      "handler": "MyPlugin.index",
+      "config": {
+        "policies": [
+          "global.isAuthenticated"
+        ]
+      }
+    }
+  ]
+}
+

Plugin policies

A plugin can have its own policies, such as adding security rules. For instance, if the plugin includes a policy named isAuthenticated, the syntax to use this policy would be:

{
+  "routes": [
+    {
+      "method": "GET",
+      "path": "/",
+      "handler": "MyPlugin.index",
+      "config": {
+        "policies": [
+          "plugins.myPlugin.isAuthenticated"
+        ]
+      }
+    }
+  ]
+}
+

Please refer to the Policies documentation for more informations.

ORM queries

Strapi supports multiple ORMs in order to let the users choose the database management system that suits their needs. Hence, each plugin must be compatible with at least one ORM. Each plugin contains a folder named queries in ./plugins/**/api/queries. A folder must be created for each ORM (eg. mongoose) with a file named mongoose.js which exports the Mongoose ORM related queries.

The queries are accessible through the strapi.query() method, which automatically contains the queries according to the ORM used by the model.

Example

Mongoose ORM queries definition:

Path — ./plugins/my-plugin/api/config/queries/mongoose/index.js.

module.exports = {
+  getUsers: async (params) => {
+    return User.find(params);
+  }
+}
+

Bookshelf ORM queries definition:

Path — ./plugins/my-plugin/api/config/queries/bookshelf/index.js.

module.exports = {
+  getUsers: async (params) => {
+    return User.fetchAll(params);
+  }
+}
+

Usage from the plugin:

Path — ./plugins/my-plugin/api/controllers/index.js.

module.exports = {
+  getUsers: async () => {
+    // Get parameters from the request
+    const { limit, sort } = ctx.request.query;
+
+    // Get the list of users using the plugins queries
+    const users = await strapi.query('User').getUsers({ limit, sort });
+
+    // Send the list of users as response
+    ctx.body = users;
+  }
+}
+

Advanced usage

Each function in the query file is bound with the ORM's model. It means that you can create generic query very easily. This feature is useful for CRUD such as we did in the Content Manager plugin.

Mongoose ORM generic queries:

Path — ./plugins/my-plugin/api/config/queries/mongoose/index.js.

module.exports = {
+  getAll: async function (params) {
+    // this refers to the Mongoose model called in the query
+    // ex: strapi.query('User').getAll(), this will be equal to the User Mongoose model.
+    return this.find(params);
+  }
+}
+

Bookshelf ORM generic queries:

Path — ./plugins/my-plugin/api/config/queries/bookshelf/index.js.

module.exports = {
+  getAll: async function (params) {
+    // this refers to the Bookshelf model called in the query
+    // ex: strapi.query('User').getAll(), this will be equal to the User Bookshelf model.
+    return this.fetchAll(params);
+  }
+}
+

Usage from the plugin:

Path — ./plugins/my-plugin/api/controllers/index.js.

module.exports = {
+  getUsers: async () => {
+    // Get parameters from the request
+    const { limit, sort } = ctx.request.query;
+
+    // Get the list of users using the plugin's queries
+    const users = await strapi.query('User').getAll({ limit, sort });
+
+    // Send the list of users as response
+    ctx.body = users;
+  }
+}
+

+ + + diff --git a/docs/.vuepress/dist/3.x.x/plugin-development/frontend-development.html b/docs/.vuepress/dist/3.x.x/plugin-development/frontend-development.html new file mode 100644 index 0000000000..2ecfbb305d --- /dev/null +++ b/docs/.vuepress/dist/3.x.x/plugin-development/frontend-development.html @@ -0,0 +1,77 @@ + + + + + + Front-end Development | Strapi Docs + + + + + + + +

Front-end Development

This section explains how to create your plugin interface in the admin panel. Refer to the Plugin Development Quick Start Section to start the project in development mode.

Introduction

Strapi's admin panel and plugins system aim to be an easy and powerful way to create new features.

The admin panel is a React application which can embed other React applications. These other React applications are the admin parts of each Strapi's plugins.

Routing

The routing is based on the React Router V4, due to it's implementation each route is declared in the containers/App/index.js file.

Also, we chose to use the Switch Router because it renders a route exclusively.

Route declaration :

Let's say that you want to create a route /user with params /:id associated with the container UserPage.

The declaration would be as followed :

Path — plugins/my-plugin/admin/src/containers/App/index.js.

import React from 'react';
+import UserPage from 'containers/UserPage';
+
+// ...
+
+class App extends React.Component {
+  // ...
+
+  render() {
+    return (
+      <div className={styles.myPlugin}>
+        <Switch>
+          <Route exact path="/plugins/my-plugin/user/:id" component={UserPage} />
+        </Switch>
+      </div>
+    );
+  }
+}
+
+// ...
+

See the Front-end Use Cases for more informations.

Data flow

Each plugin has its own data store, so it stays completely independent from the others.

Data flow is controlled thanks to Redux and redux-sagas.

Styling

The Bootstrap styles are inherited by the plugins. However, each component has its own styles, so it possible to completely customize it.

See the plugin styles for informations on its concept.

To style a plugin component:

  • Add a styles.scss file in the component directory
  • Require it from the index.js file (import styles from './styles.scss';)
  • Add some styles in the styles.scss file
.wrapper {
+    display: block;
+    background: red;
+    height: 100px;
+    width: 100px;
+}
+

Use this style in the component: <div className={styles.wrapper}></div>.

if you want to use several classes:

import cn from 'classnames';
+import styles from './styles.scss';
+
+// ...
+
+return (
+  <div className={cn(styles.wrapper, styles.otherClass)}>{this.props.children}</div>
+);
+
+// ...
+
+

i18n

React Intl provides React components and an API to format dates, numbers, and strings, including pluralization and handling translations.

Usage

We recommend to set all your components text inside the translations folder.

The example below shows how to use i18n inside your plugin.

Define all your ids with the associated message:

Path — ./plugins/my-plugin/admin/src/translations/en.json.

{
+  "notification.error.message": "An error occurred"
+}
+

Path — ./plugins/my-plugin/admin/src/translations/fr.json

{
+  "notification.error.message": "Une erreur est survenue"
+}
+

Usage inside a component

Path — ./plugins/my-plugin/admin/src/components/Foo/index.js.

import { FormattedMessage } from 'react-intl';
+import SomeOtherComponent from 'components/SomeOtherComponent';
+
+const Foo = (props) => (
+  <div className={styles.foo}>
+    <FormattedMessage id="my-plugin.notification.error.message" />
+    <SomeOtherComponent {...props} />
+  </div>
+)
+
+export default Foo;
+

See the documentation for more extensive usage.

Generators

You can use generators to create React components or containers for your plugin.

  1. In your terminal go to your plugin folder cd plugins/my-plugin
  2. Run npm run generate and choose the type of component your want to create
+ + + diff --git a/docs/.vuepress/dist/3.x.x/plugin-development/frontend-use-cases.html b/docs/.vuepress/dist/3.x.x/plugin-development/frontend-use-cases.html new file mode 100644 index 0000000000..2118a6c2a5 --- /dev/null +++ b/docs/.vuepress/dist/3.x.x/plugin-development/frontend-use-cases.html @@ -0,0 +1,541 @@ + + + + + + Front-end Use Cases | Strapi Docs + + + + + + + +

Front-end Use Cases

This section gives use cases examples on front-end plugin development.

Plugin advanced usage

This section contains advanced resources to develop plugins.

Inject design

The ExtendComponent allows you to inject design from one plugin into another.

Example

Let's say that you want to enable another plugin to inject a component into the top area of your plugin's container called FooPage;

Path — ./plugins/my-plugin/admin/src/containers/FooPage/actions.js.

import {
+  ON_TOGGLE_SHOW_LOREM,
+} from './constants';
+
+export function onToggleShowLorem() {
+  return {
+    type: ON_TOGGLE_SHOW_LOREM,
+  };
+}
+

Path — ./plugins/my-plugin/admin/src/containers/FooPage/index.js.

import React from 'react';
+import { connect } from 'react-redux';
+import { bindActionCreators, compose } from 'redux';
+import { createStructuredSelector } from 'reselect';
+import PropTypes from 'prop-types';
+
+// Import the ExtendComponent
+import ExtendComponent from 'components/ExtendComponent';
+
+// Utils
+import injectReducer from 'utils/injectReducer';
+
+// Actions
+import { onToggleShowLorem } from './action'
+
+import reducer from './reducer';
+
+// Selectors
+import { makeSelectShowLorem } from './selectors';
+
+class FooPage extends React.Component {
+  render() {
+    const lorem = this.props.showLorem ? <p>Lorem ipsum dolor sit amet, consectetur adipiscing</p> : '';
+    return (
+      <div>
+        <h1>This is FooPage container</h1>
+        <ExtendComponent
+          area="top"
+          container="FooPage"
+          plugin="my-plugin"
+          {...props}
+        />
+        {lorem}
+      </div>
+    );
+  }
+}
+
+FooPage.propTypes = {
+  onToggleShowLorem: PropTypes.func.isRequired,
+  showLorem: PropTypes.bool.isRequired,
+};
+
+function mapDispatchToProps(dispatch) {
+  return bindActionCreators(
+    {
+      onToggleShowLorem,
+    },
+    dispatch,
+  );
+}
+
+const mapStateToProps = createStructuredSelector({
+  showLorem: makeSelectShowLorem(),
+});
+
+const withConnect = connect(mapDispatchToProps, mapDispatchToProps);
+const withReducer = injectReducer({ key: 'fooPage', reducer });
+
+export default compose(
+  withReducer,
+  withConnect,
+)(FooPage);
+

Path — ./plugins/my-plugin/admin/src/containers/FooPage/reducer.js.

import { fromJS } from 'immutable';
+import { ON_TOGGLE_SHOW_LOREM } from './constants';
+
+const initialState = fromJS({
+  showLorem: false,
+});
+
+function fooPageReducer(state= initialState, action) {
+  switch (action.type) {
+    case ON_TOGGLE_SHOW_LOREM:
+      return state.set('showLorem', !state.get('showLorem'));
+    default:
+      return state;
+  }
+}
+
+export default fooPageReducer;
+

Path — ./plugins/my-plugin/admin/src/containers/FooPage/selectors.js.

import  { createSelector } from 'reselect';
+
+/**
+* Direct selector to the fooPage state domain
+*/
+
+const selectFooPageDomain = () => state => state.get('fooPage');
+
+/**
+* Other specific selectors
+*/
+
+const makeSelectShowLorem = () => createSelector(
+  selectFooPageDomain(),
+  (substate) => substate.get('showLorem'),
+);
+
+export { makeSelectShowLorem };
+

That's all now your plugin's container is injectable!

Let's see how to inject a React Component from a plugin into another.

Create your injectedComponent

Path - ./plugins/another-plugin/admin/src/extendables/BarContainer/index.js;

import React from 'react';
+import PropTypes from 'prop-types';
+
+// Import our Button component
+import Button from 'components/Button';
+
+// Other imports such as actions, selectors, sagas, reducer...
+
+class BarContainer extends React.Component {
+  render() {
+    return (
+      <div>
+        <Button primary onClick={this.props.onToggleShowLorem}>
+          Click me to show lorem paragraph
+        </Button>
+      </div>
+    );
+  }
+}
+
+BarContainer.propTypes = {
+  onToggleShowLorem: PropTypes.func,
+};
+
+BarContainer.defaultProps = {
+  onToggleShowLorem: () => {},
+};
+
+export default BarContainer;
+

Tell the admin that you want to inject a React Component from a plugin into another

You have to create a file called injectedComponents.js at the root of your another-plugin src folder.

Path — ./plugins/another-plugin/admin/src/injectedComponents.js.

import BarContainer from 'extendables/BarContainer';
+
+// export an array containing all the injected components
+export default [
+  {
+    area: 'top',
+    container: 'FooPage',
+    injectedComponent: BarContainer,
+    plugin: 'my-plugin',
+  },
+];
+

Just by doing so, the another-plugin will add a Button which toggles the lorem paragraph in the FooPage view.


Routeless container store injection

If you have a container which can be a child of several other containers (i.e. it doesn't have a route); you'll have to inject it directly in the ./plugins/my-plugin/admin/src/containers/App/index.js file as follows :

Path — ./plugins/my-plugin/admin/src/containers/App/index.js.

// ...
+import fooReducer from 'containers/Foo/reducer';
+import fooSaga from 'container/Foo/sagas';
+
+import saga from './sagas';
+import { makeSelectFoo } from './selectors';
+
+// ...
+
+export class App extends React.Component {
+  render() {
+    return (
+      <div className={styles.app}>
+        <Switch>
+          {*/ List of all your routes here */}
+        </Switch>
+      </div>
+    );
+  }
+}
+
+// ...
+
+function mapDispatchToProps(dispatch) {
+  return bindActionCreators(
+    {
+    },
+    dispatch
+  );
+}
+
+const mapStateToProps = createStructuredSelector({
+  // ...
+});
+
+const withConnect = connect(mapStateToProps, mapDispatchToProps);
+// Foo reducer
+const withFooReducer = injectReducer({ key: 'foo', reducer: fooReducer });
+// Global reducer
+const withReducer = injectReducer({ key: 'global', reducer });
+// Foo saga
+const withFooSaga = injectSaga({ key: 'foo', saga: fooSaga });
+// Global saga
+const withSaga = injectSaga({ key: 'global', saga });
+
+export default compose(
+  withFooReducer,
+  withReducer,
+  withFooSaga,
+  withSaga,
+  withConnect,
+)(App);
+

Execute logic before mounting the plugin

You can execute a business logic before your plugin is being mounted.

Usage

To do this, you need to create bootstrap.js file at the root of your src plugin's folder. +This file must contains a default functions that returns a Promise.

Example

In this example, we want to populate the left menu with links that will refer to our Content Types.

Path — ./app/plugins/content-manager/admin/src/bootstrap.js.

import { generateMenu } from 'containers/App/sagas';
+
+// This method is executed before the load of the plugin
+const bootstrap = (plugin) => new Promise((resolve, reject) => {
+  generateMenu()
+    .then(menu => {
+      plugin.leftMenuSections = menu;
+
+      resolve(plugin);
+    })
+    .catch(e => reject(e));
+});
+
+export default bootstrap;
+

Prevent plugin rendering

You can prevent your plugin from being rendered if some conditions aren't met.

Usage

To disable your plugin's rendering, you can simply create requirements.js file at the root of your src plugin's folder. +This file must contain a default function that returns a Promise.

Example

Let's say that you want to disable your plugin if the server autoReload config is disabled in development mode.

Path — ./app/config/environments/development/server.json.

{
+  "host": "localhost",
+  "port": 1337,
+  "autoReload": {
+    "enabled": true
+  },
+  "cron": {
+    "enabled": false
+  }
+}
+

You'll first create a request to check if the autoReload config is enabled.

Path — ./app/plugins/my-plugin/config/routes.json.

{
+  "routes": [
+    {
+      "method": "GET",
+      "path": "/autoReload",
+      "handler": "MyPlugin.autoReload",
+      "config": {
+        "policies": []
+      }
+    }
+  ]
+}
+

Then the associated handler:

Path — ./app/plugins/my-plugin/controllers/MyPlugin.js.

const _ = require('lodash');
+const send = require('koa-send');
+
+module.exports = {
+  autoReload: async ctx => {
+    ctx.send({ autoReload: _.get(strapi.config.environments, 'development.server.autoReload', false) });
+  }
+}
+

Finally, you'll create a file called requirements.jsat the root of your plugin's src folder.

The default function exported must return a Promise. +If you wan't to prevent the plugin from being rendered you'll have to set plugin.preventComponentRendering = true;. +In this case, you'll have to set:

plugin.blockerComponentProps = {
+  blockerComponentTitle: 'my-plugin.blocker.title',
+  blockerComponentDescription: 'my-plugin.blocker.description',
+  blockerComponentIcon: 'fa-refresh',
+};
+

To follow the example above:

Path — ./app/plugins/my-plugin/admin/src/requirements.js.

// Use our request helper
+import request from 'utils/request';
+
+const shouldRenderCompo = (plugin) => new Promise((resolve, request) => {
+  request('/my-plugin/autoReload')
+    .then(response => {
+      // If autoReload is enabled the response is `{ autoReload: true }`
+      plugin.preventComponentRendering = !response.autoReload;
+      // Set the BlockerComponent props
+      plugin.blockerComponentProps = {
+        blockerComponentTitle: 'my-plugin.blocker.title',
+        blockerComponentDescription: 'my-plugin.blocker.description',
+        blockerComponentIcon: 'fa-refresh',
+        blockerComponentContent: 'renderIde', // renderIde will add an ide section that shows the development environment server.json config
+      };
+
+      return resolve(plugin);
+    })
+    .catch(err => reject(err));
+});
+
+export default shouldRenderCompo;
+

Customization

You can render your own custom blocker by doing as follows:

Path — ./app/plugins/my-plugin/admin/src/requirements.js.

// Use our request helper
+import request from 'utils/request';
+
+// Your custom blockerComponentProps
+import MyCustomBlockerComponent from 'components/MyCustomBlockerComponent';
+
+const shouldRenderCompo = (plugin) => new Promise((resolve, request) => {
+  request('/my-plugin/autoReload')
+    .then(response => {
+      // If autoReload is enabled the response is `{ autoReload: true }`
+      plugin.preventComponentRendering = !response.autoReload;
+
+      // Tell which component to be rendered instead
+      plugin.blockerComponent = MyCustomBlockerComponent;
+
+      return resolve(plugin);
+    })
+    .catch(err => reject(err));
+});
+
+export default shouldRenderCompo;
+

Using React/Redux and sagas

If your application is going to interact with some back-end application for data, we recommend using redux saga for side effect management. +This short tutorial will show how to fetch data using actions/reducer/sagas.

Constants declaration

Path — ./plugins/my-plugin/admin/src/containers/FooPage/constants.js

export const DATA_FETCH = 'MyPlugin/FooPage/DATA_FETCH';
+export const DATA_FETCH_ERROR = 'MyPlugin/FooPage/DATA_FETCH_ERROR';
+export const DATA_FETCH_SUCCEEDED = 'MyPlugin/FooPage/DATA_FETCH_SUCCEEDED';
+

Actions declaration

Path — ./plugins/my-plugin/admin/src/containers/FooPage/actions.js

import {
+  DATA_FETCH,
+  DATA_FETCH_ERROR,
+  DATA_FETCH_SUCCEEDED,
+} from './constants';
+
+export function dataFetch(params) {
+  return {
+    type: DATA_FETCH,
+    params,
+  };
+}
+
+export function dataFetchError(errorMessage) {
+  return {
+    type: DATA_FETCH_ERROR,
+    errorMessage,
+  };
+}
+
+export function dataFetchSucceeded(data) {
+  return {
+    type: DATA_FETCH_SUCCEEDED,
+    data,
+  };
+}
+

Reducer

We strongly recommend to use Immutable.js to structure your data.

Path — ./plugins/my-plugin/admin/src/containers/FooPage/reducer.js

import { fromJS, Map } from 'immutable';
+import {
+  DATA_FETCH_ERROR,
+  DATA_FETCH_SUCCEEDED,
+} from './constants';
+
+const initialState = fromJS({
+  data: Map({}),
+  error: false,
+  errorMessage: '',
+  loading: true,
+});
+
+function fooPageReducer(state = initialState, action) {
+  switch (action.type) {
+    case DATA_FETCH_ERROR:
+      return state
+        .set('error', true)
+        .set('errorMessage', action.errorMessage)
+        .set('loading', false);
+    case DATA_FETCH_SUCCEEDED:
+      return state
+        .set('data', Map(action.data))
+        .set('error', false)
+        .set('errorMessage', '')
+        .set('loading', false);
+    default:
+      return state;
+  }
+}
+
+export default fooPageReducer;
+

Sagas

Path — ./plugins/my-plugin/admin/src/containers/FooPage/sagas.js

import { LOCATION_CHANGE } from 'react-router-redux';
+import { takeLatest, put, fork, call, take, cancel } from 'redux-saga/effects';
+
+// Use our request helper
+import request from 'utils/request';
+
+// Actions
+import { dataFetchError, dataFetchSucceeded } from './actions';
+import { DATA_FETCH } from './constants';
+
+export function* fetchData(action) {
+  try {
+    const requestUrl = `/baseUrl/${action.params}`;
+    const opts = {
+      method: 'GET',
+    };
+
+    // Fetch data
+    const response = yield call(request, requestUrl, opts);
+
+    // Pass the response to the reducer
+    yield put(dataFetchSucceeded(response));
+
+  } catch(error) {
+    yield put(dataFetchError(error));
+  }
+}
+
+// Individual export for testing
+function* defaultSaga() {
+  // Listen to DATA_FETCH event
+  const fetchDataWatcher = yield fork(takeLatest, DATA_FETCH, fetchData);
+
+  // Cancel watcher
+  yield take(LOCATION_CHANGE);
+
+  yield cancel(fetchDataWatcher);
+}
+
+export default defaultSaga;
+

N.B. You can use a selector in your sagas :

import { put, select, fork, call, take, cancel } from 'redux-saga/effects';
+import { makeSelectUserName } from './selectors';
+
+export function* foo() {
+  try {
+    const userName = yield select(makeSelectUserName());
+
+    // ...
+  } catch(error) {
+    // ...
+  }
+}
+
+function defaultSaga() {
+  // ...
+}
+
+export default defaultSaga;
+

Selectors

Reselect is a library used for slicing your redux state and providing only the relevant sub-tree to a react component. It has three key features:

  1. Computational power
  2. Memoization
  3. Composability

Creating a selector:

Path — ./plugins/my-plugin/admin/src/containers/FooPage/selectors.js

import { createSelector } from 'reselect';
+
+/**
+* Direct selector to the fooPage state domain
+*/
+const selectFooPageDomain = () => state => state.get('fooPage');
+
+/**
+ * Other specific selectors
+ */
+
+ const makeSelectLoading = () => createSelector(
+   selectFooPageDomain(),
+   (substate) => substate.get('loading'),
+ );
+
+/**
+ * Default selector used by FooPage
+ */
+
+const selectFooPage = () => createSelector(
+  selectFooDomain(),
+  (substate) => substate.toJS()
+);
+
+export default selectFooPage;
+export { makeSelectLoading };
+
+

Example

Path — ./plugins/my-plugin/admin/src/containers/FooPage/index.js

import React from 'react';
+import { bindActionCreators } from 'redux';
+import { connect, compose } from 'react-redux';
+import PropTypes from 'prop-types';
+
+// Main router
+import { router } from 'app';
+
+// Utils
+import injectSaga from 'utils/injectSaga';
+import injectReducer from 'utils/injectReducer';
+
+// Actions
+import { dataFetch } from './actions';
+// sagas
+import saga from './sagas';
+// Selectors
+import selectFooPage from './selectors';
+// Reducer
+import reducer from './reducer';
+
+export class FooPage extends React.Component {
+  componentWillReceiveProps(nextProps) {
+    if (this.props.error !== nextProps.error && nextProps.error) {
+      strapi.notification.error(nextProps.errorMessage);
+    }
+  }
+
+  componentDidUpdate(prevProps) {
+    if (prevProps.match.pathname !== this.props.pathname) {
+      this.props.dataFetch(this.props.match.params.bar);
+    }
+  }
+
+  render() {
+    if (this.props.error) return <div>An error occurred</div>;
+
+    return (
+      <div>
+        <h4>Data display</h4>
+        <span>{this.props.data.foo}</span>
+        <span>{this.props.data.bar}</span>
+      </div>
+    );
+  }
+
+  FooPage.propTypes = {
+    data: PropTypes.object.isRequired,
+    dataFetch: PropTypes.func.isRequired,
+    error: PropTypes.bool.isRequired,
+    errorMessage: PropTypes.string.isRequired,
+    match: PropTypes.object.isRequired,
+  };
+
+  const mapStateToProps = selectFoo();
+
+  function mapDispatchToProps(dispatch) {
+    return bindActionCreators(
+      {
+        dataFetch,
+      },
+      dispatch
+    );
+  }
+
+  const withConnect = connect(mapStateToProps, mapDispatchToProps);
+  const withReducer = injectReducer({ key: 'fooPage', reducer });
+  const withSagas = injectSaga({ key: 'fooPage', saga });
+
+  export default compose(
+    withReducer,
+    withSagas,
+    withConnect,
+  )(FooPage);
+}
+
+ + + diff --git a/docs/.vuepress/dist/3.x.x/plugin-development/plugin-architecture.html b/docs/.vuepress/dist/3.x.x/plugin-development/plugin-architecture.html new file mode 100644 index 0000000000..a431458bf2 --- /dev/null +++ b/docs/.vuepress/dist/3.x.x/plugin-development/plugin-architecture.html @@ -0,0 +1,55 @@ + + + + + + Plugin Folders and Files Architecture | Strapi Docs + + + + + + + +

Plugin Folders and Files Architecture

The logic of a plugin is located at his root directory ./plugins/**. The admin panel related parts of each plugin is contained in the /admin folder. +The folders and files structure is the following:

/plugin
+└─── admin // Contains the plugin's front-end
+|     └─── build // Webpack build of the plugin
+|     └─── src // Source code directory
+|          └─── bootstrap.js // (Optional) Contains the logic to execute before rendering the plugin
+|          └─── components // Contains the list of React components used by the plugin
+|          └─── containers
+|          |    └─── App // Container used by every others containers
+|          |    └─── HomePage
+|          |         └─── action.js // List of Redux actions used by the current container
+|          |         └─── constants.js // List of actions constants
+|          |         └─── index.js // React component of the current container
+|          |         └─── reducer.js // Redux reducer used by the current container
+|          |         └─── sagas.js // List of sagas functions
+|          |         └─── selectors.js // List of selectors
+|          |         └─── styles.scss // Style of the current container
+|          |
+|          └─── requirements.js // (Optional) Contains the logic to prevent a plugin from being rendered
+|          └─── translations // Contains the translations to make the plugin internationalized
+|               └─── en.json
+|               └─── fr.json
+└─── config // Contains the configurations of the plugin
+|     └─── functions
+|     |    └─── bootstrap.js // Asynchronous bootstrap function that runs before the app gets started
+|     └─── policies // Folder containing the plugin's policies
+|     └─── queries // Folder containing the plugin's models queries
+|     └─── routes.json // Contains the plugin's API routes
+└─── controllers // Contains the plugin's API controllers
+└─── middlewares // Contains the plugin's middlewares
+└─── models // Contains the plugin's API models
+└─── services // Contains the plugin's API services
+
+ + + diff --git a/docs/.vuepress/dist/3.x.x/plugin-development/plugin-left-menu.html b/docs/.vuepress/dist/3.x.x/plugin-development/plugin-left-menu.html new file mode 100644 index 0000000000..42e9a25f9c --- /dev/null +++ b/docs/.vuepress/dist/3.x.x/plugin-development/plugin-left-menu.html @@ -0,0 +1,135 @@ + + + + + + Plugin Menu library | Strapi Docs + + + + + + + +

Plugin Menu library

// ...
+
+import PluginLeftMenu from 'components/PluginLeftMenu';
+
+// ...
+
+const Foo = (props) => {
+  const sections = [
+    {
+      name: 'section 1',
+      items: [
+        { icon: 'fa-caret-square-o-right', name: 'link 1'},
+        { icon: 'fa-caret-square-o-right', name: 'link 2'},
+      ],
+    },
+  ];
+
+  return (
+    <div className={styles.foo}>
+      <div className="container-fluid">
+        <div className="row">
+          <PluginLeftMenu
+            sections={sections}
+          />
+        </div>
+      </div>
+    </div>
+  );
+}
+
+export default Foo;
+
+// ...
+

Usage

Property Type Required Description
addCustomSection function no Allows to add another section after the initial one.
basePath string yes For example the basePath of the route '/plugins/my-plugin/foo/bar' is 'my-plugin/categories'
renderCustomLink function no Allows to override the design and the behavior of a link
sections array yes Sections of the component menu

Example

// ...
+
+import PluginLeftMenu from 'components/PluginLeftMenu';
+
+// ...
+
+const addCustomSection = (sectionStyles) => (
+  // You have access to the section styles
+  <div className={sectionStyles.pluginLeftMenuSection}>
+    <p>
+      DOCUMENTATION
+    </p>
+    <ul>
+      <li>
+        Read more about strapi in our <a href="http://strapi.io/documentation" target="_blank">documentation</a>
+      </li>
+    </ul>
+  </div>
+)
+
+const renderAddLink = (props, customLinkStyles) => (
+  <li className={customLinkStyles.pluginLeftMenuLink}>
+    <div className={`${customLinkStyles.liInnerContainer}`} onClick={this.handleAddLinkClick}>
+      <div>
+        <i className={`fa ${props.link.icon}`} />
+      </div>
+      <span>{props.link.name}</span>
+    </div>
+  </li>
+)
+
+const renderCustomLink = (props, linkStyles) => {
+  if (props.link.name === 'bar') return this.renderAddLink(props, linkStyles);
+
+  return (
+    <li className={linkStyles.pluginLeftMenuLink}>
+      <NavLink className={linkStyles.link} to={`/plugins/my-plugin/foo/${props.link.name}`} activeClassName={linkStyles.linkActive}>
+        <div>
+          <i className={`fa fa-caret-square-o-right`} />
+        </div>
+        <div className={styles.contentContainer}>
+          <span className={spanStyle}>{props.link.name}</span>
+        </div>
+
+      </NavLink>
+    </li>
+  );
+}
+
+const Foo = (props) => {
+  const sections = [
+    {
+      name: 'section 1',
+      items: [
+        { icon: 'fa-caret-square-o-right', name: 'link 1'},
+        { icon: 'fa-caret-square-o-right', name: 'link 2'},
+      ],
+    },
+  ];
+
+  return (
+    <div className={styles.foo}>
+      <div className="container-fluid">
+        <div className="row">
+          <PluginLeftMenu
+            addCustomSection={addCustomSection}
+            sections={sections}
+            renderCustomLink={renderCustomLink}
+            basePath="my-plugins/foo"
+          />
+        </div>
+      </div>
+    </div>
+  );
+}
+
+// ...
+
+export default Foo;
+
+ + + diff --git a/docs/.vuepress/dist/3.x.x/plugin-development/quick-start.html b/docs/.vuepress/dist/3.x.x/plugin-development/quick-start.html new file mode 100644 index 0000000000..6ceafb403b --- /dev/null +++ b/docs/.vuepress/dist/3.x.x/plugin-development/quick-start.html @@ -0,0 +1,23 @@ + + + + + + Quick start | Strapi Docs + + + + + + + +

Quick start

To facilitate the development of a plugin, we drastically reduce the amount of commands necessary to install the entire development environment. Before getting started, you need to have Node.js (v8) and npm (v5) installed.

Development Environment Setup

To setup the development environment please follow the instructions below:

  1. Fork the repository to your own GitHub account.
  2. Clone it to your computer git clone git@github.com:strapi/strapi.git.
  3. Run npm run setup at the root of the directory.

You can run npm run setup:build to build the plugins' admin (the setup time will be longer)

If the installation failed, please remove the global packages related to Strapi. The command npm ls strapi will help you to find where your packages are installed globally.

Plugin development Setup

Create a development project

  1. Go to a folder on your computer cd /path/to/my/folder.
  2. Create a new project strapi new myDevelopmentProject --dev.

To generate a new plugin run the following commands:

  1. In your project folder cd myDevelopmentProject && strapi generate:plugin my-plugin.
  2. Link the strapi-helper-plugin dependency in your project folder cd pathToMyProject/myDevelopmentProject/plugins/my-plugin && npm link strapi-helper-plugin.
  3. Link the strapi-helper-plugin dependency in the analytics plugin folder cd pathToMyProject/myDevelopmentProject/plugins/analytics && npm link strapi-helper-plugin.
  4. Start the server in the admin folder cd pathToMyProject/myDevelopmentProject/admin && npm start and go to the following url http://localhost:4000/admin.
  5. In a new terminal window open at the root of your project launch your Strapi server strapi start.

Your are now ready to develop your own plugin and live-test your updates!

+ + + diff --git a/docs/.vuepress/dist/3.x.x/plugin-development/ui-components.html b/docs/.vuepress/dist/3.x.x/plugin-development/ui-components.html new file mode 100644 index 0000000000..75c6dfdabe --- /dev/null +++ b/docs/.vuepress/dist/3.x.x/plugin-development/ui-components.html @@ -0,0 +1,23 @@ + + + + + + Strapi Docs + + + + + + + +
+ + + diff --git a/docs/.vuepress/dist/3.x.x/plugin-development/utils.html b/docs/.vuepress/dist/3.x.x/plugin-development/utils.html new file mode 100644 index 0000000000..f23857dccc --- /dev/null +++ b/docs/.vuepress/dist/3.x.x/plugin-development/utils.html @@ -0,0 +1,356 @@ + + + + + + Helpers | Strapi Docs + + + + + + + +

Helpers

Strapi provides helpers so you don't have to develop again and again the same generic functions.

Auth

auth.js lets you get, set and delete data in either the browser's localStorage or sessionStorage.

Methods

Name Description
clear(key) Remove the data in either localStorage or sessionStorage
clearAppStorage() Remove all data from both storage
clearToken() Remove the user's jwt Token in the appropriate browser's storage
clearUserInfo() Remove the user's info from storage
get(key) Get the item in the browser's storage
getToken() Get the user's jwtToken
getUserInfo() Get the user's infos
set(value, key, isLocalStorage) Set an item in the sessionStorage. If true is passed as the 3rd parameter it sets the value in the localStorage
setToken(value, isLocalStorage) Set the user's jwtToken in the sessionStorage. If true is passed as the 2nd parameter it sets the value in the localStorage
setUserInfo(value, isLocalStorage) Set the user's info in the sessionStorage. If true is passed as the 2nd parameter it sets the value in the localStorage
import auth from 'utils/auth';
+
+// ...
+//
+auth.setToken('12345', true); // This will set 1234 in the browser's localStorage associated with the key: jwtToken
+

Colors

This function allows to darken a color.

Usage

import { darken } from 'utils/colors';
+
+const linkColor = darken('#f5f5f5', 1.5); // Will darken #F5F5F5 by 1.5% which gives #f2f2f2.
+

Get URL Query Parameters

The helpers allows to retrieve the query parameters in the URL.

Example

import getQueryParameters from 'utils/getQueryParameters';
+
+const URL = '/create?source=users-permissions';
+const source = getQueryParameters(URL, 'source');
+
+console.log(source); // users-permissions
+
+

Request helper

A request helper is available to handle all requests inside a plugin.

It takes three arguments:

  • requestUrl: The url we want to fetch.
  • options: Please refer to this documentation.
  • true: This third argument is optional. If true is passed the response will be sent only if the server has restarted check out the example.

Usage

Path - /plugins/my-plugin/admin/src/containers/**/sagas.js.

import { call, fork, put, takeLatest } from 'redux-saga/effects';
+
+// Our request helper
+import request from 'utils/request';
+import { dataFetchSucceeded, dataFetchError } from './actions';
+import { DATA_FETCH } from './constants';
+
+export function* fetchData(action) {
+  try {
+    const opts = {
+      method: 'GET',
+    };
+    const requestUrl = `/my-plugin/${action.endPoint}`;
+    const data = yield call(request, requestUrl, opts);
+
+    yield put(dataFetchSucceeded(data));
+  } catch(error) {
+    yield put(dataFetchError(error))
+  }
+}
+
+// Individual exports for testing
+function* defaultSaga() {
+  yield fork(takeLatest, DATA_FETCH, fetchData);
+}
+
+export default defaultSaga;
+

Simple example

Let's say that we have a container that fetches Content Type configurations depending on URL change.

Routing declaration:

Here we want to create a route /content-type/:contentTypeName for the ContentTypePage container.

Path — ./plugins/my-plugin/admin/src/container/App/index.js.

import React from 'react';
+import { connect } from 'react-redux';
+import { bindActionCreators, compose } from 'redux';
+
+import { createStructuredSelector } from 'reselect';
+import { Switch, Route, withRouter } from 'react-router-dom';
+import PropTypes from 'prop-types';
+import { pluginId } from 'app';
+
+import ContentTypePage from 'containers/ContentTypePage';
+import styles from './styles.scss';
+
+class App extends React.Component {
+  render() {
+    return (
+      <div className={`${pluginId} ${styles.app}`}>
+        <Switch>
+          <Route exact path="/plugins/my-plugin/content-type/:contentTypeName" component={ContentTypePage} />
+        </Switch>
+      </div>
+    );
+  }
+}
+
+App.contextTypes = {
+  router: PropTypes.object.isRequired,
+};
+
+export function mapDispatchToProps(dispatch) {
+  return bindActionCreators(
+    {},
+    dispatch
+  );
+}
+
+const mapStateToProps = createStructuredSelector({});
+const withConnect = connect(mapStateToProps, mapDispatchToProps);
+
+export default compose(
+  withConnect,
+)(App);
+
+

Constants declaration:

Let's declare the needed constants to handle fetching data:

Path — ./plugins/my-plugin/admin/src/containers/ContentTypePage/constants.js.

export const DATA_FETCH = 'myPlugin/ContentTypePage/DATA_FETCH';
+export const DATA_FETCH_ERROR = 'myPlugin/ContentTypePage/DATA_FETCH_ERROR';
+export const DATA_FETCH_SUCCEEDED = 'myPlugin/ContentTypePage/DATA_FETCH_SUCCEEDED';
+

Actions declaration:

Let's declare our actions.

Path — ./plugins/my-plugin/admin/src/containers/ContentTypePage/actions.js.

import {
+  DATA_FETCH,
+  DATA_FETCH_ERROR,
+  DATA_FETCH_SUCCEEDED,
+} from './constants';
+
+export function dataFetch(contentTypeName) {
+  return {
+    type: DATA_FETCH,
+    contentTypeName,
+  };
+}
+
+export function dataFetchError(errorMessage) {
+  return {
+    type: DATA_FETCH_ERROR,
+    errorMessage,
+  };
+}
+
+export function dataFetchSucceeded(data) {
+  // data will look like { data: { name: 'User', description: 'Some description' } }
+  return {
+    type: DATA_FETCH_SUCCEEDED,
+    data,
+  };
+}
+

Reducer setup:

Please refer to the Immutable documentation for informations about data structure.

Path — ./plugins/my-plugin/admin/src/containers/ContentTypePage/reducer.js.

import { fromJS, Map } from 'immutable';
+import {
+  DATA_FETCH,
+  DATA_FETCH_ERROR,
+  DATA_FETCH_SUCCEEDED
+} from './constants';
+
+const initialState = fromJS({
+  contentTypeName,
+  error: false,
+  errorMessage: '',
+  data: Map({}),
+});
+
+function contentTypePageReducer(state = initialState, action) {
+  switch (action.type) {
+    case DATA_FETCH:
+      return state.set('contentTypeName', action.contentTypeName);
+    case DATA_FETCH_ERROR:
+      return state
+        .set('error', true)
+        .set('errorMessage', action.errorMessage);
+    case DATA_FETCH_SUCCEEDED:
+      return state
+        .set('error', false)
+        .set('data', Map(action.data.data));
+    default:
+      return state;
+  }
+}
+
+export default contentTypePageReducer;
+

Selectors setup:

Path — ./plugins/my-plugin/admin/src/containers/ContentTypePage/selectors.js.

import { createSelector } from 'reselect';
+
+/**
+ * Direct selector to the contentTypePage state domain
+ */
+const selectContentTypePageDomain = () => state => state.get('contentTypePage');
+
+/**
+ * Other specific selectors
+ */
+
+
+/**
+ * Default selector used by ContentTypePage
+ */
+
+const selectContentTypePage = () => createSelector(
+  selectContentTypePageDomain(),
+  (substate) => substate.toJS()
+);
+
+const makeSelectContentTypeName = () => createSelector(
+  selectContentTypePageDomain(),
+  (substate) => substate.get('contentTypeName');
+)
+export default selectContentTypePage;
+export { makeSelectContentTypeName, selectContentTypePageDomain };
+

Handling route change:

Path — ./plugins/my-plugin/admin/src/containers/ContentTypePage/index.js.

import React from 'react';
+import { connect } from 'react-redux';
+import { createStructuredSelector } from 'reselect';
+import { bindActionCreators, compose } from 'redux';
+import { NavLink } from 'react-router-dom';
+import PropTypes from 'prop-types';
+import { map } from 'lodash';
+
+// Utils to create the container's store
+import injectSaga from 'utils/injectSaga';
+import injectReducer from 'utils/injectReducer';
+
+import { dataFetch } from './actions';
+import { selectContentTypePage } from './selectors';
+import saga from './sagas';
+import reducer from './reducer';
+import styles from './styles.scss';
+
+export class ContentTypePage extends React.Component { // eslint-disable-line react/prefer-stateless-function
+  constructor(props) {
+    super(props);
+
+    this.links = [
+      {
+        to: 'plugin/my-plugin/content-type/product',
+        info: 'Product',
+      },
+      {
+        to: 'plugin/my-plugin/content-type/user',
+        info: 'User',
+      },
+    ];
+  }
+
+  componentDidMount() {
+    this.props.dataFetch(this.props.match.params.contentTypeName);
+  }
+
+  componentWillReceiveProps(nextProps) {
+    if (nextProps.match.params.contentTypeName !== this.props.match.params.contentTypeName) {
+      this.props.dataFetch(nextProps.match.params.contentTypeName);
+    }
+  }
+
+  render() {
+    return (
+      <div className={styles.contentTypePage}>
+        <div>
+          <ul>
+            {map(this.links, (link, key) => (
+              <li key={key}>
+                <NavLink to={link.to}>{link.info}</NavLink>
+              </li>
+            ))}
+          </ul>
+        </div>
+        <div>
+          <h1>{this.props.data.name}</h1>
+          <p>{this.props.data.description}</p>
+        </div>
+      </div>
+    );
+  }
+}
+
+const mapStateToProps = selectContentTypePage();
+
+function mapDispatchToProps(dispatch) {
+  return bindActionCreators(
+    {
+      dataFetch,
+    },
+    dispatch,
+  );
+}
+
+ContentTypePage.propTypes = {
+  data: PropTypes.object.isRequired,
+  dataFetch: PropTypes.func.isRequired,
+  match: PropTypes.object.isRequired,
+};
+
+const withConnect = connect(mapStateToProps, mapDispatchToProps);
+const withSaga = injectSaga({ key: 'contentTypePage', saga });
+const withReducer = injectReducer({ key: 'contentTypePage', reducer });
+
+export default compose(
+  withReducer,
+  withSaga,
+  withConnect,
+)(ContentTypePage);
+

Fetching data:

The sagas.js file is in charge of fetching data.

Path — ./plugins/my-plugin/admin/src/containers/ContentTypePage/sagas.js.

import { LOCATION_CHANGE } from 'react-router-redux';
+import { takeLatest, call, take, put, fork, cancel, select } from 'redux-saga/effects';
+import request from 'utils/request';
+import {
+  dataFetchError,
+  dataFetchSucceeded,
+} from './actions';
+import { DATA_FETCH } from './constants';
+import { makeSelectContentTypeName } from './selectors';
+
+export function* fetchData() {
+  try {
+    const opts = { method: 'GET' };
+
+    // To make a POST request { method: 'POST', body: {Object} }
+
+    const endPoint = yield select(makeSelectContentTypeName());
+    const requestUrl = `my-plugin/**/${endPoint}`;
+
+    // Fetching data with our request helper
+    const data = yield call(request, requestUrl, opts);
+    yield put(dataFetchSucceeded(data));
+  } catch(error) {
+    yield put(dataFetchError(error.message));
+  }
+}
+
+function* defaultSaga() {
+  const loadDataWatcher = yield fork(takeLatest, DATA_FETCH, fetchData);
+
+  yield take(LOCATION_CHANGE);
+  yield cancel(loadDataWatcher);
+}
+
+export default defaultSaga;
+

Example with server autoReload watcher

Let's say that you want to develop a plugin that needs server restart on file change (like the settings-manager plugin) and you want to be aware of that to display some stuff..., you just have to send a third argument: true to our request helper and it will ping a dedicated route and send the response when the server has restarted.

Path — ./plugins/my-plugin/admin/src/containers/**/sagas.js.

import { takeLatest, call, take, put, fork, cancel, select } from 'redux-saga/effects';
+import request from 'utils/request';
+import {
+  submitSucceeded,
+  submitError,
+} from './actions';
+import { SUBMIT } from './constants';
+// Other useful imports like selectors...
+// ...
+
+export function* postData() {
+  try {
+    const body = { data: 'someData' };
+    const opts = { method: 'POST', body };
+    const requestUrl = `**yourUrl**`;
+
+    const response = yield call(request, requestUrl, opts, true);
+
+    if (response.ok) {
+      yield put(submitSucceeded());      
+    } else {
+      yield put(submitError('An error occurred'));
+    }
+  } catch(error) {
+    yield put(submitError(error.message));
+  }
+}
+
+function* defaultSaga() {
+  yield fork(takeLatest, SUBMIT, postData);
+  // ...
+}
+
+export default defaultSaga;
+
+ + + diff --git a/docs/.vuepress/dist/3.x.x/tutorials/index.html b/docs/.vuepress/dist/3.x.x/tutorials/index.html new file mode 100644 index 0000000000..a5cd13fbe5 --- /dev/null +++ b/docs/.vuepress/dist/3.x.x/tutorials/index.html @@ -0,0 +1,26 @@ + + + + + + Tutorials | Strapi Docs + + + + + + + +
+ + + diff --git a/docs/.vuepress/dist/404.html b/docs/.vuepress/dist/404.html new file mode 100644 index 0000000000..0df28f2f35 --- /dev/null +++ b/docs/.vuepress/dist/404.html @@ -0,0 +1,17 @@ + + + + + + Strapi Docs + + + + + + + +

404

There's nothing here.
Take me home.
+ + + diff --git a/docs/.vuepress/dist/assets/css/1.styles.77d89b12.css b/docs/.vuepress/dist/assets/css/1.styles.77d89b12.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/.vuepress/dist/assets/css/2.styles.08038ddb.css b/docs/.vuepress/dist/assets/css/2.styles.08038ddb.css new file mode 100644 index 0000000000..4cbd7d26ef --- /dev/null +++ b/docs/.vuepress/dist/assets/css/2.styles.08038ddb.css @@ -0,0 +1 @@ +.badge[data-v-099ab69c]{display:inline-block;font-size:14px;height:18px;line-height:18px;border-radius:3px;padding:0 6px;color:#fff;margin-right:5px;background-color:#42b983}.badge.middle[data-v-099ab69c]{vertical-align:middle}.badge.top[data-v-099ab69c]{vertical-align:top}.badge.green[data-v-099ab69c],.badge.tip[data-v-099ab69c]{background-color:#42b983}.badge.error[data-v-099ab69c]{background-color:#da5961}.badge.warn[data-v-099ab69c],.badge.warning[data-v-099ab69c],.badge.yellow[data-v-099ab69c]{background-color:#e7c000} \ No newline at end of file diff --git a/docs/.vuepress/dist/assets/css/styles.a8210063.css b/docs/.vuepress/dist/assets/css/styles.a8210063.css new file mode 100644 index 0000000000..c5cfa7df71 --- /dev/null +++ b/docs/.vuepress/dist/assets/css/styles.a8210063.css @@ -0,0 +1 @@ +.home{padding:3.6rem 2rem 0;max-width:960px;margin:0 auto}.home .hero{text-align:center}.home .hero img{max-height:280px;display:block;margin:3rem auto 1.5rem}.home .hero h1{font-size:3rem}.home .hero .action,.home .hero .description,.home .hero h1{margin:1.8rem auto}.home .hero .description{max-width:35rem;font-size:1.6rem;line-height:1.3;color:#6a8bad}.home .hero .action-button{display:inline-block;font-size:1.2rem;color:#fff;background-color:#2f80ed;padding:.8rem 1.6rem;border-radius:4px;transition:background-color .1s ease;box-sizing:border-box;border-bottom:1px solid #1570eb}.home .hero .action-button:hover{background-color:#448def}.home .features{border-top:1px solid #eaecef;padding:1.2rem 0;margin-top:2.5rem;display:flex;flex-wrap:wrap;align-items:flex-start;align-content:stretch;justify-content:space-between}.home .feature{flex-grow:1;flex-basis:30%;max-width:30%}.home .feature h2{font-size:1.4rem;font-weight:500;border-bottom:none;padding-bottom:0;color:#3a5169}.home .feature p{color:#4e6e8e}.home .footer{padding:2.5rem;border-top:1px solid #eaecef;text-align:center;color:#4e6e8e}@media (max-width:719px){.home .features{flex-direction:column}.home .feature{max-width:100%;padding:0 2.5rem}}@media (max-width:419px){.home{padding-left:1.5rem;padding-right:1.5rem}.home .hero img{max-height:210px;margin:2rem auto 1.2rem}.home .hero h1{font-size:2rem}.home .hero .action,.home .hero .description,.home .hero h1{margin:1.2rem auto}.home .hero .description{font-size:1.2rem}.home .hero .action-button{font-size:1rem;padding:.6rem 1.2rem}.home .feature h2{font-size:1.25rem}}.sidebar-button{display:none;width:1.25rem;height:1.25rem;position:absolute;padding:.6rem;top:.6rem;left:1rem}.sidebar-button .icon{display:block;width:1.25rem;height:1.25rem}@media (max-width:719px){.sidebar-button{display:block}}.search-box{display:inline-block;position:relative;margin-right:.5rem}.search-box input{cursor:text;width:10rem;color:#4e6e8e;display:inline-block;border:1px solid #cfd4db;border-radius:2rem;font-size:.9rem;line-height:2rem;padding:0 .5rem 0 2rem;outline:none;transition:all .2s ease;background:#fff url(/documentation/assets/img/search.83621669.svg) .6rem .5rem no-repeat;background-size:1rem}.search-box input:focus{cursor:auto;border-color:#2f80ed}.search-box .suggestions{background:#fff;width:20rem;position:absolute;top:1.5rem;border:1px solid #cfd4db;border-radius:6px;padding:.4rem;list-style-type:none}.search-box .suggestions.align-right{right:0}.search-box .suggestion{line-height:1.4;padding:.4rem .6rem;border-radius:4px;cursor:pointer}.search-box .suggestion a{color:#5d82a6}.search-box .suggestion a .page-title{font-weight:600}.search-box .suggestion a .header{font-size:.9em;margin-left:.25em}.search-box .suggestion.focused{background-color:#f3f4f5}.search-box .suggestion.focused a{color:#2f80ed}@media (max-width:959px){.search-box input{cursor:pointer;width:0;border-color:transparent;position:relative;left:1rem}.search-box input:focus{cursor:text;left:0;width:10rem}}@media (max-width:959px) and (min-width:719px){.search-box .suggestions{left:0}}@media (max-width:719px){.search-box{margin-right:0}.search-box .suggestions{right:0}}@media (max-width:419px){.search-box .suggestions{width:calc(100vw - 4rem)}.search-box input:focus{width:8rem}}.dropdown-enter,.dropdown-leave-to{height:0!important}.dropdown-wrapper .dropdown-title{display:block}.dropdown-wrapper .dropdown-title:hover{border-color:transparent}.dropdown-wrapper .dropdown-title .arrow{vertical-align:middle;margin-top:-1px;margin-left:.4rem}.dropdown-wrapper .nav-dropdown .dropdown-item{color:inherit;line-height:1.7rem}.dropdown-wrapper .nav-dropdown .dropdown-item h4{margin:.45rem 0 0;border-top:1px solid #eee;padding:.45rem 1.5rem 0 1.25rem}.dropdown-wrapper .nav-dropdown .dropdown-item .dropdown-subitem-wrapper{padding:0;list-style:none}.dropdown-wrapper .nav-dropdown .dropdown-item .dropdown-subitem-wrapper .dropdown-subitem{font-size:.9em}.dropdown-wrapper .nav-dropdown .dropdown-item a{display:block;line-height:1.7rem;position:relative;border-bottom:none;font-weight:400;margin-bottom:0;padding:0 1.5rem 0 1.25rem}.dropdown-wrapper .nav-dropdown .dropdown-item a.router-link-active,.dropdown-wrapper .nav-dropdown .dropdown-item a:hover{color:#2f80ed}.dropdown-wrapper .nav-dropdown .dropdown-item a.router-link-active:after{content:"";width:0;height:0;border-left:5px solid #2f80ed;border-top:3px solid transparent;border-bottom:3px solid transparent;position:absolute;top:calc(50% - 2px);left:9px}.dropdown-wrapper .nav-dropdown .dropdown-item:first-child h4{margin-top:0;padding-top:0;border-top:0}@media (max-width:719px){.dropdown-wrapper.open .dropdown-title{margin-bottom:.5rem}.dropdown-wrapper .nav-dropdown{transition:height .1s ease-out;overflow:hidden}.dropdown-wrapper .nav-dropdown .dropdown-item h4{border-top:0;margin-top:0;padding-top:0}.dropdown-wrapper .nav-dropdown .dropdown-item>a,.dropdown-wrapper .nav-dropdown .dropdown-item h4{font-size:15px;line-height:2rem}.dropdown-wrapper .nav-dropdown .dropdown-item .dropdown-subitem{font-size:14px;padding-left:1rem}}@media (min-width:719px){.dropdown-wrapper{height:1.8rem}.dropdown-wrapper:hover .nav-dropdown{display:block!important}.dropdown-wrapper .dropdown-title .arrow{border-left:4px solid transparent;border-right:4px solid transparent;border-top:6px solid #ccc;border-bottom:0}.dropdown-wrapper .nav-dropdown{display:none;height:auto!important;box-sizing:border-box;max-height:calc(100vh - 2.7rem);overflow-y:auto;position:absolute;top:100%;right:0;background-color:#fff;padding:.6rem 0;border:1px solid #ddd;border-bottom-color:#ccc;text-align:left;border-radius:.25rem;white-space:nowrap;margin:0}}.nav-links{display:inline-block}.nav-links a{line-height:1.4rem;color:inherit}.nav-links a.router-link-active,.nav-links a:hover{color:#2f80ed}.nav-links .nav-item{cursor:pointer;position:relative;display:inline-block;margin-left:1.5rem;line-height:2rem}.nav-links .repo-link{margin-left:1.5rem}@media (max-width:719px){.nav-links .nav-item,.nav-links .repo-link{margin-left:0}}@media (min-width:719px){.nav-links a.router-link-active,.nav-links a:hover{color:#2c3e50}.nav-item>a:not(.external).router-link-active,.nav-item>a:not(.external):hover{margin-bottom:-2px;border-bottom:2px solid #408aee}}.navbar{padding:.7rem 1.5rem;line-height:2.2rem;position:relative}.navbar a,.navbar img,.navbar span{display:inline-block}.navbar .logo{height:2.2rem;min-width:2.2rem;margin-right:.8rem;vertical-align:top}.navbar .site-name{font-size:1.3rem;font-weight:600;color:#2c3e50;position:relative}.navbar .links{font-size:.9rem;position:absolute;right:1.5rem;top:.7rem}@media (max-width:719px){.navbar{padding-left:4rem}.navbar .can-hide{display:none}}.page-edit,.page-nav{max-width:740px;margin:0 auto;padding:2rem 2.5rem}@media (max-width:959px){.page-edit,.page-nav{padding:2rem}}@media (max-width:419px){.page-edit,.page-nav{padding:1.5rem}}.page{padding-bottom:2rem}.page-edit{padding-top:1rem;padding-bottom:1rem;overflow:auto}.page-edit .edit-link{display:inline-block}.page-edit .edit-link a{color:#4e6e8e;margin-right:.25rem}.page-edit .last-updated{float:right;font-size:.9em}.page-edit .last-updated .prefix{font-weight:500;color:#4e6e8e}.page-edit .last-updated .time{font-weight:400;color:#aaa}.page-nav{padding-top:1rem;padding-bottom:0}.page-nav .inner{min-height:2rem;margin-top:0;border-top:1px solid #eaecef;padding-top:1rem;overflow:auto}.page-nav .next{float:right}@media (max-width:719px){.page-edit .edit-link{margin-bottom:.5rem}.page-edit .last-updated{font-size:.8em;float:none;text-align:left}}.sidebar .sidebar-sub-headers{padding-left:1rem;font-size:.95em}a.sidebar-link{font-weight:400;display:inline-block;color:#2c3e50;border-left:.25rem solid transparent;padding:.35rem 1rem .35rem 1.25rem;line-height:1.4;width:100%;box-sizing:border-box}a.sidebar-link:hover{color:#2f80ed}a.sidebar-link.active{font-weight:600;color:#2f80ed;border-left-color:#2f80ed}.sidebar-group a.sidebar-link{padding-left:2rem}.sidebar-sub-headers a.sidebar-link{padding-top:.25rem;padding-bottom:.25rem;border-left:none}.sidebar-sub-headers a.sidebar-link.active{font-weight:500}.sidebar-group:not(.first){margin-top:1em}.sidebar-group .sidebar-group{padding-left:.5em}.sidebar-group:not(.collapsable) .sidebar-heading{cursor:auto;color:inherit}.sidebar-heading{color:#999;transition:color .15s ease;cursor:pointer;font-size:1.1em;font-weight:700;padding:0 1.5rem;margin-top:0;margin-bottom:.5rem}.sidebar-heading.open,.sidebar-heading:hover{color:inherit}.sidebar-heading .arrow{position:relative;top:-.12em;left:.5em}.sidebar-heading:.open .arrow{top:-.18em}.sidebar-group-items{transition:height .1s ease-out;overflow:hidden}.sidebar ul{padding:0;margin:0;list-style-type:none}.sidebar a{display:inline-block}.sidebar .nav-links{display:none;border-bottom:1px solid #eaecef;padding:.5rem 0 .75rem 0}.sidebar .nav-links a{font-weight:600}.sidebar .nav-links .nav-item,.sidebar .nav-links .repo-link{display:block;line-height:1.25rem;font-size:1.1em;padding:.5rem 0 .5rem 1.5rem}.sidebar .sidebar-links{padding:1.5rem 0}@media (max-width:719px){.sidebar .nav-links{display:block}.sidebar .nav-links .dropdown-wrapper .nav-dropdown .dropdown-item a.router-link-active:after{top:calc(1rem - 2px)}.sidebar .sidebar-links{padding:1rem 0}}code[class*=language-],pre[class*=language-]{color:#ccc;background:none;font-family:Consolas,Monaco,Andale Mono,Ubuntu Mono,monospace;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;tab-size:4;hyphens:none}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#2d2d2d}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.block-comment,.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#999}.token.punctuation{color:#ccc}.token.attr-name,.token.deleted,.token.namespace,.token.tag{color:#e2777a}.token.function-name{color:#6196cc}.token.boolean,.token.function,.token.number{color:#f08d49}.token.class-name,.token.constant,.token.property,.token.symbol{color:#f8c555}.token.atrule,.token.builtin,.token.important,.token.keyword,.token.selector{color:#cc99cd}.token.attr-value,.token.char,.token.regex,.token.string,.token.variable{color:#7ec699}.token.entity,.token.operator,.token.url{color:#67cdcc}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.token.inserted{color:green}#nprogress{pointer-events:none}#nprogress .bar{background:#2f80ed;position:fixed;z-index:1031;top:0;left:0;width:100%;height:2px}#nprogress .peg{display:block;position:absolute;right:0;width:100px;height:100%;box-shadow:0 0 10px #2f80ed,0 0 5px #2f80ed;opacity:1;transform:rotate(3deg) translateY(-4px)}#nprogress .spinner{display:block;position:fixed;z-index:1031;top:15px;right:15px}#nprogress .spinner-icon{width:18px;height:18px;box-sizing:border-box;border:2px solid transparent;border-top-color:#2f80ed;border-left-color:#2f80ed;border-radius:50%;animation:nprogress-spinner .4s linear infinite}.nprogress-custom-parent{overflow:hidden;position:relative}.nprogress-custom-parent #nprogress .bar,.nprogress-custom-parent #nprogress .spinner{position:absolute}@keyframes nprogress-spinner{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.content code{color:#476582;padding:.25rem .5rem;margin:0;font-size:.85em;background-color:rgba(27,31,35,.05);border-radius:3px}.content pre,.content pre[class*=language-]{line-height:1.4;padding:1.25rem 1.5rem;margin:.85rem 0;background:transparent;overflow:auto}.content pre[class*=language-] code,.content pre code{color:#fff;padding:0;background-color:transparent;border-radius:0}div[class*=language-]{position:relative;background-color:#282c34;border-radius:6px}div[class*=language-] .highlight-lines{user-select:none;padding-top:1.3rem;position:absolute;top:0;left:0;width:100%;line-height:1.4}div[class*=language-] .highlight-lines .highlighted{background-color:rgba(0,0,0,.66)}div[class*=language-] pre{position:relative;z-index:1}div[class*=language-]:before{position:absolute;z-index:3;top:.8em;right:1em;font-size:.75rem;color:hsla(0,0%,100%,.4)}div[class*=language-]:not(.line-numbers-mode) .line-numbers-wrapper{display:none}div[class*=language-].line-numbers-mode .highlight-lines .highlighted{position:relative}div[class*=language-].line-numbers-mode .highlight-lines .highlighted:before{content:" ";position:absolute;z-index:3;left:0;top:0;display:block;width:3.5rem;height:100%;background-color:rgba(0,0,0,.66)}div[class*=language-].line-numbers-mode pre{padding-left:4.5rem;vertical-align:middle}div[class*=language-].line-numbers-mode .line-numbers-wrapper{position:absolute;top:0;width:3.5rem;text-align:center;color:hsla(0,0%,100%,.3);padding:1.25rem 0;line-height:1.4}div[class*=language-].line-numbers-mode .line-numbers-wrapper br{user-select:none}div[class*=language-].line-numbers-mode .line-numbers-wrapper .line-number{position:relative;z-index:4;user-select:none;font-size:.85em}div[class*=language-].line-numbers-mode:after{content:"";position:absolute;z-index:2;top:0;left:0;width:3.5rem;height:100%;border-radius:6px 0 0 6px;border-right:1px solid rgba(0,0,0,.66);background-color:#282c34}div[class~=language-js]:before{content:"js"}div[class~=language-ts]:before{content:"ts"}div[class~=language-html]:before{content:"html"}div[class~=language-md]:before{content:"md"}div[class~=language-vue]:before{content:"vue"}div[class~=language-css]:before{content:"css"}div[class~=language-sass]:before{content:"sass"}div[class~=language-scss]:before{content:"scss"}div[class~=language-less]:before{content:"less"}div[class~=language-stylus]:before{content:"stylus"}div[class~=language-go]:before{content:"go"}div[class~=language-java]:before{content:"java"}div[class~=language-c]:before{content:"c"}div[class~=language-sh]:before{content:"sh"}div[class~=language-yaml]:before{content:"yaml"}div[class~=language-javascript]:before{content:"js"}div[class~=language-typescript]:before{content:"ts"}div[class~=language-markup]:before{content:"html"}div[class~=language-markdown]:before{content:"md"}div[class~=language-json]:before{content:"json"}div[class~=language-ruby]:before{content:"rb"}div[class~=language-python]:before{content:"py"}div[class~=language-bash]:before{content:"sh"}.custom-block .custom-block-title{font-weight:600;margin-bottom:-.4rem}.custom-block.danger,.custom-block.note,.custom-block.tip,.custom-block.warning{padding:.1rem 1.5rem;border-left-width:.5rem;border-left-style:solid;margin:1rem 0}.custom-block.tip{background-color:#f3f5f7;border-color:#42b983}.custom-block.note{background-color:#f3f5f7;border-color:#2f80ed}.custom-block.warning{background-color:rgba(255,229,100,.3);border-color:#e7c000;color:#6b5900}.custom-block.warning .custom-block-title{color:#b29400}.custom-block.warning a{color:#2c3e50}.custom-block.danger{background-color:#ffe6e6;border-color:#c00;color:#4d0000}.custom-block.danger .custom-block-title{color:#900}.custom-block.danger a{color:#2c3e50}.arrow{display:inline-block;width:0;height:0}.arrow.up{border-bottom:6px solid #ccc}.arrow.down,.arrow.up{border-left:4px solid transparent;border-right:4px solid transparent}.arrow.down{border-top:6px solid #ccc}.arrow.right{border-left:6px solid #ccc}.arrow.left,.arrow.right{border-top:4px solid transparent;border-bottom:4px solid transparent}.arrow.left{border-right:6px solid #ccc}.content:not(.custom){max-width:740px;margin:0 auto;padding:2rem 2.5rem}@media (max-width:959px){.content:not(.custom){padding:2rem}}@media (max-width:419px){.content:not(.custom){padding:1.5rem}}.table-of-contents .badge{vertical-align:middle}body,html{padding:0;margin:0}body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-size:16px;color:#2c3e50}.intro{text-align:center;font-size:1.15em;margin:0 2em}a img+svg{display:none!important}.version-selector{margin:1em 1em 0 1.5em;color:#4e6e8e;display:block;border:1px solid #cfd4db;font-size:.9rem;line-height:2rem;padding:.5em 1rem .5em 1em;outline:none;transition:all .2s ease}.version-selector:focus{cursor:auto;border-color:#2f80ed}.text-center{text-align:center}.flex{display:flex}.justify-around{justify-content:space-around}.page{padding-left:20rem}.navbar{z-index:20;right:0;height:3.6rem;background-color:#fff;box-sizing:border-box;border-bottom:1px solid #eaecef}.navbar,.sidebar-mask{position:fixed;top:0;left:0}.sidebar-mask{z-index:9;width:100vw;height:100vh;display:none}.sidebar{font-size:15px;background-color:#fff;width:20rem;position:fixed;z-index:10;margin:0;top:3.6rem;left:0;bottom:0;box-sizing:border-box;border-right:1px solid #eaecef;overflow-y:auto}.content:not(.custom)>:first-child{margin-top:3.6rem}.content:not(.custom) a:hover{text-decoration:underline}.content:not(.custom) p.demo{padding:1rem 1.5rem;border:1px solid #ddd;border-radius:4px}.content:not(.custom) img{max-width:100%}.content.custom{padding:0;margin:0}.content.custom img{max-width:100%}a{font-weight:500;text-decoration:none}a,p a code{color:#2f80ed}p a code{font-weight:400}kbd{background:#eee;border:.15rem solid #ddd;border-bottom:.25rem solid #ddd;border-radius:.15rem;padding:0 .15em}blockquote{font-size:1.2rem;color:#999;border-left:.25rem solid #dfe2e5;margin-left:0;padding-left:1rem}ol,ul{padding-left:1.2em}strong{font-weight:600}h1,h2,h3,h4,h5,h6{font-weight:600;line-height:1.25}.content:not(.custom)>h1,.content:not(.custom)>h2,.content:not(.custom)>h3,.content:not(.custom)>h4,.content:not(.custom)>h5,.content:not(.custom)>h6{margin-top:-3.1rem;padding-top:4.6rem;margin-bottom:0}.content:not(.custom)>h1:first-child,.content:not(.custom)>h2:first-child,.content:not(.custom)>h3:first-child,.content:not(.custom)>h4:first-child,.content:not(.custom)>h5:first-child,.content:not(.custom)>h6:first-child{margin-top:-1.5rem;margin-bottom:1rem}.content:not(.custom)>h1:first-child+.custom-block,.content:not(.custom)>h1:first-child+p,.content:not(.custom)>h1:first-child+pre,.content:not(.custom)>h2:first-child+.custom-block,.content:not(.custom)>h2:first-child+p,.content:not(.custom)>h2:first-child+pre,.content:not(.custom)>h3:first-child+.custom-block,.content:not(.custom)>h3:first-child+p,.content:not(.custom)>h3:first-child+pre,.content:not(.custom)>h4:first-child+.custom-block,.content:not(.custom)>h4:first-child+p,.content:not(.custom)>h4:first-child+pre,.content:not(.custom)>h5:first-child+.custom-block,.content:not(.custom)>h5:first-child+p,.content:not(.custom)>h5:first-child+pre,.content:not(.custom)>h6:first-child+.custom-block,.content:not(.custom)>h6:first-child+p,.content:not(.custom)>h6:first-child+pre{margin-top:2rem}h1:hover .header-anchor,h2:hover .header-anchor,h3:hover .header-anchor,h4:hover .header-anchor,h5:hover .header-anchor,h6:hover .header-anchor{opacity:1}h1{font-size:2.2rem}h2{font-size:1.65rem;padding-bottom:.3rem;border-bottom:1px solid #eaecef}h3{font-size:1.35rem}a.header-anchor{font-size:.85em;float:left;margin-left:-.87em;padding-right:.23em;margin-top:.125em;opacity:0}a.header-anchor:hover{text-decoration:none}.line-number,code,kbd{font-family:source-code-pro,Menlo,Monaco,Consolas,Courier New,monospace}ol,p,ul{line-height:1.7}hr{border:0;border-top:1px solid #eaecef}table{border-collapse:collapse;margin:1rem 0;display:block;overflow-x:auto}tr{border-top:1px solid #dfe2e5}tr:nth-child(2n){background-color:#f6f8fa}td,th{border:1px solid #dfe2e5;padding:.6em 1em}.custom-layout{padding-top:3.6rem}.theme-container.sidebar-open .sidebar-mask{display:block}.theme-container.no-navbar .content:not(.custom)>h1,.theme-container.no-navbar h2,.theme-container.no-navbar h3,.theme-container.no-navbar h4,.theme-container.no-navbar h5,.theme-container.no-navbar h6{margin-top:1.5rem;padding-top:0}.theme-container.no-navbar .sidebar{top:0}.theme-container.no-navbar .custom-layout{padding-top:0}@media (min-width:720px){.theme-container.no-sidebar .sidebar{display:none}.theme-container.no-sidebar .page{padding-left:0}}@media (max-width:959px){.sidebar{font-size:15px;width:16.4rem}.page{padding-left:16.4rem}}@media (max-width:719px){.sidebar{top:0;padding-top:3.6rem;transform:translateX(-100%);transition:transform .2s ease}.page{padding-left:0}.theme-container.sidebar-open .sidebar{transform:translateX(0)}.theme-container.no-navbar .sidebar{padding-top:0}}@media (max-width:419px){h1{font-size:1.9rem}.content div[class*=language-]{margin:.85rem -1.5rem;border-radius:0}}.icon.outbound{color:#aaa;display:inline-block} \ No newline at end of file diff --git a/docs/.vuepress/dist/assets/img/getting-started_add_entry.5ce15d30.png b/docs/.vuepress/dist/assets/img/getting-started_add_entry.5ce15d30.png new file mode 100644 index 0000000000..3641893a2d Binary files /dev/null and b/docs/.vuepress/dist/assets/img/getting-started_add_entry.5ce15d30.png differ diff --git a/docs/.vuepress/dist/assets/img/getting-started_allow_access.ea852994.png b/docs/.vuepress/dist/assets/img/getting-started_allow_access.ea852994.png new file mode 100644 index 0000000000..3c87f056b0 Binary files /dev/null and b/docs/.vuepress/dist/assets/img/getting-started_allow_access.ea852994.png differ diff --git a/docs/.vuepress/dist/assets/img/getting-started_create_content_type.f4e658ac.png b/docs/.vuepress/dist/assets/img/getting-started_create_content_type.f4e658ac.png new file mode 100644 index 0000000000..01bcb17035 Binary files /dev/null and b/docs/.vuepress/dist/assets/img/getting-started_create_content_type.f4e658ac.png differ diff --git a/docs/.vuepress/dist/assets/img/getting-started_list_fields.ecdf11cf.png b/docs/.vuepress/dist/assets/img/getting-started_list_fields.ecdf11cf.png new file mode 100644 index 0000000000..eda037d52e Binary files /dev/null and b/docs/.vuepress/dist/assets/img/getting-started_list_fields.ecdf11cf.png differ diff --git a/docs/.vuepress/dist/assets/img/getting-started_manage_role_home.11fb8455.png b/docs/.vuepress/dist/assets/img/getting-started_manage_role_home.11fb8455.png new file mode 100644 index 0000000000..30844f5da0 Binary files /dev/null and b/docs/.vuepress/dist/assets/img/getting-started_manage_role_home.11fb8455.png differ diff --git a/docs/.vuepress/dist/assets/img/getting-started_no_content_type.144f7dab.png b/docs/.vuepress/dist/assets/img/getting-started_no_content_type.144f7dab.png new file mode 100644 index 0000000000..4df862022e Binary files /dev/null and b/docs/.vuepress/dist/assets/img/getting-started_no_content_type.144f7dab.png differ diff --git a/docs/.vuepress/dist/assets/img/getting-started_no_entry.aef6f603.png b/docs/.vuepress/dist/assets/img/getting-started_no_entry.aef6f603.png new file mode 100644 index 0000000000..dfaae83f68 Binary files /dev/null and b/docs/.vuepress/dist/assets/img/getting-started_no_entry.aef6f603.png differ diff --git a/docs/.vuepress/dist/assets/img/getting-started_register.e656c1ff.png b/docs/.vuepress/dist/assets/img/getting-started_register.e656c1ff.png new file mode 100644 index 0000000000..8532758787 Binary files /dev/null and b/docs/.vuepress/dist/assets/img/getting-started_register.e656c1ff.png differ diff --git a/docs/.vuepress/dist/assets/img/getting-started_with_entry.423d6421.png b/docs/.vuepress/dist/assets/img/getting-started_with_entry.423d6421.png new file mode 100644 index 0000000000..0f51766cfa Binary files /dev/null and b/docs/.vuepress/dist/assets/img/getting-started_with_entry.423d6421.png differ diff --git a/docs/.vuepress/dist/assets/img/search.83621669.svg b/docs/.vuepress/dist/assets/img/search.83621669.svg new file mode 100644 index 0000000000..03d83913e8 --- /dev/null +++ b/docs/.vuepress/dist/assets/img/search.83621669.svg @@ -0,0 +1 @@ + diff --git a/docs/.vuepress/dist/assets/img/terminal_new.3f7e23c3.png b/docs/.vuepress/dist/assets/img/terminal_new.3f7e23c3.png new file mode 100644 index 0000000000..0ea2c51cdd Binary files /dev/null and b/docs/.vuepress/dist/assets/img/terminal_new.3f7e23c3.png differ diff --git a/docs/.vuepress/dist/assets/img/terminal_start.b3a7364f.png b/docs/.vuepress/dist/assets/img/terminal_start.b3a7364f.png new file mode 100644 index 0000000000..8e0e575e01 Binary files /dev/null and b/docs/.vuepress/dist/assets/img/terminal_start.b3a7364f.png differ diff --git a/docs/.vuepress/dist/assets/js/1.77d89b12.js b/docs/.vuepress/dist/assets/js/1.77d89b12.js new file mode 100644 index 0000000000..2ba319f5b9 --- /dev/null +++ b/docs/.vuepress/dist/assets/js/1.77d89b12.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[1],{174:function(n,w,o){}}]); \ No newline at end of file diff --git a/docs/.vuepress/dist/assets/js/10.f9e7d997.js b/docs/.vuepress/dist/assets/js/10.f9e7d997.js new file mode 100644 index 0000000000..6d4fb11193 --- /dev/null +++ b/docs/.vuepress/dist/assets/js/10.f9e7d997.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[10],{229:function(t,s,a){"use strict";a.r(s);var e=a(0),n=Object(e.a)({},function(){this.$createElement;this._self._c;return this._m(0)},[function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"content"},[a("h1",{attrs:{id:"customization"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#customization","aria-hidden":"true"}},[t._v("#")]),t._v(" Customization")]),t._v(" "),a("p",[t._v("In keeping with the Node.js philosophy, Strapi aims to keep its core as small\nas possible, delegating all but the most critical functions to separate modules.")]),t._v(" "),a("p",[t._v("Generators are designed to make it easier to customize the "),a("code",[t._v("$ strapi new")]),t._v("\nand "),a("code",[t._v("$ strapi generate")]),t._v(" command-line tools, and provide better support\nfor different user features, custom admin panel, configuration options,\nview engines, etc.")]),t._v(" "),a("p",[t._v("Custom generators are linked to your machine aiming to have your personal\nconfiguration and features at any time, for every application.")]),t._v(" "),a("p",[t._v("You can edit your custom generators inside the "),a("code",[t._v(".strapirc")]),t._v(" file at "),a("code",[t._v("$HOME")]),t._v(".")]),t._v(" "),a("p",[t._v("First, make sure you this file exists:")]),t._v(" "),a("div",{staticClass:"language-bash extra-class"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[t._v("$ strapi config\n")])])]),a("p",[t._v("This file should look like this:")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"generators"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("At this time, you don't have any custom generators on your machine.")]),t._v(" "),a("p",[t._v("In your "),a("code",[t._v(".strapirc")]),t._v(" file, a custom generator is an object with three keys:")]),t._v(" "),a("ul",[a("li",[a("code",[t._v("repository")]),t._v(": the Git repository to clone.")]),t._v(" "),a("li",[a("code",[t._v("remote")]),t._v(": the current remote to pull updates from.")]),t._v(" "),a("li",[a("code",[t._v("branch")]),t._v(": the branch you want to pull updates from.")])]),t._v(" "),a("p",[t._v("For example, to add a custom "),a("code",[t._v("blog")]),t._v(" generator, follow this:")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"generators"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"blog"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"repository"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"git@github.com:username/strapi-generate-blog.git"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"remote"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"origin"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"branch"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"master"')]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("Once you have updated your "),a("code",[t._v(".strapirc")]),t._v(" file, you need to clone and/or update your\ngenerators. To do so, just execute:")]),t._v(" "),a("div",{staticClass:"language-bash extra-class"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[t._v("$ strapi update\n")])])]),a("p",[t._v("This command will clone every new repository written in your configuration file\nand pull the latest updates for the other ones.")]),t._v(" "),a("p",[t._v("Then, you can generate your "),a("code",[t._v("blog")]),t._v(" files inside your project with:")]),t._v(" "),a("div",{staticClass:"language-bash extra-class"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[t._v("$ strapi generate blog\n")])])])])}],!1,null,null,null);n.options.__file="customization.md";s.default=n.exports}}]); \ No newline at end of file diff --git a/docs/.vuepress/dist/assets/js/11.bfb9de0f.js b/docs/.vuepress/dist/assets/js/11.bfb9de0f.js new file mode 100644 index 0000000000..9f24d5a777 --- /dev/null +++ b/docs/.vuepress/dist/assets/js/11.bfb9de0f.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[11],{228:function(t,s,a){"use strict";a.r(s);var n=a(0),e=Object(n.a)({},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"content"},[t._m(0),t._v(" "),a("p",[t._v("Strapi contains a set of tools to send emails. This part is based on the\nfamous email node module: "),a("a",{attrs:{href:"http://nodemailer.com",target:"_blank",rel:"noopener noreferrer"}},[t._v("Nodemailer"),a("OutboundLink")],1),t._v(".")]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),t._m(3),a("p",[t._v("Options:")]),t._v(" "),t._m(4),t._v(" "),t._m(5),t._v(" "),a("p",[t._v("The email service allows you to easily send emails from anywhere in your application.")]),t._v(" "),a("p",[t._v("Usage as a promise (yieldable) :")]),t._v(" "),t._m(6),a("p",[t._v("Usage with a callback :")]),t._v(" "),t._m(7),t._m(8),t._v(" "),a("p",[t._v("The email API is a simple API which can be used from your client (front-end, mobile...) application.")]),t._v(" "),a("p",[t._v("Route used to send emails:")]),t._v(" "),t._m(9),a("p",[t._v("Request payload:")]),t._v(" "),t._m(10),a("p",[t._v("Response payload:")]),t._v(" "),t._m(11),t._m(12),t._v(" "),a("p",[t._v("Each sent email is registered in the database. So you can retrieve them whenever\nyou want. However, you can disable this option by overriding the email service logic.")])])},[function(){var t=this.$createElement,s=this._self._c||t;return s("h1",{attrs:{id:"email"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#email","aria-hidden":"true"}},[this._v("#")]),this._v(" Email")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"email-config"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#email-config","aria-hidden":"true"}},[this._v("#")]),this._v(" Email config")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("To change the STMP config, edit the "),s("code",[this._v("./api/email/config/environments/development/smtp.json")]),this._v(" file.")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"smtp"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"from"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"test"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"service"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"name"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('""')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"user"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('""')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"pass"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('""')]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("ul",[a("li",[a("code",[t._v("from")]),t._v(" (string): The email address used to send emails.")]),t._v(" "),a("li",[a("code",[t._v("service")]),t._v(" (object): The SMTP service info:\n"),a("ul",[a("li",[a("code",[t._v("name")]),t._v(" (string): Name of the service used to send emails (eg. "),a("code",[t._v("Gmail")]),t._v(").")]),t._v(" "),a("li",[a("code",[t._v("user")]),t._v(" (string): Username of the service used (eg. "),a("code",[t._v("john@gmail.com")]),t._v(").")]),t._v(" "),a("li",[a("code",[t._v("pass")]),t._v(" (string): Password of the username used (eg. "),a("code",[t._v("12356")]),t._v(").")])])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"email-service"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#email-service","aria-hidden":"true"}},[this._v("#")]),this._v(" Email service")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[t._v("strapi"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("api"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("email"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("services"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("email"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("send")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'contact@company.com'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token comment"}},[t._v("// Sender (defaults to `strapi.config.smtp.from`).")]),t._v("\n to"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{attrs:{class:"token string"}},[t._v("'john@doe.com'")]),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token comment"}},[t._v("// Recipients list.")]),t._v("\n html"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'

Hello John

'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token comment"}},[t._v("// HTML version of the email content.")]),t._v("\n text"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'Hello John'")]),t._v(" "),a("span",{attrs:{class:"token comment"}},[t._v("// Text version of the email content.")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("then")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token keyword"}},[t._v("function")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("data"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n console"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("log")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("data"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token keyword"}},[t._v("catch")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token keyword"}},[t._v("function")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n console"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("log")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[t._v("strapi"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("api"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("email"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("services"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("email"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("send")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'contact@company.com'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token comment"}},[t._v("// Sender (defaults to `strapi.config.smtp.from`).")]),t._v("\n to"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{attrs:{class:"token string"}},[t._v("'john@doe.com'")]),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token comment"}},[t._v("// Recipients list.")]),t._v("\n html"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'

Hello John

'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token comment"}},[t._v("// HTML version of the email content.")]),t._v("\n text"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'Hello John'")]),t._v(" "),a("span",{attrs:{class:"token comment"}},[t._v("// Text version of the email content.")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("function")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" data"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n console"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("log")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n console"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("log")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("data"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"email-api"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#email-api","aria-hidden":"true"}},[this._v("#")]),this._v(" Email API")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-bash extra-class"},[s("pre",{pre:!0,attrs:{class:"language-bash"}},[s("code",[this._v("POST /email\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'contact@company.com'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token comment"}},[t._v("// Optional : sender (defaults to `strapi.config.smtp.from`).")]),t._v("\n to"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{attrs:{class:"token string"}},[t._v("'john@doe.com'")]),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token comment"}},[t._v("// Recipients list.")]),t._v("\n html"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'

Hello John

'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token comment"}},[t._v("// HTML version of the email content.")]),t._v("\n text"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'Hello John'")]),t._v(" "),a("span",{attrs:{class:"token comment"}},[t._v("// Text version of the email content.")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"sent"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token boolean"}},[t._v("true")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"from"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"contact@company.com"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"to"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"john@doe.com"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"html"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"

Hello John

"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"text"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"Hello John"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"template"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"default"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"lang"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"en"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"createdAt"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"2015-10-21T09:10:36.486Z"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"updatedAt"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"2015-10-21T09:10:36.871Z"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"id"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("2")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"email-model"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#email-model","aria-hidden":"true"}},[this._v("#")]),this._v(" Email model")])}],!1,null,null,null);e.options.__file="email.md";s.default=e.exports}}]); \ No newline at end of file diff --git a/docs/.vuepress/dist/assets/js/12.124227d1.js b/docs/.vuepress/dist/assets/js/12.124227d1.js new file mode 100644 index 0000000000..b2d49d4fbe --- /dev/null +++ b/docs/.vuepress/dist/assets/js/12.124227d1.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[12],{227:function(t,s,a){"use strict";a.r(s);var n=a(0),e=Object(n.a)({},function(){this.$createElement;this._self._c;return this._m(0)},[function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"content"},[a("h1",{attrs:{id:"graphql"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#graphql","aria-hidden":"true"}},[t._v("#")]),t._v(" GraphQL")]),t._v(" "),a("p",[t._v("GraphQL is a data querying language that allows you to execute complex nested\nrequests between your clients and server applications.")]),t._v(" "),a("h2",{attrs:{id:"configuration"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#configuration","aria-hidden":"true"}},[t._v("#")]),t._v(" Configuration")]),t._v(" "),a("p",[t._v("By default, GraphQL is enabled and the HTTP endpoint is "),a("code",[t._v("/graphql")]),t._v(".\nYou can override this settings in the "),a("code",[t._v("./config/general.json")]),t._v(" file.")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"graphql"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"enabled"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token boolean"}},[t._v("true")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"route"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"/graphql"')]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("Options:")]),t._v(" "),a("ul",[a("li",[a("code",[t._v("enabled")]),t._v(" (boolean): Enabled or disabled GraphQL.")]),t._v(" "),a("li",[a("code",[t._v("route")]),t._v(" (string): Change GraphQL endpoint.")])]),t._v(" "),a("p",[t._v("Note: If GraphQL is disabled, the GraphQL global variable is not exposed.")]),t._v(" "),a("h2",{attrs:{id:"execute-simple-query"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#execute-simple-query","aria-hidden":"true"}},[t._v("#")]),t._v(" Execute simple query")]),t._v(" "),a("h3",{attrs:{id:"programmatically"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#programmatically","aria-hidden":"true"}},[t._v("#")]),t._v(" Programmatically")]),t._v(" "),a("p",[t._v("Strapi takes over GraphQL natively. We added a function called "),a("code",[t._v("query")]),t._v(" to execute\nyour query without given as a parameters the GraphQL schemas each time.")]),t._v(" "),a("p",[t._v("An example of how to use "),a("code",[t._v("query")]),t._v(" function:")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token comment"}},[t._v("// Build your query")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("const")]),t._v(" query "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'{ users{firstName lastName posts{title}} }'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{attrs:{class:"token comment"}},[t._v("// Execute the query")]),t._v("\ngraphql"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("query")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("query"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("then")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token keyword"}},[t._v("function")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("result"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n console"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("log")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("result"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token keyword"}},[t._v("catch")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token keyword"}},[t._v("function")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("error"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n console"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("log")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("error"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),a("p",[t._v("And the JSON result:")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"users"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"firstname"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"John"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"lastname"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"Doe"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"posts"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"title"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"First title..."')]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"title"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"Second title..."')]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"title"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"Third title..."')]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" \n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"firstname"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"Karl"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"lastname"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"Doe"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"posts"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"title"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"Fourth title..."')]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" \n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("h3",{attrs:{id:"with-a-http-request"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#with-a-http-request","aria-hidden":"true"}},[t._v("#")]),t._v(" With a HTTP request")]),t._v(" "),a("p",[t._v("Strapi also provides a HTTP GraphQL server to execute request from your front-end application.")]),t._v(" "),a("p",[t._v("An example of how to execute the same request as above with a HTTP request with jQuery.")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[t._v("$"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token keyword"}},[t._v("get")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'http://yourserver.com/graphql?query={ users{firstName lastName posts{title}} }'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("function")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("data"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n console"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("log")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("data"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),a("h2",{attrs:{id:"execute-complex-queries"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#execute-complex-queries","aria-hidden":"true"}},[t._v("#")]),t._v(" Execute complex queries")]),t._v(" "),a("h3",{attrs:{id:"query-parameters"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#query-parameters","aria-hidden":"true"}},[t._v("#")]),t._v(" Query parameters")]),t._v(" "),a("p",[t._v("If you're using Waterline ORM installed by default with Strapi, you have access to\nsome Waterline query parameters in your GraphQL query such as "),a("code",[t._v("sort")]),t._v(", "),a("code",[t._v("limit")]),t._v(" or "),a("code",[t._v("skip")]),t._v(".\nStrapi also provides the "),a("code",[t._v("start")]),t._v(" and "),a("code",[t._v("end")]),t._v(" parameters to select records between two dates.")]),t._v(" "),a("p",[t._v("This example will return 10 users' records sorted alphabetically by "),a("code",[t._v("firstName")]),t._v(":")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("const")]),t._v(" query "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'{ users(limit: 10, sort: \"firstName ASC\"){firstName lastName post{title}} }'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),a("p",[t._v("You can access to the 10 next users by adding the "),a("code",[t._v("skip")]),t._v(" parameter:")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("const")]),t._v(" query "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'{ users(limit: 10, sort: \"firstName ASC\", skip: 10){firstName lastName posts{title}} }'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),a("p",[t._v("And you also can select those records in a period between two dates with the "),a("code",[t._v("start")]),t._v(" and "),a("code",[t._v("end")]),t._v(" parameters:")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("const")]),t._v(" query "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('\'{ users(limit: 10, sort: "firstName ASC", skip: 10, start: "09/21/2015", end:" 09/22/2015"){firstName lastName posts{title}} }\'')]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),a("h3",{attrs:{id:"useful-functions"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#useful-functions","aria-hidden":"true"}},[t._v("#")]),t._v(" Useful functions")]),t._v(" "),a("p",[t._v("Strapi comes with a powerful set of useful functions such as "),a("code",[t._v("getLatest")]),t._v(", "),a("code",[t._v("getFirst")]),t._v(" and "),a("code",[t._v("count")]),t._v(".")]),t._v(" "),a("p",[t._v("Returns the 5 latest users from the September 27th 2015 at 8:59:59 PM:")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("const")]),t._v(" query "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'{ getLatestUsers(count: 5, start: \"9/27/2015 20:59:59\"){firstName lastName posts{title}} }'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),a("p",[t._v("Returns the 5 first users:")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("const")]),t._v(" query "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'{ getFirstUsers(count: 5){firstName lastName posts{title}} }'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),a("p",[t._v("Returns the number of subscribers the September 28th 2015:")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("const")]),t._v(" query "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('\'{ countUsers(start: "9/28/2015", end: "9/28/2015") }\'')]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])])}],!1,null,null,null);e.options.__file="graphql.md";s.default=e.exports}}]); \ No newline at end of file diff --git a/docs/.vuepress/dist/assets/js/13.d8092700.js b/docs/.vuepress/dist/assets/js/13.d8092700.js new file mode 100644 index 0000000000..cb79396dc9 --- /dev/null +++ b/docs/.vuepress/dist/assets/js/13.d8092700.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[13],{226:function(t,s,a){"use strict";a.r(s);var n=a(0),e=Object(n.a)({},function(){this.$createElement;this._self._c;return this._m(0)},[function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"content"},[a("h1",{attrs:{id:"internationalization"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#internationalization","aria-hidden":"true"}},[t._v("#")]),t._v(" Internationalization")]),t._v(" "),a("p",[t._v("Strapi provides built-in support for detecting user language preferences and translating\nstatic words/sentences.")]),t._v(" "),a("h2",{attrs:{id:"i18n-settings"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#i18n-settings","aria-hidden":"true"}},[t._v("#")]),t._v(" i18n settings")]),t._v(" "),a("p",[t._v("Settings for localization/internationalization may be configured in "),a("code",[t._v("strapi.config.i18n")]),t._v(".\nThe most common reason you'll need to modify these settings is to edit the list of your\napplication's supported locales and/or the location of your translation stringfiles.")]),t._v(" "),a("h2",{attrs:{id:"locales"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#locales","aria-hidden":"true"}},[t._v("#")]),t._v(" Locales")]),t._v(" "),a("p",[t._v("Strapi reads JSON-formatted translation files from your project's "),a("code",[t._v("./config/locales")]),t._v("\ndirectory. Each file corresponds with a locale (usually a language) that your backend will support.\nThese files contain locale-specific strings (as JSON key-value pairs) that you can use in your\nviews, controllers, etc.")]),t._v(" "),a("p",[t._v("When your server is in "),a("code",[t._v("production")]),t._v(" mode it will read these files only once and then cache\nthe result. It will not write any updated strings when in "),a("code",[t._v("production")]),t._v(" mode.")]),t._v(" "),a("p",[t._v("Otherwise, the files will be read on every instantiation of the "),a("code",[t._v("i18n")]),t._v(" object.\nAdditionally newly-detected strings will be automatically added, and written out,\nto the locale JSON files.")]),t._v(" "),a("p",[t._v("These files contain locale-specific strings (as JSON key-value pairs) that you can use in your views,\ncontrollers, etc. Here is an example locale file ("),a("code",[t._v("./config/locales/fr.json")]),t._v("):")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"Hello!"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"Bonjour!"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"Hello %s, how are you today?"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"Bonjour %s, comment allez-vous aujourd\'hui ?"')]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("Note that the keys in your stringfiles are case sensitive and require exact matches.\nThere are a few different schools of thought on the best approach here, and it really depends on\nwho/how often you'll be editing the stringfiles in the future. Especially if you'll be\nediting the translations by hand, simpler, all-lowercase key names may be preferable for maintainability.")]),t._v(" "),a("p",[t._v("For example, here's another pass at "),a("code",[t._v("./config/locales/fr.json")]),t._v(":")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"hello"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"Bonjour!"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"hello-how-are-you-today"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"Bonjour %s, comment allez-vous aujourd\'hui ?"')]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("And here's "),a("code",[t._v("./config/locales/en.json")]),t._v(":")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"hello"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"Hello!"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"hello-how-are-you-today"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"Hello %s, how are you today?"')]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("You can also nest locale strings. But a better approach would be to use "),a("code",[t._v(".")]),t._v(" to represent nested strings.\nFor example, here's the list of labels for the index page of a user controller:")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"user.index.label.id"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"User ID"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"user.index.label.name"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"User Name"')]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("h2",{attrs:{id:"translate-responses"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#translate-responses","aria-hidden":"true"}},[t._v("#")]),t._v(" Translate responses")]),t._v(" "),a("p",[t._v("Locales are accessible from everywhere in your application.")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("body "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("i18n"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("__")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'hello-how-are-you-today'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'John'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token comment"}},[t._v('// => "Hello John, how are you today?"')]),t._v("\n")])])]),a("p",[t._v("Different plural forms are supported as a response to "),a("code",[t._v("count")]),t._v(" with "),a("code",[t._v("this.i18n.__n(one, other, count)")]),t._v(".")]),t._v(" "),a("p",[t._v("Use "),a("code",[t._v("this.i18n.__n()")]),t._v(" as you would use "),a("code",[t._v("this.i18.__()")]),t._v(" directly:")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("body "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("i18n"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("__n")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'%s cat'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'%s cats'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("1")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token comment"}},[t._v('// => "1 cat"')]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("body "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("i18n"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("__n")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'%s cat'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'%s cats'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("3")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token comment"}},[t._v('// => "3 cats"')]),t._v("\n")])])]),a("p",[t._v("Or from locales:")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"catEat"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"one"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"%d cat eats the %s"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"other"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'%d cats eat the %s'")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("body "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("i18n"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("__n")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'catEat'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("10")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'mouse'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token comment"}},[t._v('// => "10 cats eat the mouse"')]),t._v("\n")])])])])}],!1,null,null,null);e.options.__file="internationalization.md";s.default=e.exports}}]); \ No newline at end of file diff --git a/docs/.vuepress/dist/assets/js/14.7cfd4cb8.js b/docs/.vuepress/dist/assets/js/14.7cfd4cb8.js new file mode 100644 index 0000000000..cddcca36b9 --- /dev/null +++ b/docs/.vuepress/dist/assets/js/14.7cfd4cb8.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[14],{225:function(t,e,a){"use strict";a.r(e);var s=a(0),n=Object(s.a)({},function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"content"},[t._m(0),t._v(" "),a("hr"),t._v(" "),t._m(1),t._v(" "),a("hr"),t._v(" "),a("p",[t._v("Strapi is an open-source Node.js rich framework for building applications and services.")]),t._v(" "),a("p",[t._v("Strapi enables developers to focus on writing reusable application logic instead of spending time\nbuilding infrastructure. It is designed for building practical, production-ready Node.js applications\nin a matter of hours instead of weeks.")]),t._v(" "),a("p",[t._v("The framework sits on top of "),a("a",{attrs:{href:"http://koajs.com/",target:"_blank",rel:"noopener noreferrer"}},[t._v("Koa"),a("OutboundLink")],1),t._v(". Its ensemble of small modules work\ntogether to provide simplicity, maintainability, and structural conventions to Node.js applications.")]),t._v(" "),t._m(2),t._v(" "),t._m(3),t._v(" "),a("p",[t._v("Install the latest stable release with the npm command-line tool:")]),t._v(" "),t._m(4),t._m(5),t._v(" "),a("p",[t._v("You now are able to use the Strapi CLI. Simply create your first application and start the server:")]),t._v(" "),t._m(6),a("p",[t._v("The default home page is accessible at "),a("a",{attrs:{href:"http://localhost:1337/",target:"_blank",rel:"noopener noreferrer"}},[t._v("http://localhost:1337/"),a("OutboundLink")],1),t._v(".")]),t._v(" "),t._m(7),t._v(" "),t._m(8),t._m(9),t._v(" "),t._m(10),t._m(11),t._v(" "),t._m(12),t._v(" "),t._m(13),t._v(" "),t._m(14),a("p",[t._v("This will generate a Strapi application without:")]),t._v(" "),t._m(15),t._v(" "),a("p",[t._v("This feature allows you to only use Strapi for your HTTP server structure if you want to.")]),t._v(" "),t._m(16),t._v(" "),a("p",[t._v("The Strapi Studio allows you to easily build and manage your application environment\nthanks to a powerful User Interface.")]),t._v(" "),a("p",[t._v("Log into the Strapi Studio with your user account ("),a("a",{attrs:{href:"http://studio.strapi.io",target:"_blank",rel:"noopener noreferrer"}},[t._v("http://studio.strapi.io"),a("OutboundLink")],1),t._v(")\nand follow the instructions to start the experience.")]),t._v(" "),t._m(17),t._v(" "),a("blockquote",[a("p",[t._v("We advise you to use our Studio to build APIs. To do so, you need to create a Strapi account.\n"),a("a",{attrs:{href:"http://studio.strapi.io",target:"_blank",rel:"noopener noreferrer"}},[t._v("Go to the Strapi Studio to signup"),a("OutboundLink")],1),t._v(".\nStudio is dedicated to developers to build applications without writing\nany single line of code thanks to its powerful set of tools.")])]),t._v(" "),a("p",[t._v("After creating an account on the Strapi Studio, you are able to link your machine to your\nStrapi Studio account to get access to all features offered by the Strapi ecosystem.\nUse your Strapi account credentials.")]),t._v(" "),t._m(18),t._m(19),t._v(" "),t._m(20),t._v(" "),a("p",[t._v("Building on top of Strapi means your application is written entirely in JavaScript,\nthe language you and your team are already using in the browser.")]),t._v(" "),a("p",[t._v("Since you spend less time context-shifting, you're able to write code in a more consistent style,\nwhich makes development more productive.")]),t._v(" "),a("p",[t._v("The entire Strapi framework is written in ES2015.")]),t._v(" "),t._m(21),t._v(" "),a("p",[t._v("Strapi provides a robust layer for fundamental web applications to help you write your business\nlogic, without obscuring Node.js features that you know and love. Our goal is to make writing\nbusiness logic much easier than other frameworks.")]),t._v(" "),t._m(22),t._v(" "),a("p",[t._v("Strapi comes with a generator that help jumpstart your application's backend without writing any code. Just run:")]),t._v(" "),t._m(23),a("p",[t._v("and you'll get an API that lets you read, paginate, sort, filter, create, destroy, update,\nand associate cars.")]),t._v(" "),t._m(24),t._v(" "),a("p",[t._v("We take security very seriously. This is why Strapi comes with several security layers that just work\ndepending on your needs. Strapi provides configuration for CORS, CSRF, CSP, X-Frame-Options, XSS, HSTS,\nHTTPS, SSL, proxy, IP filtering and ships reusable security policies.")]),t._v(" "),a("p",[t._v("No matter what you need to secure, Strapi is the right tool to make it right.")]),t._v(" "),t._m(25),t._v(" "),a("p",[t._v("Strapi comes installed with a powerful ORM/ODM called Waterline, a datastore-agnostic tool that\ndramatically simplifies interaction with one or more databases.")]),t._v(" "),a("p",[t._v("It provides an abstraction layer on top of the underlying database, allowing you to easily query\nand manipulate your data without writing vendor-specific integration code.")]),t._v(" "),a("p",[t._v("Strapi offers a new take on the familiar relational model, aimed at making data modeling more practical.\nYou can do all the same things you might be used to (one-to-many, many-to-many), but you can also assign\nmultiple named associations per-model. Better yet, you can assign different models to different databases,\nand your associations/joins will still work, even across NoSQL and relational boundries.")]),t._v(" "),a("p",[t._v("Strapi has no problem implicitly/automatically joining a SQL table with a NoSQL collection and vice versa.")]),t._v(" "),t._m(26),t._v(" "),a("p",[t._v("Strapi is compatible with any front-end strategy; whether it's Angular, Backbone, Ember,\niOS, Android, Windows Phone, or something else that hasn't been invented yet.")]),t._v(" "),a("p",[t._v("Plus it's easy to serve up the same API to be consumed by another web service or community of developers.")]),t._v(" "),t._m(27),t._v(" "),a("p",[t._v("Convention over configuration is a consistent approach makes developing applications more\npredictable and efficient for everybody involved.")]),t._v(" "),a("p",[t._v("If anyone on your team has worked with frameworks, Strapi will feel pretty familiar.\nNot only that, but they can look at a Strapi project and know, generally, how to code up the basic\npatterns they've implemented over and over again in the past; whether their background.\nWhat about your second application, or your third? Each time you create a new Strapi application,\nyou start with a sane, familiar boilerplate that makes you more productive.")]),t._v(" "),a("p",[t._v("Configuration files give you extra opportunities for human error.")]),t._v(" "),a("p",[t._v("In many cases, you'll even be able to recycle some of your code.")]),t._v(" "),t._m(28),t._v(" "),t._m(29),t._v(" "),t._m(30),t._m(31),t._v(" "),t._m(32),a("p",[t._v('When an error occurs and it is still possible to respond to the client,\naka no data has been written to the socket, Strapi will respond appropriately with\na 500 "Internal Server Error". In either case an app-level "error" is emitted for logging purposes.')]),t._v(" "),t._m(33),t._v(" "),a("p",[t._v("Strapi has built in support for the idea of having a different set of settings for each environment.\nReal applications have this too, but often the framework around them doesn't accommodate it and\nyou end up having to swap configuration files in and out to achieve the same effect.")]),t._v(" "),t._m(34),t._v(" "),a("p",[t._v("Strapi is flexible enough to allow you to explore and create when you have the time to but also\nprovides automation tools when you don't.")])])},[function(){var t=this.$createElement,e=this._self._c||t;return e("h1",{attrs:{id:"introduction"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#introduction","aria-hidden":"true"}},[this._v("#")]),this._v(" Introduction")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("Important Note: "),e("strong",[this._v("Strapi 1.x is on maintenance only")]),this._v(". Development focuses on the upcoming Strapi 3.0.")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"getting-started"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#getting-started","aria-hidden":"true"}},[this._v("#")]),this._v(" Getting Started")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"installation"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#installation","aria-hidden":"true"}},[this._v("#")]),this._v(" Installation")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[this._v("$ "),e("span",{attrs:{class:"token function"}},[this._v("npm")]),this._v(" "),e("span",{attrs:{class:"token function"}},[this._v("install")]),this._v(" strapi -g\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"create-your-first-project"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#create-your-first-project","aria-hidden":"true"}},[this._v("#")]),this._v(" Create your first project")])},function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"language-bash extra-class"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[t._v("$ strapi new "),a("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("appName"),a("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v("\n$ "),a("span",{attrs:{class:"token function"}},[t._v("cd")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("appName"),a("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v("\n$ strapi start\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"create-your-first-api"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#create-your-first-api","aria-hidden":"true"}},[this._v("#")]),this._v(" Create your first API")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[this._v("$ strapi generate api "),e("span",{attrs:{class:"token operator"}},[this._v("<")]),this._v("apiName"),e("span",{attrs:{class:"token operator"}},[this._v(">")]),this._v("\n")])])])},function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("p",[t._v("For example, you can create a "),a("code",[t._v("car")]),t._v(" API with a name ("),a("code",[t._v("name")]),t._v("), year ("),a("code",[t._v("year")]),t._v(") and\nthe license plate ("),a("code",[t._v("license")]),t._v(") with:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[this._v("$ strapi generate api car name:string year:integer license:string\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"alternatives"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#alternatives","aria-hidden":"true"}},[this._v("#")]),this._v(" Alternatives")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"dry-application"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#dry-application","aria-hidden":"true"}},[this._v("#")]),this._v(" Dry Application")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("Note that you can generate a dry application using the "),e("code",[this._v("dry")]),this._v(" option:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[this._v("$ strapi new "),e("span",{attrs:{class:"token operator"}},[this._v("<")]),this._v("appName"),e("span",{attrs:{class:"token operator"}},[this._v(">")]),this._v(" --dry\n")])])])},function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("ul",[a("li",[t._v("the built-in "),a("code",[t._v("user")]),t._v(", "),a("code",[t._v("email")]),t._v(" and "),a("code",[t._v("upload")]),t._v(" APIs,")]),t._v(" "),a("li",[t._v("the "),a("code",[t._v("grant")]),t._v(" hook,")]),t._v(" "),a("li",[t._v("the open-source admin panel,")]),t._v(" "),a("li",[t._v("the Waterline ORM ("),a("code",[t._v("waterline")]),t._v(" and "),a("code",[t._v("blueprints")]),t._v(" hooks disabled),")]),t._v(" "),a("li",[t._v("the Strapi Studio connection ("),a("code",[t._v("studio")]),t._v(" hook disabled).")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"create-an-api-via-the-strapi-studio"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#create-an-api-via-the-strapi-studio","aria-hidden":"true"}},[this._v("#")]),this._v(" Create an API via the Strapi Studio")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"link-to-the-strapi-studio"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#link-to-the-strapi-studio","aria-hidden":"true"}},[this._v("#")]),this._v(" Link to the Strapi Studio")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[this._v("$ strapi login\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"key-features"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#key-features","aria-hidden":"true"}},[this._v("#")]),this._v(" Key-features")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"_100-javascript"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#_100-javascript","aria-hidden":"true"}},[this._v("#")]),this._v(" 100% JavaScript")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"getting-started-quickly"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#getting-started-quickly","aria-hidden":"true"}},[this._v("#")]),this._v(" Getting started quickly")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"auto-generate-rest-apis"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#auto-generate-rest-apis","aria-hidden":"true"}},[this._v("#")]),this._v(" Auto-generate REST APIs")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[this._v("$ strapi generate api car\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"security"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#security","aria-hidden":"true"}},[this._v("#")]),this._v(" Security")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"datastore-agnostic"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#datastore-agnostic","aria-hidden":"true"}},[this._v("#")]),this._v(" Datastore-agnostic")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"front-end-agnostic"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#front-end-agnostic","aria-hidden":"true"}},[this._v("#")]),this._v(" Front-end agnostic")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"convention-over-configuration"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#convention-over-configuration","aria-hidden":"true"}},[this._v("#")]),this._v(" Convention over configuration")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"error-handling"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#error-handling","aria-hidden":"true"}},[this._v("#")]),this._v(" Error Handling")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("By default outputs all errors to "),e("code",[this._v("stderr")]),this._v(" unless "),e("code",[this._v("NODE_ENV")]),this._v(" is "),e("code",[this._v("test")]),this._v('.\nTo perform custom error-handling logic such as centralized logging you can add an "error" event listener:')])},function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[t._v("strapi"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("app"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("on")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'error'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("function")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n strapi"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("log"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("error")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'server error'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" err"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("If an error in the req/res cycle and it is not possible to respond to the client,\nthe "),e("code",[this._v("Context")]),this._v(" instance is also passed:")])},function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[t._v("strapi"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("app"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("on")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'error'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("function")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" ctx"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n strapi"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("log"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("error")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'server error'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" err"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" ctx"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"different-environments"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#different-environments","aria-hidden":"true"}},[this._v("#")]),this._v(" Different environments")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"loose-coupling"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#loose-coupling","aria-hidden":"true"}},[this._v("#")]),this._v(" Loose coupling")])}],!1,null,null,null);n.options.__file="introduction.md";e.default=n.exports}}]); \ No newline at end of file diff --git a/docs/.vuepress/dist/assets/js/15.55a20f7c.js b/docs/.vuepress/dist/assets/js/15.55a20f7c.js new file mode 100644 index 0000000000..c773c8c8d1 --- /dev/null +++ b/docs/.vuepress/dist/assets/js/15.55a20f7c.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[15],{224:function(t,s,a){"use strict";a.r(s);var n=a(0),o=Object(n.a)({},function(){this.$createElement;this._self._c;return this._m(0)},[function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"content"},[a("h1",{attrs:{id:"logging"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#logging","aria-hidden":"true"}},[t._v("#")]),t._v(" Logging")]),t._v(" "),a("p",[t._v("Strapi comes with a simple and useful built-in logger.\nIts usage is purposely very similar to "),a("code",[t._v("console.log()")]),t._v(", but with a handful of\nextra features; namely support for multiple log levels with colorized,\nprefixed console output.")]),t._v(" "),a("p",[t._v("The logger is accessible through the "),a("code",[t._v("strapi")]),t._v(" object directly with "),a("code",[t._v("strapi.log")]),t._v(".")]),t._v(" "),a("p",[t._v("You can work with this logger in the same way that you work with the default logger:")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[t._v("strapi"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("log"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("info")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'Logs work!'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),a("h2",{attrs:{id:"logging-with-metadata"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#logging-with-metadata","aria-hidden":"true"}},[t._v("#")]),t._v(" Logging with Metadata")]),t._v(" "),a("p",[t._v("In addition to logging string messages, the logger will also optionally log additional\nJSON metadata objects. Adding metadata is simple:")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[t._v("strapi"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("log"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("info")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'Test log message'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n anything"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'This is metadata'")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),a("h2",{attrs:{id:"string-interpolation"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#string-interpolation","aria-hidden":"true"}},[t._v("#")]),t._v(" String interpolation")]),t._v(" "),a("p",[t._v("The log method provides the same string interpolation methods like "),a("code",[t._v("util.format")]),t._v(".")]),t._v(" "),a("p",[t._v("This allows for the following log messages.")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[t._v("strapi"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("log"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("info")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'test message %s'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'my string'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token comment"}},[t._v("// => info: test message my string")]),t._v("\n")])])]),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[t._v("strapi"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("log"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("info")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'test message %d'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("123")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token comment"}},[t._v("// => info: test message 123")]),t._v("\n")])])]),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[t._v("strapi"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("log"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("info")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'test message %j'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n number"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("123")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token comment"}},[t._v('// => info: test message {"number":123}')]),t._v("\n"),a("span",{attrs:{class:"token comment"}},[t._v("// => meta = {}")]),t._v("\n")])])]),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[t._v("strapi"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("log"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("info")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'test message %s, %s'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'first'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'second'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n number"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("123")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token comment"}},[t._v("// => info: test message first, second")]),t._v("\n"),a("span",{attrs:{class:"token comment"}},[t._v("// => meta = {number: 123}")]),t._v("\n")])])]),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[t._v("strapi"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("log"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("info")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'test message'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'first'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'second'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n number"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("123")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token comment"}},[t._v("// => info: test message first second")]),t._v("\n"),a("span",{attrs:{class:"token comment"}},[t._v("// => meta = {number: 123}")]),t._v("\n")])])]),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[t._v("strapi"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("log"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("info")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'test message %s, %s'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'first'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'second'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n number"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("123")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("function")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token comment"}},[t._v("// => info: test message first, second")]),t._v("\n"),a("span",{attrs:{class:"token comment"}},[t._v("// => meta = {number: 123}")]),t._v("\n"),a("span",{attrs:{class:"token comment"}},[t._v("// => callback = function() {}")]),t._v("\n")])])]),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[t._v("strapi"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("log"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("info")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'test message'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'first'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'second'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n number"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("123")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("function")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token comment"}},[t._v("// => info: test message first second")]),t._v("\n"),a("span",{attrs:{class:"token comment"}},[t._v("// => meta = {number: 123}")]),t._v("\n"),a("span",{attrs:{class:"token comment"}},[t._v("// => callback = function() {}")]),t._v("\n")])])]),a("h2",{attrs:{id:"logging-levels"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#logging-levels","aria-hidden":"true"}},[t._v("#")]),t._v(" Logging levels")]),t._v(" "),a("p",[t._v("Setting the level for your logging message can be accomplished by using\nthe level specified methods defined.")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[t._v("strapi"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("log"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("debug")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'This is a debug log'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\nstrapi"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("log"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("info")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'This is an info log'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\nstrapi"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("log"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("warn")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'This is a warning log'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\nstrapi"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("log"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("error")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'This is an error log '")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])])}],!1,null,null,null);o.options.__file="logging.md";s.default=o.exports}}]); \ No newline at end of file diff --git a/docs/.vuepress/dist/assets/js/16.5c84c402.js b/docs/.vuepress/dist/assets/js/16.5c84c402.js new file mode 100644 index 0000000000..a61e9acde6 --- /dev/null +++ b/docs/.vuepress/dist/assets/js/16.5c84c402.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[16],{223:function(t,s,a){"use strict";a.r(s);var n=a(0),e=Object(n.a)({},function(){this.$createElement;this._self._c;return this._m(0)},[function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"content"},[a("h1",{attrs:{id:"models"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#models","aria-hidden":"true"}},[t._v("#")]),t._v(" Models")]),t._v(" "),a("p",[t._v("Strapi comes installed with a powerful Object-Relational-Mapper (ORM) called Waterline,\na datastore-agnostic tool that dramatically simplifies interaction with one or more databases.")]),t._v(" "),a("p",[t._v("Models represent a structure of data which requires persistent storage. The data may live in any data-store\nbut is interfaced in the same way. This allows your users to live in PostgreSQL and your user preferences\nto live in MongoDB and you will interact with the data models in the exact same way.")]),t._v(" "),a("p",[t._v("If you're using MySQL, a model might correspond to a table. If you're using MongoDB, it might correspond\nto a collection. In either case, the goal is to provide a simple, modular way of managing data without\nrelying on any one type of database.")]),t._v(" "),a("p",[t._v("Models are defined in the "),a("code",[t._v("./api//models")]),t._v(" directory.")]),t._v(" "),a("h2",{attrs:{id:"model-settings"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#model-settings","aria-hidden":"true"}},[t._v("#")]),t._v(" Model settings")]),t._v(" "),a("p",[t._v("The following properties can be specified at the top level of your model definition to override\nthe defaults for that particular model.")]),t._v(" "),a("p",[t._v("For example, this a basic model "),a("code",[t._v("Pet")]),t._v(":")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"identity"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"pet"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"connection"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"mongoDBServer"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"schema"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token boolean"}},[t._v("true")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"attributes"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"name"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"type"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"string"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"required"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token boolean"}},[t._v("true")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"gender"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"type"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"string"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"enum"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{attrs:{class:"token string"}},[t._v('"male"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"female"')]),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"age"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"type"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"int"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"max"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("100")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"birthDate"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"type"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"date"')]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"breed"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"type"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"string"')]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"autoPK"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token boolean"}},[t._v("true")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"autoCreatedAt"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token boolean"}},[t._v("true")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"autoUpdatedAt"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token boolean"}},[t._v("true")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),a("h3",{attrs:{id:"schema"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#schema","aria-hidden":"true"}},[t._v("#")]),t._v(" schema")]),t._v(" "),a("p",[t._v("A flag to toggle schemaless or schema mode in databases that support schemaless data structures.\nIf turned off, this will allow you to store arbitrary data in a record. If turned on, only attributes\ndefined in the model's attributes object will be stored.")]),t._v(" "),a("p",[t._v("For adapters that don't require a schema, such as MongoDB or Redis, the "),a("code",[t._v("schema")]),t._v(" key is set to "),a("code",[t._v("false")]),t._v(".")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"schema"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token boolean"}},[t._v("true")]),a("span",{attrs:{class:"token operator"}},[t._v("|")]),a("span",{attrs:{class:"token boolean"}},[t._v("false")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("h3",{attrs:{id:"connection"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#connection","aria-hidden":"true"}},[t._v("#")]),t._v(" connection")]),t._v(" "),a("p",[t._v("The configured database connection where this model will fetch and save its data.\nDefaults to "),a("code",[t._v("defaultSQLite")]),t._v(", the default connection that uses the "),a("code",[t._v("waterline-sqlite3")]),t._v(" adapter.")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"connection"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"mongoDBServer"')]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("h3",{attrs:{id:"identity"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#identity","aria-hidden":"true"}},[t._v("#")]),t._v(" identity")]),t._v(" "),a("p",[t._v("The lowercase unique key for the model. By default, a model's identity is inferred automatically\nby lowercasing its filename. You should never change this property on your models.")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"identity"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"petModel"')]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("h3",{attrs:{id:"globalid"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#globalid","aria-hidden":"true"}},[t._v("#")]),t._v(" globalId")]),t._v(" "),a("p",[t._v("This flag changes the global name by which you can access your model (if the globalization of models\nis enabled). You should never change this property on your models.")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"globaId"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"pets"')]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("For example to access to your model function:")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[t._v("Pets"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("find")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("exec")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token keyword"}},[t._v("function")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("error"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" pets"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("error"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n console"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("log")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("error"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{attrs:{class:"token boolean"}},[t._v("false")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n console"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("log")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("pets"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),a("h3",{attrs:{id:"autopk"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#autopk","aria-hidden":"true"}},[t._v("#")]),t._v(" autoPK")]),t._v(" "),a("p",[t._v("A flag to toggle the automatic definition of a primary key in your model.\nThe details of this default primary key vary between adapters. In any case, the primary keys generated\nby "),a("code",[t._v("autoPK")]),t._v(" will be unique. If turned off no primary key will be created by default, and you will need\nto define one manually using "),a("code",[t._v("primaryKey: true")]),t._v(" for one of the model attributes.")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"autoPK"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token boolean"}},[t._v("true")]),a("span",{attrs:{class:"token operator"}},[t._v("|")]),a("span",{attrs:{class:"token boolean"}},[t._v("false")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("h3",{attrs:{id:"autocreatedat"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#autocreatedat","aria-hidden":"true"}},[t._v("#")]),t._v(" autoCreatedAt")]),t._v(" "),a("p",[t._v("A flag to toggle the automatic definition of a "),a("code",[t._v("createdAt")]),t._v(" attribute in your model.\nBy default, "),a("code",[t._v("createdAt")]),t._v(" is an attribute which will be automatically set when a record is created with\nthe current timestamp.")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"autoCreatedAt"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token boolean"}},[t._v("true")]),a("span",{attrs:{class:"token operator"}},[t._v("|")]),a("span",{attrs:{class:"token boolean"}},[t._v("false")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("h3",{attrs:{id:"autoupdatedat"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#autoupdatedat","aria-hidden":"true"}},[t._v("#")]),t._v(" autoUpdatedAt")]),t._v(" "),a("p",[t._v("A flag to toggle the automatic definition of a "),a("code",[t._v("updatedAt")]),t._v(" attribute in your model.\nBy default, "),a("code",[t._v("updatedAt")]),t._v(" is an attribute which will be automatically set with the current timestamp\nevery time a record is updated.")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"autoUpdatedAt"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token boolean"}},[t._v("true")]),a("span",{attrs:{class:"token operator"}},[t._v("|")]),a("span",{attrs:{class:"token boolean"}},[t._v("false")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("h3",{attrs:{id:"tablename"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#tablename","aria-hidden":"true"}},[t._v("#")]),t._v(" tableName")]),t._v(" "),a("p",[t._v("You can define a custom name for the physical collection in your adapter by adding a "),a("code",[t._v("tableName")]),t._v("\nattribute. This isn't just for tables. In MySQL, PostgreSQL, Oracle, etc. this setting refers\nto the name of the table, but in MongoDB or Redis, it refers to the collection, and so forth.\nIf no "),a("code",[t._v("tableName")]),t._v(" is specified, Waterline will use the model's "),a("code",[t._v("identity")]),t._v(" as its "),a("code",[t._v("tableName")]),t._v(".")]),t._v(" "),a("p",[t._v("This is particularly useful for working with pre-existing/legacy databases.")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"tableName"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"pets_table"')]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("h3",{attrs:{id:"attributes"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#attributes","aria-hidden":"true"}},[t._v("#")]),t._v(" attributes")]),t._v(" "),a("p",[t._v("Model attributes are basic pieces of information about a model.\nA model called "),a("code",[t._v("Pet")]),t._v(" might have attributes called "),a("code",[t._v("name")]),t._v(", "),a("code",[t._v("gender")]),t._v(", "),a("code",[t._v("age")]),t._v(",\n"),a("code",[t._v("birthday")]),t._v(" and "),a("code",[t._v("breed")]),t._v(".")]),t._v(" "),a("p",[t._v("Options can be used to enforce various constraints and add special enhancements to model attributes.")]),t._v(" "),a("h4",{attrs:{id:"type"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#type","aria-hidden":"true"}},[t._v("#")]),t._v(" type")]),t._v(" "),a("p",[t._v("Specifies the type of data that will be stored in this attribute. One of:")]),t._v(" "),a("ul",[a("li",[a("code",[t._v("string")])]),t._v(" "),a("li",[a("code",[t._v("text")])]),t._v(" "),a("li",[a("code",[t._v("integer")])]),t._v(" "),a("li",[a("code",[t._v("float")])]),t._v(" "),a("li",[a("code",[t._v("date")])]),t._v(" "),a("li",[a("code",[t._v("datetime")])]),t._v(" "),a("li",[a("code",[t._v("boolean")])]),t._v(" "),a("li",[a("code",[t._v("binary")])]),t._v(" "),a("li",[a("code",[t._v("array")])]),t._v(" "),a("li",[a("code",[t._v("json")])])]),t._v(" "),a("p",[t._v("Defaults to "),a("code",[t._v("string")]),t._v(" if not specified.")]),t._v(" "),a("h3",{attrs:{id:"validations"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#validations","aria-hidden":"true"}},[t._v("#")]),t._v(" Validations")]),t._v(" "),a("p",[t._v("Strapi bundles support for automatic validations of your models' attributes.\nAny time a record is updated, or a new record is created, the data for each attribute will\nbe checked against all of your predefined validation rules. This provides a convenient failsafe\nto ensure that invalid entries don't make their way into your application's database(s).")]),t._v(" "),a("p",[t._v("Validations are defined directly in your collection attributes.")]),t._v(" "),a("ul",[a("li",[a("code",[t._v("after")]),t._v(" (date): Checks if string date in this record is after the specified "),a("code",[t._v("Date")]),t._v(".\nMust be valid JavaScript "),a("code",[t._v("Date")]),t._v(".")]),t._v(" "),a("li",[a("code",[t._v("alpha")]),t._v(" (boolean): Checks if string in this record contains only letters (a-zA-Z).")]),t._v(" "),a("li",[a("code",[t._v("alphadashed")]),t._v(" (boolean): Checks if string in this record contains only numbers and/or dashes.")]),t._v(" "),a("li",[a("code",[t._v("alphanumeric")]),t._v(" (boolean): Checks if string in this record contains only letters and numbers.")]),t._v(" "),a("li",[a("code",[t._v("alphanumericdashed")]),t._v(" (boolean): Checks if string in this record contains only numbers and/or\nletters and/or dashes.")]),t._v(" "),a("li",[a("code",[t._v("array")]),t._v(" (boolean): Checks if this record is a valid JavaScript array object.\nStrings formatted as arrays will fail.")]),t._v(" "),a("li",[a("code",[t._v("before")]),t._v(" (date): Checks if string in this record is a date that's before the specified date.")]),t._v(" "),a("li",[a("code",[t._v("binary")]),t._v(" (boolean): Checks if this record is a valid binary data. Strings will pass.")]),t._v(" "),a("li",[a("code",[t._v("boolean")]),t._v(" (boolean): Checks if this record is a valid boolean. Strings will fail.")]),t._v(" "),a("li",[a("code",[t._v("contains")]),t._v(" (string): Checks if string in this record contains the seed.")]),t._v(" "),a("li",[a("code",[t._v("creditcard")]),t._v(" (boolean): Checks if string in this record is a credit card.")]),t._v(" "),a("li",[a("code",[t._v("date")]),t._v(" (boolean): Checks if string in this record is a date takes both strings and JavaScript.")]),t._v(" "),a("li",[a("code",[t._v("datetime")]),t._v(" (boolean): Checks if string in this record looks like a JavaScript "),a("code",[t._v("datetime")]),t._v(".")]),t._v(" "),a("li",[a("code",[t._v("decimal")]),t._v(" (boolean): Checks if it contains a decimal or is less than 1.")]),t._v(" "),a("li",[a("code",[t._v("email")]),t._v(" (boolean): Checks if string in this record looks like an email address.")]),t._v(" "),a("li",[a("code",[t._v("empty")]),t._v(" (boolean): Checks if the entry is empty. Arrays, strings, or arguments objects with\na length of 0 and objects with no\nown enumerable properties are considered empty.")]),t._v(" "),a("li",[a("code",[t._v("equals")]),t._v(" (integer): Checks if string in this record is equal to the specified value.\nThey must match in both value and type.")]),t._v(" "),a("li",[a("code",[t._v("falsey")]),t._v(" (boolean): Would a Javascript engine register a value of "),a("code",[t._v("false")]),t._v(" on this?.")]),t._v(" "),a("li",[a("code",[t._v("finite")]),t._v(" (boolean): Checks if given value is, or can be coerced to, a finite number.\nThis is not the same as native "),a("code",[t._v("isFinite")]),t._v("\nwhich will return "),a("code",[t._v("true")]),t._v(" for booleans and empty strings.")]),t._v(" "),a("li",[a("code",[t._v("float")]),t._v(" (boolean): Checks if string in this record is of the number type float.")]),t._v(" "),a("li",[a("code",[t._v("hexadecimal")]),t._v(" (boolean): Checks if string in this record is a hexadecimal number.")]),t._v(" "),a("li",[a("code",[t._v("hexColor")]),t._v(" (boolean): Checks if string in this record is a hexadecimal color.")]),t._v(" "),a("li",[a("code",[t._v("in")]),t._v(" (array): Checks if string in this record is in the specified array of allowed\nstring values.")]),t._v(" "),a("li",[a("code",[t._v("int")]),t._v(" (boolean): Check if string in this record is an integer.")]),t._v(" "),a("li",[a("code",[t._v("integer")]),t._v(" (boolean): Check if string in this record is an integer. Alias for "),a("code",[t._v("int")]),t._v(".")]),t._v(" "),a("li",[a("code",[t._v("ip")]),t._v(" (boolean): Checks if string in this record is a valid IP (v4 or v6).")]),t._v(" "),a("li",[a("code",[t._v("ipv4")]),t._v(" (boolean): Checks if string in this record is a valid IP v4.")]),t._v(" "),a("li",[a("code",[t._v("ipv6")]),t._v(" (boolean): Checks if string in this record is aa valid IP v6.")]),t._v(" "),a("li",[a("code",[t._v("json")]),t._v(" (boolean): Checks if the record is a JSON.")]),t._v(" "),a("li",[a("code",[t._v("lowercase")]),t._v(" (boolean): Check if string in this record is in all lowercase.")]),t._v(" "),a("li",[a("code",[t._v("max")]),t._v(" (integer): max value for an integer.")]),t._v(" "),a("li",[a("code",[t._v("maxLength")]),t._v(" (integer):")]),t._v(" "),a("li",[a("code",[t._v("min")]),t._v(" (integer): min value for an integer.")]),t._v(" "),a("li",[a("code",[t._v("minLength")]),t._v(" (integer):")]),t._v(" "),a("li",[a("code",[t._v("notContains")]),t._v(" (string): Checks if string in this record doesn't contain the seed.")]),t._v(" "),a("li",[a("code",[t._v("notIn")]),t._v(" (array): does the value of this model attribute exist inside of the defined\nvalidator value (of the same type).\nTakes strings and arrays.")]),t._v(" "),a("li",[a("code",[t._v("notNull")]),t._v(" (boolean): does this not have a value of "),a("code",[t._v("null")]),t._v(" ?.")]),t._v(" "),a("li",[a("code",[t._v("null")]),t._v(" (boolean): Checks if string in this record is null.")]),t._v(" "),a("li",[a("code",[t._v("number")]),t._v(" (boolean): Checks if this record is a number. "),a("code",[t._v("NaN")]),t._v(" is considered a number.")]),t._v(" "),a("li",[a("code",[t._v("numeric")]),t._v(" (boolean): Checks if string in this record contains only numbers.")]),t._v(" "),a("li",[a("code",[t._v("object")]),t._v(" (boolean): Checks if this attribute is the language type of Object.\nPasses for arrays, functions, objects,\nregexes, new Number(0), and new String('') !")]),t._v(" "),a("li",[a("code",[t._v("regex")]),t._v(" (regex): Checks if the record matches the specific regex.")]),t._v(" "),a("li",[a("code",[t._v("required")]),t._v(" (boolean): Must this model attribute contain valid data before a new\nrecord can be created?.")]),t._v(" "),a("li",[a("code",[t._v("string")]),t._v(" (boolean): Checks if the record is a string.")]),t._v(" "),a("li",[a("code",[t._v("text")]),t._v(" (boolean): Checks if the record is a text.")]),t._v(" "),a("li",[a("code",[t._v("truthy")]),t._v(" (boolean): Would a Javascript engine register a value of "),a("code",[t._v("false")]),t._v(" on this?")]),t._v(" "),a("li",[a("code",[t._v("undefined")]),t._v(" (boolean): Would a JavaScript engine register this thing as have the\nvalue "),a("code",[t._v("undefined")]),t._v("?")]),t._v(" "),a("li",[a("code",[t._v("uppercase")]),t._v(" (boolean): Checks if string in this record is uppercase.")]),t._v(" "),a("li",[a("code",[t._v("url")]),t._v(" (boolean): Checks if string in this record is a URL.")]),t._v(" "),a("li",[a("code",[t._v("urlish")]),t._v(" (boolean): Checks if string in this record contains something that looks like\na route, ending with a file extension.")]),t._v(" "),a("li",[a("code",[t._v("uuid")]),t._v(" (boolean): Checks if string in this record is a UUID (v3, v4, or v5).")]),t._v(" "),a("li",[a("code",[t._v("uuidv3")]),t._v(" (boolean): Checks if string in this record is a UUID (v3).")]),t._v(" "),a("li",[a("code",[t._v("uuidv4")]),t._v(" (boolean): Checks if string in this record is a UUID (v4).")])]),t._v(" "),a("h4",{attrs:{id:"defaultsto"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#defaultsto","aria-hidden":"true"}},[t._v("#")]),t._v(" defaultsTo")]),t._v(" "),a("p",[t._v("When a record is created, if no value was supplied, the record will be created with the specified\n"),a("code",[t._v("defaultsTo")]),t._v(" value.")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token string"}},[t._v('"attributes"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"usersGroup"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"type"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"string"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"defaultsTo"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"guess"')]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("h4",{attrs:{id:"autoincrement"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#autoincrement","aria-hidden":"true"}},[t._v("#")]),t._v(" autoIncrement")]),t._v(" "),a("p",[t._v("Sets up the attribute as an auto-increment key. When a new record is added to the model,\nif a value for this attribute is not specified, it will be generated by incrementing the most recent\nrecord's value by one.")]),t._v(" "),a("p",[t._v("Attributes which specify "),a("code",[t._v("autoIncrement")]),t._v(" should always be of "),a("code",[t._v("type: integer")]),t._v(".\nAlso, bear in mind that the level of support varies across different datastores.\nFor instance, MySQL will not allow more than one auto-incrementing column per table.")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token string"}},[t._v('"attributes"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"placeInLine"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"type"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"integer"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"autoIncrement"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token boolean"}},[t._v("true")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("h4",{attrs:{id:"unique"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#unique","aria-hidden":"true"}},[t._v("#")]),t._v(" unique")]),t._v(" "),a("p",[t._v("Ensures no two records will be allowed with the same value for the target attribute.\nThis is an adapter-level constraint, so in most cases this will result in a unique index on the\nattribute being created in the underlying datastore.")]),t._v(" "),a("p",[t._v("Defaults to "),a("code",[t._v("false")]),t._v(" if not specified.")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token string"}},[t._v('"attributes"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"username"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"type"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"string"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"unique"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token boolean"}},[t._v("true")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("h4",{attrs:{id:"primarykey"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#primarykey","aria-hidden":"true"}},[t._v("#")]),t._v(" primaryKey")]),t._v(" "),a("p",[t._v("Use this attribute as the the primary key for the record. Only one attribute per model can be the\n"),a("code",[t._v("primaryKey")]),t._v(". Defaults to "),a("code",[t._v("false")]),t._v(" if not specified.")]),t._v(" "),a("p",[t._v("This should never be used unless "),a("code",[t._v("autoPK")]),t._v(" is set to "),a("code",[t._v("false")]),t._v(".")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token string"}},[t._v('"attributes"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"uuid"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"type"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"string"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"primaryKey"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token boolean"}},[t._v("true")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"required"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token boolean"}},[t._v("true")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("h4",{attrs:{id:"enum"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#enum","aria-hidden":"true"}},[t._v("#")]),t._v(" enum")]),t._v(" "),a("p",[t._v("A special validation property which only saves data which matches a whitelisted set of values.")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token string"}},[t._v('"attributes"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"gender"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"type"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"string"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"enum"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{attrs:{class:"token string"}},[t._v('"male"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"female"')]),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("h4",{attrs:{id:"size"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#size","aria-hidden":"true"}},[t._v("#")]),t._v(" size")]),t._v(" "),a("p",[t._v("If supported in the adapter, can be used to define the size of the attribute.\nFor example in MySQL, "),a("code",[t._v("size")]),t._v(" can be specified as a number ("),a("code",[t._v("n")]),t._v(") to create a column with the SQL\ndata type: "),a("code",[t._v("varchar(n)")]),t._v(".")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token string"}},[t._v('"attributes"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"name"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"type"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"string"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"size"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("24")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("h4",{attrs:{id:"columnname"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#columnname","aria-hidden":"true"}},[t._v("#")]),t._v(" columnName")]),t._v(" "),a("p",[t._v("Inside an attribute definition, you can specify a "),a("code",[t._v("columnName")]),t._v(" to force Waterline to store data\nfor that attribute in a specific column in the configured connection.\nBe aware that this is not necessarily SQL-specific. It will also work for MongoDB fields, etc.")]),t._v(" "),a("p",[t._v("While the "),a("code",[t._v("columnName")]),t._v(" property is primarily designed for working with existing/legacy databases,\nit can also be useful in situations where your database is being shared by other applications,\nor you don't have access permissions to change the schema.")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token string"}},[t._v('"attributes"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"name"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"type"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"string"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"columnName"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"pet_name"')]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("h2",{attrs:{id:"associations"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#associations","aria-hidden":"true"}},[t._v("#")]),t._v(" Associations")]),t._v(" "),a("p",[t._v("With Waterline you can associate models with other models across all data stores.\nThis means that your users can live in PostgreSQL and their photos can live in MongoDB\nand you can interact with the data as if they lived together on the same database.\nYou can also have associations that live on separate connections or in different databases\nwithin the same adapter.")]),t._v(" "),a("h3",{attrs:{id:"one-way-associations"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#one-way-associations","aria-hidden":"true"}},[t._v("#")]),t._v(" One-Way associations")]),t._v(" "),a("p",[t._v("A one-way association is where a model is associated with another model.\nYou could query that model and populate to get the associated model.\nYou can't however query the associated model and populate to get the associating model.")]),t._v(" "),a("p",[t._v("In this example, we are associating a "),a("code",[t._v("User")]),t._v(" with a "),a("code",[t._v("Pet")]),t._v(" but not a "),a("code",[t._v("Pet")]),t._v(" with a "),a("code",[t._v("User")]),t._v(".\nBecause we have only formed an association on one of the models, a "),a("code",[t._v("Pet")]),t._v(" has no restrictions\non the number of "),a("code",[t._v("User")]),t._v(" models it can belong to. If we wanted to, we could change this and\nassociate the "),a("code",[t._v("Pet")]),t._v(" with exactly one "),a("code",[t._v("User")]),t._v(" and the "),a("code",[t._v("User")]),t._v(" with exactly one "),a("code",[t._v("Pet")]),t._v(".")]),t._v(" "),a("p",[a("code",[t._v("./api/pet/models/Pet.settings.json")]),t._v(":")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"attributes"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"name"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"type"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"string"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"required"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token boolean"}},[t._v("true")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"unique"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token boolean"}},[t._v("true")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"color"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"type"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"string"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"required"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token boolean"}},[t._v("true")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[a("code",[t._v("./api/user/models/User.settings.json")]),t._v(":")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"attributes"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"name"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"type"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"string"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"required"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token boolean"}},[t._v("true")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"unique"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token boolean"}},[t._v("true")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"color"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"type"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"string"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"required"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token boolean"}},[t._v("true")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"pony"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"model"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"pet"')]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("h3",{attrs:{id:"one-to-one-associations"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#one-to-one-associations","aria-hidden":"true"}},[t._v("#")]),t._v(" One-to-One associations")]),t._v(" "),a("p",[t._v("A one-to-one association states that a model may only be associated with one other model.\nIn order for the model to know which other model it is associated with a foreign key must\nbe included in the record.")]),t._v(" "),a("p",[t._v("In this example, we are associating a "),a("code",[t._v("Pet")]),t._v(" with a "),a("code",[t._v("User")]),t._v(". The "),a("code",[t._v("User")]),t._v(" may only have one\n"),a("code",[t._v("Pet")]),t._v(" and viceversa, a "),a("code",[t._v("Pet")]),t._v(" can only have one "),a("code",[t._v("User")]),t._v(". However, in order to query this association\nfrom both sides, you will have to create/update both models.")]),t._v(" "),a("p",[a("code",[t._v("./api/pet/models/Pet.settings.json")]),t._v(":")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"attributes"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"name"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"type"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"string"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"required"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token boolean"}},[t._v("true")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"unique"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token boolean"}},[t._v("true")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"color"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"type"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"string"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"required"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token boolean"}},[t._v("true")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"owner"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"model"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"user"')]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[a("code",[t._v("./api/user/models/User.settings.json")]),t._v(":")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"attributes"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"name"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"type"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"string"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"required"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token boolean"}},[t._v("true")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"unique"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token boolean"}},[t._v("true")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"age"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"type"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"integer"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"required"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token boolean"}},[t._v("true")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"pony"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"model"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"pet"')]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("h3",{attrs:{id:"one-to-many-associations"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#one-to-many-associations","aria-hidden":"true"}},[t._v("#")]),t._v(" One-to-Many associations")]),t._v(" "),a("p",[t._v("A one-to-many association states that a model can be associated with many other models.\nTo build this association a virtual attribute is added to a model using the "),a("code",[t._v("collection")]),t._v(" property.\nIn a one-to-many association one side must have a "),a("code",[t._v("collection")]),t._v(" attribute and the other side must contain a\n"),a("code",[t._v("model")]),t._v(" attribute. This allows the many side to know which records it needs to get when a "),a("code",[t._v("populate")]),t._v(" is used.")]),t._v(" "),a("p",[t._v("Because you may want a model to have multiple one-to-many associations on another model a "),a("code",[t._v("via")]),t._v(" key is\nneeded on the "),a("code",[t._v("collection")]),t._v(" attribute. This states which "),a("code",[t._v("model")]),t._v(" attribute on the one side of the association\nis used to populate the records.")]),t._v(" "),a("p",[t._v("In this example, a "),a("code",[t._v("User")]),t._v(" can have several "),a("code",[t._v("Pet")]),t._v(", but a "),a("code",[t._v("Pet")]),t._v(" has only one "),a("code",[t._v("owner")]),t._v(" (from the "),a("code",[t._v("User")]),t._v(" model).")]),t._v(" "),a("p",[a("code",[t._v("./api/pet/models/Pet.settings.json")]),t._v(":")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"attributes"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"name"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"type"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"string"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"required"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token boolean"}},[t._v("true")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"unique"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token boolean"}},[t._v("true")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"color"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"type"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"string"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"required"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token boolean"}},[t._v("true")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"owner"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"model"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"user"')]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[a("code",[t._v("./api/user/models/User.settings.json")]),t._v(":")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"attributes"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"name"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"type"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"string"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"required"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token boolean"}},[t._v("true")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"unique"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token boolean"}},[t._v("true")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"age"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"type"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"integer"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"required"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token boolean"}},[t._v("true")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"pets"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"collection"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"pet"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"via"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"owner"')]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("h3",{attrs:{id:"many-to-many-associations"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#many-to-many-associations","aria-hidden":"true"}},[t._v("#")]),t._v(" Many-to-Many associations")]),t._v(" "),a("p",[t._v("A many-to-many association states that a model can be associated with many other models\nand vice-versa. Because both models can have many related models a new join table will\nneed to be created to keep track of these relations.")]),t._v(" "),a("p",[t._v("Waterline will look at your models and if it finds that two models both have "),a("code",[t._v("collection")]),t._v("\nattributes that point to each other, it will automatically build up a join table for you.")]),t._v(" "),a("p",[t._v("Because you may want a model to have multiple many-to-many associations on another model\na "),a("code",[t._v("via")]),t._v(" key is needed on the "),a("code",[t._v("collection")]),t._v(" attribute. This states which "),a("code",[t._v("model")]),t._v(" attribute on the\none side of the association is used to populate the records.")]),t._v(" "),a("p",[t._v("Using the "),a("code",[t._v("User")]),t._v(" and "),a("code",[t._v("Pet")]),t._v(" example lets look at how to build a schema where a "),a("code",[t._v("User")]),t._v(" may\nhave many "),a("code",[t._v("Pet")]),t._v(" records and a "),a("code",[t._v("Pet")]),t._v(" may have multiple owners.")]),t._v(" "),a("p",[t._v("In this example, we will start with an array of users and an array of pets.\nWe will create records for each element in each array then associate all of the "),a("code",[t._v("Pets")]),t._v(" with all\nof the "),a("code",[t._v("Users")]),t._v(". If everything worked properly, we should be able to query any "),a("code",[t._v("User")]),t._v(" and see that\nthey "),a("em",[t._v("own")]),t._v(" all of the "),a("code",[t._v("Pets")]),t._v(". Furthermore, we should be able to query any "),a("code",[t._v("Pet")]),t._v(" and see that\nit is "),a("em",[t._v("owned")]),t._v(" by every "),a("code",[t._v("User")]),t._v(".")]),t._v(" "),a("p",[a("code",[t._v("./api/pet/models/Pet.settings.json")]),t._v(":")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"attributes"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"name"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"type"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"string"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"required"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token boolean"}},[t._v("true")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"unique"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token boolean"}},[t._v("true")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"color"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"type"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"string"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"required"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token boolean"}},[t._v("true")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"owners"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"collection"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"user"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"via"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"pets"')]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[a("code",[t._v("./api/user/models/User.settings.json")]),t._v(":")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"attributes"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"name"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"type"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"string"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"required"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token boolean"}},[t._v("true")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"unique"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token boolean"}},[t._v("true")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"age"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"type"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"integer"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"required"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token boolean"}},[t._v("true")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"pets"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"collection"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"pet"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"via"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"owners"')]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("h2",{attrs:{id:"lifecycle-callbacks"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#lifecycle-callbacks","aria-hidden":"true"}},[t._v("#")]),t._v(" Lifecycle Callbacks")]),t._v(" "),a("p",[t._v("Lifecycle callbacks are functions you can define to run at certain times in a query.\nThey are hooks that you can tap into in order to change data.")]),t._v(" "),a("p",[t._v("Strapi exposes a handful of lifecycle callbacks by default.")]),t._v(" "),a("h3",{attrs:{id:"callbacks-on-create"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#callbacks-on-create","aria-hidden":"true"}},[t._v("#")]),t._v(" Callbacks on create")]),t._v(" "),a("ul",[a("li",[a("code",[t._v("beforeValidate")]),t._v(": "),a("code",[t._v("fn(values, cb)")])]),t._v(" "),a("li",[a("code",[t._v("afterValidate")]),t._v(": "),a("code",[t._v("fn(values, cb)")])]),t._v(" "),a("li",[a("code",[t._v("beforeCreate")]),t._v(": "),a("code",[t._v("fn(values, cb)")])]),t._v(" "),a("li",[a("code",[t._v("afterCreate")]),t._v(": "),a("code",[t._v("fn(newlyInsertedRecord, cb)")])])]),t._v(" "),a("h3",{attrs:{id:"callbacks-on-update"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#callbacks-on-update","aria-hidden":"true"}},[t._v("#")]),t._v(" Callbacks on update")]),t._v(" "),a("ul",[a("li",[a("code",[t._v("beforeValidate: fn(valuesToUpdate, cb)")])]),t._v(" "),a("li",[a("code",[t._v("afterValidate: fn(valuesToUpdate, cb)")])]),t._v(" "),a("li",[a("code",[t._v("beforeUpdate: fn(valuesToUpdate, cb)")])]),t._v(" "),a("li",[a("code",[t._v("afterUpdate: fn(updatedRecord, cb)")])])]),t._v(" "),a("h3",{attrs:{id:"callbacks-on-destroy"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#callbacks-on-destroy","aria-hidden":"true"}},[t._v("#")]),t._v(" Callbacks on destroy")]),t._v(" "),a("ul",[a("li",[a("code",[t._v("beforeDestroy")]),t._v(": "),a("code",[t._v("fn(criteria, cb)")])]),t._v(" "),a("li",[a("code",[t._v("afterDestroy")]),t._v(": "),a("code",[t._v("fn(deletedRecord, cb)")])])]),t._v(" "),a("p",[t._v("For example, this could be your "),a("code",[t._v("./api/pet/models/Pet.js")]),t._v(" file:")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[t._v("module"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("exports "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token comment"}},[t._v("/**\n * Basic settings\n */")]),t._v("\n\n "),a("span",{attrs:{class:"token comment"}},[t._v("// The identity to use.")]),t._v("\n identity"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" settings"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("identity"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\n "),a("span",{attrs:{class:"token comment"}},[t._v("// The connection to use.")]),t._v("\n connection"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" settings"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("connection"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\n "),a("span",{attrs:{class:"token comment"}},[t._v("// Do you want to respect schema?")]),t._v("\n schema"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" settings"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("schema"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\n "),a("span",{attrs:{class:"token comment"}},[t._v("// Merge simple attributes from settings with those ones.")]),t._v("\n attributes"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" _"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("merge")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("settings"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("attributes"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\n "),a("span",{attrs:{class:"token comment"}},[t._v("// Do you automatically want to have time data?")]),t._v("\n autoCreatedAt"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" settings"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("autoCreatedAt"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n autoUpdatedAt"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" settings"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("autoUpdatedAt"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\n "),a("span",{attrs:{class:"token comment"}},[t._v("/**\n * Lifecycle callbacks on create\n */")]),t._v("\n\n "),a("span",{attrs:{class:"token comment"}},[t._v("// Before creating a value.")]),t._v("\n beforeCreate"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("function")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("values"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" next"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token comment"}},[t._v("// Do some stuff")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("next")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\n "),a("span",{attrs:{class:"token comment"}},[t._v("// After creating a value.")]),t._v("\n afterCreate"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("function")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("newlyInsertedRecord"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" next"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token comment"}},[t._v("// Do some stuff")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("next")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\n "),a("span",{attrs:{class:"token comment"}},[t._v("/**\n * Lifecycle callbacks on update\n */")]),t._v("\n\n "),a("span",{attrs:{class:"token comment"}},[t._v("// Before updating a value.")]),t._v("\n beforeUpdate"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("function")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("valuesToUpdate"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" next"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token comment"}},[t._v("// Do some stuff")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("next")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\n "),a("span",{attrs:{class:"token comment"}},[t._v("// After updating a value.")]),t._v("\n afterUpdate"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("function")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("updatedRecord"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" next"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token comment"}},[t._v("// Do some stuff")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("next")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\n "),a("span",{attrs:{class:"token comment"}},[t._v("/**\n * Lifecycle callbacks on destroy\n */")]),t._v("\n\n "),a("span",{attrs:{class:"token comment"}},[t._v("// Before destroying a value.")]),t._v("\n beforeDestroy"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("function")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("criteria"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" next"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token comment"}},[t._v("// Do some stuff")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("next")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\n "),a("span",{attrs:{class:"token comment"}},[t._v("// After destroying a value.")]),t._v("\n afterDestroy"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("function")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("destroyedRecords"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" next"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token comment"}},[t._v("// Do some stuff")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("next")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])])}],!1,null,null,null);e.options.__file="models.md";s.default=e.exports}}]); \ No newline at end of file diff --git a/docs/.vuepress/dist/assets/js/17.1c93d494.js b/docs/.vuepress/dist/assets/js/17.1c93d494.js new file mode 100644 index 0000000000..f1a4a58550 --- /dev/null +++ b/docs/.vuepress/dist/assets/js/17.1c93d494.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[17],{222:function(t,a,s){"use strict";s.r(a);var n=s(0),e=Object(n.a)({},function(){this.$createElement;this._self._c;return this._m(0)},[function(){var t=this,a=t.$createElement,s=t._self._c||a;return s("div",{staticClass:"content"},[s("h1",{attrs:{id:"query-interface"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#query-interface","aria-hidden":"true"}},[t._v("#")]),t._v(" Query Interface")]),t._v(" "),s("p",[t._v("The Waterline Query Interface allows you to interact with your models the same\nway no matter which adapter they are using. This means you can use the same Query\nLanguage whether your data lives in MySQL, MongoDB, PostgreSQL, etc.")]),t._v(" "),s("h2",{attrs:{id:"query-methods"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#query-methods","aria-hidden":"true"}},[t._v("#")]),t._v(" Query Methods")]),t._v(" "),s("p",[t._v("Every model in Waterline will have a set of query methods exposed on it to allow\nyou to interact with the database in a normalized fashion.\nThese are known as the CRUD ("),s("code",[t._v("Create")]),t._v(", "),s("code",[t._v("Read")]),t._v(", "),s("code",[t._v("Update")]),t._v(" and "),s("code",[t._v("Delete")]),t._v(") methods and\nis the primary way of interacting with your data.")]),t._v(" "),s("p",[t._v("There are also a special set of queries known as "),s("em",[t._v("dynamic queries")]),t._v(".\nThese are special class methods that are dynamically generated when you initialize Waterline.\nWe call them dynamic finders. They perform many of the same functions as the other class\nmethods but you can call them directly on an attribute in your model.")]),t._v(" "),s("p",[t._v("For most class methods, the callback parameter is optional and if one is not supplied,\nit will return a chainable object.")]),t._v(" "),s("h3",{attrs:{id:"find-criteria-callback"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#find-criteria-callback","aria-hidden":"true"}},[t._v("#")]),t._v(" .find(criteria, [callback])")]),t._v(" "),s("p",[s("code",[t._v("find")]),t._v(" will return an array of records that match the supplied criteria.\nCriteria can be built using the Query Language.")]),t._v(" "),s("ul",[s("li",[t._v("The "),s("code",[t._v("criteria")]),t._v(" is required and accepts "),s("code",[t._v("{}")]),t._v(", "),s("code",[t._v("[{}]")]),t._v(", "),s("code",[t._v("string")]),t._v(" and "),s("code",[t._v("int")]),t._v(" data types.")]),t._v(" "),s("li",[t._v("The "),s("code",[t._v("callback")]),t._v(" function is optional.")])]),t._v(" "),s("p",[t._v("Any string arguments passed must be the ID of the record.\nThis method will always return records in an array.\nIf you are trying to find an attribute that is an array, you must wrap it in an additional\nset of brackets otherwise Waterline will think you want to perform an "),s("code",[t._v("inQuery")]),t._v(".")]),t._v(" "),s("div",{staticClass:"language-js extra-class"},[s("pre",{pre:!0,attrs:{class:"language-js"}},[s("code",[t._v("User"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("find")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n name"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token string"}},[t._v("'Walter Jr'")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("exec")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token keyword"}},[t._v("function")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err"),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" users"),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err"),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n console"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("log")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err"),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n console"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("log")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("users"),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),s("h3",{attrs:{id:"findone-criteria-callback"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#findone-criteria-callback","aria-hidden":"true"}},[t._v("#")]),t._v(" .findOne(criteria, [callback])")]),t._v(" "),s("p",[s("code",[t._v("findOne")]),t._v(" will return an object with the first matching result in the data store.")]),t._v(" "),s("ul",[s("li",[t._v("The "),s("code",[t._v("criteria")]),t._v(" is required and accepts "),s("code",[t._v("{}")]),t._v(", "),s("code",[t._v("[{}]")]),t._v(", "),s("code",[t._v("string")]),t._v(" and "),s("code",[t._v("int")]),t._v(" data types.")]),t._v(" "),s("li",[t._v("The "),s("code",[t._v("callback")]),t._v(" function is optional.")])]),t._v(" "),s("p",[t._v("Any string arguments passed must be the ID of the record.\nIf you are trying to find an attribute that is an array, you must wrap it in an additional\nset of brackets otherwise Waterline will think you want to perform an "),s("code",[t._v("inQuery")]),t._v(".")]),t._v(" "),s("div",{staticClass:"language-js extra-class"},[s("pre",{pre:!0,attrs:{class:"language-js"}},[s("code",[t._v("User"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("findOne")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n name"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token string"}},[t._v("'Walter Jr'")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("exec")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token keyword"}},[t._v("function")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err"),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" user"),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err"),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n console"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("log")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err"),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n console"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("log")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("user"),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),s("h3",{attrs:{id:"create-criteria-callback"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#create-criteria-callback","aria-hidden":"true"}},[t._v("#")]),t._v(" .create(criteria, [callback])")]),t._v(" "),s("p",[s("code",[t._v("create")]),t._v(" will attempt to create a new record in the datastore.\nIf the data is valid and passes all validations it will be sent to the adapters "),s("code",[t._v("create")]),t._v(" method.")]),t._v(" "),s("ul",[s("li",[t._v("The "),s("code",[t._v("criteria")]),t._v(" is required and accepts "),s("code",[t._v("{}")]),t._v(" and "),s("code",[t._v("[{}]")]),t._v(" data types.")]),t._v(" "),s("li",[t._v("The "),s("code",[t._v("callback")]),t._v(" function is optional.")])]),t._v(" "),s("div",{staticClass:"language-js extra-class"},[s("pre",{pre:!0,attrs:{class:"language-js"}},[s("code",[t._v("User"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("create")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n name"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token string"}},[t._v("'Walter Jr'")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("exec")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token keyword"}},[t._v("function")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err"),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" user"),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err"),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n console"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("log")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err"),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n console"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("log")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("user"),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),s("h3",{attrs:{id:"findorcreate-criteria-values-callback"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#findorcreate-criteria-values-callback","aria-hidden":"true"}},[t._v("#")]),t._v(" .findOrCreate(criteria, [values, callback])")]),t._v(" "),s("p",[s("code",[t._v("findOrCreate")]),t._v(" will return a single record if one was found or created,\nor an array of records if multiple get found/created via the supplied criteria or values.\nCriteria can be built using the Query Language.")]),t._v(" "),s("ul",[s("li",[t._v("The "),s("code",[t._v("criteria")]),t._v(" is required and accepts "),s("code",[t._v("{}")]),t._v(", "),s("code",[t._v("[{}]")]),t._v(", "),s("code",[t._v("string")]),t._v(" and "),s("code",[t._v("int")]),t._v(" data types.")]),t._v(" "),s("li",[t._v("The "),s("code",[t._v("values")]),t._v(" is optional and accepts "),s("code",[t._v("{}")]),t._v(" and "),s("code",[t._v("[{}]")]),t._v(" data types.")]),t._v(" "),s("li",[t._v("The "),s("code",[t._v("callback")]),t._v(" function is optional.")])]),t._v(" "),s("p",[t._v("Any string arguments passed must be the ID of the record.\nThis method can return a single record or an array of records.\nIf a model is not found and creation values are omitted, it will get created with the supplied criteria values.")]),t._v(" "),s("p",[t._v("Unless an adapter implements its own version of "),s("code",[t._v("findOrCreate")]),t._v(", "),s("code",[t._v("findOrCreate")]),t._v(" will do the\n"),s("code",[t._v("find")]),t._v(" and "),s("code",[t._v("create")]),t._v(" calls in two separate steps (not transactional).\nIn a high frequency scenario it's possible for duplicates to be created if the query field(s) are not indexed.")]),t._v(" "),s("p",[t._v('Either user(s) with the name "Walter Jr" get returned or\na single user gets created with the name "Walter Jr" and returned:')]),t._v(" "),s("div",{staticClass:"language-js extra-class"},[s("pre",{pre:!0,attrs:{class:"language-js"}},[s("code",[t._v("User"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("findOrCreate")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n name"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token string"}},[t._v("'Walter Jr'")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("exec")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token keyword"}},[t._v("function")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err"),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" users"),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err"),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n console"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("log")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err"),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n console"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("log")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("users"),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),s("h3",{attrs:{id:"update-search-criteria-values-callback"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#update-search-criteria-values-callback","aria-hidden":"true"}},[t._v("#")]),t._v(" .update(search criteria, values, [callback])")]),t._v(" "),s("p",[s("code",[t._v("update")]),t._v(" will attempt to update any records matching the criteria passed in.\nCriteria can be built using the Query Language.")]),t._v(" "),s("ul",[s("li",[t._v("The "),s("code",[t._v("criteria")]),t._v(" is required and accepts "),s("code",[t._v("{}")]),t._v(", "),s("code",[t._v("[{}]")]),t._v(", "),s("code",[t._v("string")]),t._v(" and "),s("code",[t._v("int")]),t._v(" data types.")]),t._v(" "),s("li",[t._v("The "),s("code",[t._v("values")]),t._v(" is required and accepts "),s("code",[t._v("{}")]),t._v(" and "),s("code",[t._v("[{}]")]),t._v(" data types.")]),t._v(" "),s("li",[t._v("The "),s("code",[t._v("callback")]),t._v(" function is optional.")])]),t._v(" "),s("p",[t._v("Although you may pass "),s("code",[t._v(".update()")]),t._v(" an object or an array of objects,\nit will always return an array of objects. Any string arguments passed must be the ID\nof the record. If you specify a primary key instead of a criteria object,\nany "),s("code",[t._v(".where()")]),t._v(" filters will be ignored.")]),t._v(" "),s("div",{staticClass:"language-js extra-class"},[s("pre",{pre:!0,attrs:{class:"language-js"}},[s("code",[t._v("User"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("update")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n name"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token string"}},[t._v("'Walter Jr'")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n name"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token string"}},[t._v("'Flynn'")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("exec")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token keyword"}},[t._v("function")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err"),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" user"),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err"),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n console"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("log")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err"),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n console"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("log")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("user"),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),s("h3",{attrs:{id:"destroy-criteria-callback"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#destroy-criteria-callback","aria-hidden":"true"}},[t._v("#")]),t._v(" .destroy(criteria, [callback])")]),t._v(" "),s("p",[s("code",[t._v("destroy")]),t._v(" will destroy any records matching the provided criteria.\nCriteria can be built using the Query Language.")]),t._v(" "),s("ul",[s("li",[t._v("The "),s("code",[t._v("criteria")]),t._v(" is required and accepts "),s("code",[t._v("{}")]),t._v(", "),s("code",[t._v("[{}]")]),t._v(", "),s("code",[t._v("string")]),t._v(" and "),s("code",[t._v("int")]),t._v(" data types.")]),t._v(" "),s("li",[t._v("The "),s("code",[t._v("callback")]),t._v(" function is optional.")])]),t._v(" "),s("p",[t._v("If you want to confirm the record exists before you delete it,\nyou must first perform a "),s("code",[t._v(".find()")]),t._v(". Any string arguments passed must be the ID of the record.")]),t._v(" "),s("div",{staticClass:"language-js extra-class"},[s("pre",{pre:!0,attrs:{class:"language-js"}},[s("code",[t._v("User"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("destroy")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n name"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token string"}},[t._v("'Flynn'")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("exec")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token keyword"}},[t._v("function")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err"),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err"),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n console"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("log")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err"),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),s("h3",{attrs:{id:"query-query-data-callback"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#query-query-data-callback","aria-hidden":"true"}},[t._v("#")]),t._v(" .query(query, [data], callback)")]),t._v(" "),s("p",[t._v("Some adapters, such as "),s("code",[t._v("sails-mysql")]),t._v(" and "),s("code",[t._v("sails-postgresql")]),t._v(", support the query function\nwhich will run the provided RAW query against the database.\nThis can sometimes be useful if you want to run complex queries and performance is very important.")]),t._v(" "),s("ul",[s("li",[t._v("The "),s("code",[t._v("query")]),t._v(" is required and accepts "),s("code",[t._v("string")]),t._v(" data types.")]),t._v(" "),s("li",[t._v("The "),s("code",[t._v("data")]),t._v(" is optional and accepts "),s("code",[t._v("array")]),t._v(" data types.")]),t._v(" "),s("li",[t._v("The "),s("code",[t._v("callback")]),t._v(" function is required.")])]),t._v(" "),s("p",[t._v("The type of the results returned depend on your adapter: "),s("code",[t._v("sails-mysql")]),t._v(" returns an array of objects\nand "),s("code",[t._v("sails-postgresql")]),t._v(" returns an object containing metadata and the actual results within a 'rows' array.\nThis function does currently not support promises.")]),t._v(" "),s("p",[t._v("Using PostgreSQL:")]),t._v(" "),s("div",{staticClass:"language-js extra-class"},[s("pre",{pre:!0,attrs:{class:"language-js"}},[s("code",[s("span",{attrs:{class:"token keyword"}},[t._v("const")]),t._v(" title "),s("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{attrs:{class:"token string"}},[t._v('"The King\'s Speech"')]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\nMovie"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("query")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token string"}},[t._v("'SELECT * FROM movie WHERE title = $1'")]),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("[")]),t._v("title"),s("span",{attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{attrs:{class:"token keyword"}},[t._v("function")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err"),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" results"),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n console"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("log")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token string"}},[t._v("'Found the following movie: '")]),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" results"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("rows"),s("span",{attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{attrs:{class:"token number"}},[t._v("0")]),s("span",{attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),s("p",[t._v("Using MySQL:")]),t._v(" "),s("div",{staticClass:"language-js extra-class"},[s("pre",{pre:!0,attrs:{class:"language-js"}},[s("code",[s("span",{attrs:{class:"token keyword"}},[t._v("const")]),t._v(" title "),s("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{attrs:{class:"token string"}},[t._v('"The King\'s Speech"')]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\nMovie"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("query")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token string"}},[t._v("'SELECT * FROM movie WHERE title = $1'")]),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("[")]),t._v("title"),s("span",{attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{attrs:{class:"token keyword"}},[t._v("function")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err"),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" results"),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n console"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("log")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token string"}},[t._v("'Found the following movie: '")]),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" results"),s("span",{attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{attrs:{class:"token number"}},[t._v("0")]),s("span",{attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),s("h2",{attrs:{id:"query-language"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#query-language","aria-hidden":"true"}},[t._v("#")]),t._v(" Query Language")]),t._v(" "),s("p",[t._v("The Waterline Query Language is an object based criteria used to retrieve the\nrecords from any of the supported database adapters.\nThis allows you to change your database without changing your codebase.")]),t._v(" "),s("p",[t._v("All queries inside of Waterline are case insensitive. This allows for easier querying\nbut makes indexing strings tough. This is something to be aware of if you are\nindexing and searching on string fields.")]),t._v(" "),s("h3",{attrs:{id:"query-language-basics"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#query-language-basics","aria-hidden":"true"}},[t._v("#")]),t._v(" Query Language Basics")]),t._v(" "),s("p",[t._v("The criteria objects are formed using one of four types of object keys.\nThese are the top level keys used in a query object. It is loosely based on the\ncriteria used in MongoDB with a few slight variations.")]),t._v(" "),s("p",[t._v("Queries can be built using either a "),s("code",[t._v("where")]),t._v(" key to specify attributes,\nwhich will allow you to also use query options such as "),s("code",[t._v("limit")]),t._v(" and "),s("code",[t._v("skip")]),t._v(" or\nif "),s("code",[t._v("where")]),t._v(" is excluded the entire object will be treated as a "),s("code",[t._v("where")]),t._v(" criteria.")]),t._v(" "),s("div",{staticClass:"language-js extra-class"},[s("pre",{pre:!0,attrs:{class:"language-js"}},[s("code",[t._v("User"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("find")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n where"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n name"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token string"}},[t._v("'John'")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n skip"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token number"}},[t._v("20")]),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n limit"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token number"}},[t._v("10")]),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n sort"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token string"}},[t._v("'name DESC'")]),t._v("\n"),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),s("p",[t._v("Or:")]),t._v(" "),s("div",{staticClass:"language-js extra-class"},[s("pre",{pre:!0,attrs:{class:"language-js"}},[s("code",[t._v("User"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("find")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n name"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token string"}},[t._v("'John'")]),t._v("\n"),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),s("h4",{attrs:{id:"key-pairs"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#key-pairs","aria-hidden":"true"}},[t._v("#")]),t._v(" Key Pairs")]),t._v(" "),s("p",[t._v("A key pair can be used to search records for values matching exactly what is specified.\nThis is the base of a criteria object where the key represents an attribute on a model\nand the value is a strict equality check of the records for matching values.")]),t._v(" "),s("div",{staticClass:"language-js extra-class"},[s("pre",{pre:!0,attrs:{class:"language-js"}},[s("code",[t._v("User"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("find")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n name"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token string"}},[t._v("'John'")]),t._v("\n"),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),s("p",[t._v("They can be used together to search multiple attributes:")]),t._v(" "),s("div",{staticClass:"language-js extra-class"},[s("pre",{pre:!0,attrs:{class:"language-js"}},[s("code",[t._v("User"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("find")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n name"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token string"}},[t._v("'John'")]),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n country"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token string"}},[t._v("'France'")]),t._v("\n"),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),s("h4",{attrs:{id:"modified-pairs"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#modified-pairs","aria-hidden":"true"}},[t._v("#")]),t._v(" Modified Pairs")]),t._v(" "),s("p",[t._v("Modified pairs also have model attributes for keys but they also use any of the\nsupported criteria modifiers to perform queries where a strict equality check wouldn't work.")]),t._v(" "),s("div",{staticClass:"language-js extra-class"},[s("pre",{pre:!0,attrs:{class:"language-js"}},[s("code",[t._v("User"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("find")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n name"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n contains"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token string"}},[t._v("'alt'")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),s("h4",{attrs:{id:"in-pairs"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#in-pairs","aria-hidden":"true"}},[t._v("#")]),t._v(" In Pairs")]),t._v(" "),s("p",[t._v("In queries work similarly to MySQL "),s("code",[t._v("in")]),t._v(" queries. Each element in the array is treated as "),s("code",[t._v("or")]),t._v(".")]),t._v(" "),s("div",{staticClass:"language-js extra-class"},[s("pre",{pre:!0,attrs:{class:"language-js"}},[s("code",[t._v("User"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("find")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n name"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{attrs:{class:"token string"}},[t._v("'John'")]),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{attrs:{class:"token string"}},[t._v("'Walter'")]),s("span",{attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n"),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),s("h4",{attrs:{id:"not-in-pairs"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#not-in-pairs","aria-hidden":"true"}},[t._v("#")]),t._v(" Not-In Pairs")]),t._v(" "),s("p",[t._v("Not-In queries work similar to "),s("code",[t._v("in")]),t._v(" queries, except for the nested object criteria.")]),t._v(" "),s("div",{staticClass:"language-js extra-class"},[s("pre",{pre:!0,attrs:{class:"language-js"}},[s("code",[t._v("User"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("find")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n name"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{attrs:{class:"token string"}},[t._v("'!'")]),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{attrs:{class:"token string"}},[t._v("'John'")]),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{attrs:{class:"token string"}},[t._v("'Walter'")]),s("span",{attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),s("h4",{attrs:{id:"or-pairs"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#or-pairs","aria-hidden":"true"}},[t._v("#")]),t._v(" Or Pairs")]),t._v(" "),s("p",[t._v("Performing "),s("code",[t._v("OR")]),t._v(" queries is done by using an array of query pairs.\nResults will be returned that match any of the criteria objects inside the array.")]),t._v(" "),s("div",{staticClass:"language-js extra-class"},[s("pre",{pre:!0,attrs:{class:"language-js"}},[s("code",[t._v("User"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("find")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n or"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("[")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n name"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token string"}},[t._v("'John'")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n occupation"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token string"}},[t._v("'Developer'")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n"),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),s("h3",{attrs:{id:"criteria-modifiers"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#criteria-modifiers","aria-hidden":"true"}},[t._v("#")]),t._v(" Criteria Modifiers")]),t._v(" "),s("p",[t._v("The following modifiers are available to use when building queries:")]),t._v(" "),s("ul",[s("li",[s("code",[t._v("<")]),t._v(" or "),s("code",[t._v("lessThan")])]),t._v(" "),s("li",[s("code",[t._v("<=")]),t._v(" or "),s("code",[t._v("lessThanOrEqual")])]),t._v(" "),s("li",[s("code",[t._v(">")]),t._v(" or "),s("code",[t._v("greaterThan")])]),t._v(" "),s("li",[s("code",[t._v(">=")]),t._v(" or "),s("code",[t._v("greaterThanOrEqual")])]),t._v(" "),s("li",[s("code",[t._v("!")]),t._v(" or "),s("code",[t._v("not")])]),t._v(" "),s("li",[s("code",[t._v("like")])]),t._v(" "),s("li",[s("code",[t._v("contains")])]),t._v(" "),s("li",[s("code",[t._v("startsWith")])]),t._v(" "),s("li",[s("code",[t._v("endsWith")])])]),t._v(" "),s("h4",{attrs:{id:"or-lessthan"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#or-lessthan","aria-hidden":"true"}},[t._v("#")]),t._v(" < or lessThan")]),t._v(" "),s("p",[t._v("Searches for records where the value is less than the value specified.")]),t._v(" "),s("div",{staticClass:"language-js extra-class"},[s("pre",{pre:!0,attrs:{class:"language-js"}},[s("code",[t._v("User"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("find")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n age"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{attrs:{class:"token string"}},[t._v("'<'")]),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token number"}},[t._v("30")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),s("h4",{attrs:{id:"or-lessthanorequal"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#or-lessthanorequal","aria-hidden":"true"}},[t._v("#")]),t._v(" <= or lessThanOrEqual")]),t._v(" "),s("p",[t._v("Searches for records where the value is less or equal to the value specified.")]),t._v(" "),s("div",{staticClass:"language-js extra-class"},[s("pre",{pre:!0,attrs:{class:"language-js"}},[s("code",[t._v("User"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("find")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n age"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{attrs:{class:"token string"}},[t._v("'<='")]),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token number"}},[t._v("21")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),s("h4",{attrs:{id:"or-greaterthan"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#or-greaterthan","aria-hidden":"true"}},[t._v("#")]),t._v(" > or greaterThan")]),t._v(" "),s("p",[t._v("Searches for records where the value is more than the value specified.")]),t._v(" "),s("div",{staticClass:"language-js extra-class"},[s("pre",{pre:!0,attrs:{class:"language-js"}},[s("code",[t._v("User"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("find")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n age"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{attrs:{class:"token string"}},[t._v("'>'")]),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token number"}},[t._v("18")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),s("h4",{attrs:{id:"or-greaterthanorequal"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#or-greaterthanorequal","aria-hidden":"true"}},[t._v("#")]),t._v(" >= or greaterThanOrEqual")]),t._v(" "),s("p",[t._v("Searches for records where the value is more or equal to the value specified.")]),t._v(" "),s("div",{staticClass:"language-js extra-class"},[s("pre",{pre:!0,attrs:{class:"language-js"}},[s("code",[t._v("User"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("find")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n age"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{attrs:{class:"token string"}},[t._v("'>='")]),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token number"}},[t._v("21")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),s("h4",{attrs:{id:"or-not"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#or-not","aria-hidden":"true"}},[t._v("#")]),t._v(" ! or not")]),t._v(" "),s("p",[t._v("Searches for records where the value is not equal to the value specified.")]),t._v(" "),s("div",{staticClass:"language-js extra-class"},[s("pre",{pre:!0,attrs:{class:"language-js"}},[s("code",[t._v("User"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("find")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n name"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{attrs:{class:"token string"}},[t._v("'!'")]),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token string"}},[t._v("'John'")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),s("h4",{attrs:{id:"like"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#like","aria-hidden":"true"}},[t._v("#")]),t._v(" like")]),t._v(" "),s("p",[t._v("Searches for records using pattern matching with the "),s("code",[t._v("%")]),t._v(" sign.")]),t._v(" "),s("div",{staticClass:"language-js extra-class"},[s("pre",{pre:!0,attrs:{class:"language-js"}},[s("code",[t._v("User"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("find")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n food"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{attrs:{class:"token string"}},[t._v("'like'")]),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token string"}},[t._v("'%burgers'")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),s("h4",{attrs:{id:"contains"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#contains","aria-hidden":"true"}},[t._v("#")]),t._v(" contains")]),t._v(" "),s("p",[t._v("A shorthand for pattern matching both sides of a string.\nWill return records where the value contains the string anywhere inside of it.")]),t._v(" "),s("div",{staticClass:"language-js extra-class"},[s("pre",{pre:!0,attrs:{class:"language-js"}},[s("code",[t._v("User"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("find")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{attrs:{class:"token keyword"}},[t._v("class")]),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{attrs:{class:"token string"}},[t._v("'like'")]),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token string"}},[t._v("'%history%'")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),s("div",{staticClass:"language-js extra-class"},[s("pre",{pre:!0,attrs:{class:"language-js"}},[s("code",[t._v("User"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("find")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{attrs:{class:"token keyword"}},[t._v("class")]),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{attrs:{class:"token string"}},[t._v("'contains'")]),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token string"}},[t._v("'history'")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),s("h4",{attrs:{id:"startswith"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#startswith","aria-hidden":"true"}},[t._v("#")]),t._v(" startsWith")]),t._v(" "),s("p",[t._v("A shorthand for pattern matching the right side of a string\nWill return records where the value starts with the supplied string value.")]),t._v(" "),s("div",{staticClass:"language-js extra-class"},[s("pre",{pre:!0,attrs:{class:"language-js"}},[s("code",[t._v("User"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("find")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{attrs:{class:"token keyword"}},[t._v("class")]),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{attrs:{class:"token string"}},[t._v("'startsWith'")]),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token string"}},[t._v("'french'")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),s("div",{staticClass:"language-js extra-class"},[s("pre",{pre:!0,attrs:{class:"language-js"}},[s("code",[t._v("User"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("find")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{attrs:{class:"token keyword"}},[t._v("class")]),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{attrs:{class:"token string"}},[t._v("'like'")]),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token string"}},[t._v("'french%'")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),s("h4",{attrs:{id:"endswith"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#endswith","aria-hidden":"true"}},[t._v("#")]),t._v(" endsWith")]),t._v(" "),s("p",[t._v("A shorthand for pattern matching the left side of a string.\nWill return records where the value ends with the supplied string value.")]),t._v(" "),s("div",{staticClass:"language-js extra-class"},[s("pre",{pre:!0,attrs:{class:"language-js"}},[s("code",[t._v("User"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("find")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{attrs:{class:"token keyword"}},[t._v("class")]),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{attrs:{class:"token string"}},[t._v("'endsWith'")]),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token string"}},[t._v("'can'")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),s("div",{staticClass:"language-js extra-class"},[s("pre",{pre:!0,attrs:{class:"language-js"}},[s("code",[t._v("User"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("find")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{attrs:{class:"token keyword"}},[t._v("class")]),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{attrs:{class:"token string"}},[t._v("'like'")]),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token string"}},[t._v("'%can'")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),s("h4",{attrs:{id:"date-ranges"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#date-ranges","aria-hidden":"true"}},[t._v("#")]),t._v(" Date Ranges")]),t._v(" "),s("p",[t._v("You can do date range queries using the comparison operators.")]),t._v(" "),s("div",{staticClass:"language-js extra-class"},[s("pre",{pre:!0,attrs:{class:"language-js"}},[s("code",[t._v("User"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("find")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n date"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{attrs:{class:"token string"}},[t._v("'>'")]),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token keyword"}},[t._v("new")]),t._v(" "),s("span",{attrs:{class:"token class-name"}},[t._v("Date")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token string"}},[t._v("'2/4/2014'")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{attrs:{class:"token string"}},[t._v("'<'")]),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token keyword"}},[t._v("new")]),t._v(" "),s("span",{attrs:{class:"token class-name"}},[t._v("Date")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token string"}},[t._v("'2/7/2014'")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),s("h3",{attrs:{id:"query-options"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#query-options","aria-hidden":"true"}},[t._v("#")]),t._v(" Query Options")]),t._v(" "),s("p",[t._v("Query options allow you refine the results that are returned from a query.")]),t._v(" "),s("h4",{attrs:{id:"limit-results"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#limit-results","aria-hidden":"true"}},[t._v("#")]),t._v(" Limit results")]),t._v(" "),s("p",[t._v("Limit the number of results returned from a query.")]),t._v(" "),s("div",{staticClass:"language-js extra-class"},[s("pre",{pre:!0,attrs:{class:"language-js"}},[s("code",[t._v("User"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("find")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n where"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n name"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token string"}},[t._v("'John'")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n limit"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token number"}},[t._v("20")]),t._v("\n"),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),s("h4",{attrs:{id:"skip-results"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#skip-results","aria-hidden":"true"}},[t._v("#")]),t._v(" Skip results")]),t._v(" "),s("p",[t._v("Return all the results excluding the number of items to skip.")]),t._v(" "),s("div",{staticClass:"language-js extra-class"},[s("pre",{pre:!0,attrs:{class:"language-js"}},[s("code",[t._v("User"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("find")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n where"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n name"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token string"}},[t._v("'John'")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n skip"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token number"}},[t._v("10")]),t._v("\n"),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),s("h4",{attrs:{id:"pagination"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#pagination","aria-hidden":"true"}},[t._v("#")]),t._v(" Pagination")]),t._v(" "),s("p",[s("code",[t._v("skip")]),t._v(" and "),s("code",[t._v("limit")]),t._v(" can be used together to build up a pagination system.")]),t._v(" "),s("div",{staticClass:"language-js extra-class"},[s("pre",{pre:!0,attrs:{class:"language-js"}},[s("code",[t._v("User"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("find")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n where"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n name"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token string"}},[t._v("'John'")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n limit"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token number"}},[t._v("10")]),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n skip"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token number"}},[t._v("10")]),t._v("\n"),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),s("h4",{attrs:{id:"sort-results"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#sort-results","aria-hidden":"true"}},[t._v("#")]),t._v(" Sort results")]),t._v(" "),s("p",[t._v("Results can be sorted by attribute name. Simply specify an attribute name for\nnatural (ascending) sort, or specify an "),s("code",[t._v("asc")]),t._v(" or "),s("code",[t._v("desc")]),t._v(" flag for ascending or\ndescending orders respectively.")]),t._v(" "),s("p",[t._v("Sort by name in ascending order:")]),t._v(" "),s("div",{staticClass:"language-js extra-class"},[s("pre",{pre:!0,attrs:{class:"language-js"}},[s("code",[t._v("User"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("find")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n where"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n name"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token string"}},[t._v("'John'")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n sort"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token string"}},[t._v("'name'")]),t._v("\n"),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),s("p",[t._v("Sort by name in descending order:")]),t._v(" "),s("div",{staticClass:"language-js extra-class"},[s("pre",{pre:!0,attrs:{class:"language-js"}},[s("code",[t._v("User"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("find")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n where"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n name"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token string"}},[t._v("'John'")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n sort"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token string"}},[t._v("'name DESC'")]),t._v("\n"),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),s("p",[t._v("Sort by name in ascending order:")]),t._v(" "),s("div",{staticClass:"language-js extra-class"},[s("pre",{pre:!0,attrs:{class:"language-js"}},[s("code",[t._v("User"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("find")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n where"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n name"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token string"}},[t._v("'John'")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n sort"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token string"}},[t._v("'name ASC'")]),t._v("\n"),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),s("p",[t._v("Sort by binary notation")]),t._v(" "),s("div",{staticClass:"language-js extra-class"},[s("pre",{pre:!0,attrs:{class:"language-js"}},[s("code",[t._v("User"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("find")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n where"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n name"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token string"}},[t._v("'John'")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n sort"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n name"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token number"}},[t._v("1")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),s("p",[t._v("Sort by multiple attributes:")]),t._v(" "),s("div",{staticClass:"language-js extra-class"},[s("pre",{pre:!0,attrs:{class:"language-js"}},[s("code",[t._v("User"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("find")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n where"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n name"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token string"}},[t._v("'John'")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n sort"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n name"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token number"}},[t._v("1")]),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n age"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token number"}},[t._v("0")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),s("h4",{attrs:{id:"select-a-field"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#select-a-field","aria-hidden":"true"}},[t._v("#")]),t._v(" Select a field")]),t._v(" "),s("p",[t._v("Apply a projection to a Waterline query.")]),t._v(" "),s("p",[t._v("This example only returns the field name:")]),t._v(" "),s("div",{staticClass:"language-js extra-class"},[s("pre",{pre:!0,attrs:{class:"language-js"}},[s("code",[t._v("User"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("find")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n where"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n age"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{attrs:{class:"token string"}},[t._v("'<'")]),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token number"}},[t._v("30")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n select"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{attrs:{class:"token string"}},[t._v("'name'")]),s("span",{attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n"),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])])}],!1,null,null,null);e.options.__file="queries.md";a.default=e.exports}}]); \ No newline at end of file diff --git a/docs/.vuepress/dist/assets/js/18.e1f1758f.js b/docs/.vuepress/dist/assets/js/18.e1f1758f.js new file mode 100644 index 0000000000..469d3450bc --- /dev/null +++ b/docs/.vuepress/dist/assets/js/18.e1f1758f.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[18],{221:function(t,s,a){"use strict";a.r(s);var e=a(0),n=Object(e.a)({},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"content"},[t._m(0),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),t._m(3),t._v(" "),a("p",[t._v("Request header object.")]),t._v(" "),t._m(4),t._v(" "),t._m(5),t._v(" "),t._m(6),t._v(" "),a("p",[t._v("Request method.")]),t._v(" "),t._m(7),t._v(" "),t._m(8),t._v(" "),t._m(9),t._v(" "),t._m(10),t._v(" "),t._m(11),t._v(" "),a("p",[t._v("Get request URL.")]),t._v(" "),t._m(12),t._v(" "),a("p",[t._v("Set request URL, useful for url rewrites.")]),t._v(" "),t._m(13),t._v(" "),a("p",[t._v("Get request original URL.")]),t._v(" "),t._m(14),t._v(" "),t._m(15),t._v(" "),t._m(16),t._m(17),t._v(" "),t._m(18),t._v(" "),t._m(19),t._m(20),t._v(" "),a("p",[t._v("Get request pathname.")]),t._v(" "),t._m(21),t._v(" "),a("p",[t._v("Set request pathname and retain query-string when present.")]),t._v(" "),t._m(22),t._v(" "),t._m(23),t._v(" "),t._m(24),t._v(" "),a("p",[t._v("Set raw query string.")]),t._v(" "),t._m(25),t._v(" "),t._m(26),t._v(" "),t._m(27),t._v(" "),a("p",[t._v("Set raw query string.")]),t._v(" "),t._m(28),t._v(" "),t._m(29),t._v(" "),t._m(30),t._v(" "),t._m(31),t._v(" "),t._m(32),t._v(" "),t._m(33),t._v(" "),t._m(34),t._m(35),t._v(" "),t._m(36),t._v(" "),t._m(37),t._m(38),t._v(" "),t._m(39),t._v(" "),a("p",[t._v('For example "color=blue&size=small":')]),t._v(" "),t._m(40),t._m(41),t._v(" "),t._m(42),t._v(" "),t._m(43),t._m(44),t._v(" "),t._m(45),t._v(" "),t._m(46),t._m(47),t._v(" "),t._m(48),t._v(" "),t._m(49),t._v(" "),t._m(50),t._v(" "),t._m(51),t._v(" "),t._m(52),t._v(" "),t._m(53),t._v(" "),t._m(54),t._v(" "),t._m(55),t._v(" "),t._m(56),t._v(" "),t._m(57),t._v(" "),a("p",[t._v("Return subdomains as an array.")]),t._v(" "),t._m(58),t._v(" "),t._m(59),t._v(" "),t._m(60),t._v(" "),t._m(61),t._v(" "),t._m(62),a("p",[t._v("For example if you want to ensure that\nonly images are sent to a given route:")]),t._v(" "),t._m(63),t._m(64),t._v(" "),a("p",[t._v("Strapi's "),a("code",[t._v("request")]),t._v(" object includes helpful content negotiation utilities powered by\n"),a("a",{attrs:{href:"http://github.com/expressjs/accepts",target:"_blank",rel:"noopener noreferrer"}},[t._v("accepts"),a("OutboundLink")],1),t._v(" and\n"),a("a",{attrs:{href:"https://github.com/federomero/negotiator",target:"_blank",rel:"noopener noreferrer"}},[t._v("negotiator"),a("OutboundLink")],1),t._v(".")]),t._v(" "),a("p",[t._v("These utilities are:")]),t._v(" "),t._m(65),t._v(" "),a("p",[t._v("If no types are supplied, all acceptable types are returned.")]),t._v(" "),t._m(66),t._v(" "),a("p",[t._v("In the case of missing accept headers where any type is acceptable, the first type will\nbe returned. Thus, the order of types you supply is important.")]),t._v(" "),t._m(67),t._v(" "),t._m(68),t._v(" "),t._m(69),t._m(70),t._v(" "),t._m(71),t._m(72),t._v(" "),t._m(73),t._v(" "),t._m(74),a("p",[t._v("When no arguments are given all accepted encodings\nare returned as an array:")]),t._v(" "),t._m(75),t._m(76),t._v(" "),t._m(77),t._v(" "),t._m(78),t._v(" "),t._m(79),a("p",[t._v("When no arguments are given all accepted charsets\nare returned as an array:")]),t._v(" "),t._m(80),t._m(81),t._v(" "),t._m(82),t._v(" "),t._m(83),a("p",[t._v("When no arguments are given all accepted languages\nare returned as an array:")]),t._v(" "),t._m(84),t._m(85),t._v(" "),a("p",[t._v("Check if the request is idempotent.")]),t._v(" "),t._m(86),t._v(" "),a("p",[t._v("Return the request socket.")]),t._v(" "),t._m(87),t._v(" "),a("p",[t._v("Return request header.")])])},[function(){var t=this.$createElement,s=this._self._c||t;return s("h1",{attrs:{id:"request"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#request","aria-hidden":"true"}},[this._v("#")]),this._v(" Request")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("A Strapi "),s("code",[this._v("Request")]),this._v(" object is an abstraction on top of Node's vanilla request object,\nproviding additional functionality that is useful for every day HTTP server\ndevelopment.")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"api"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#api","aria-hidden":"true"}},[this._v("#")]),this._v(" API")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"request-header"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#request-header","aria-hidden":"true"}},[this._v("#")]),this._v(" request.header")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"request-headers"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#request-headers","aria-hidden":"true"}},[this._v("#")]),this._v(" request.headers")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Request header object. Alias as "),s("code",[this._v("request.header")]),this._v(".")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"request-method"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#request-method","aria-hidden":"true"}},[this._v("#")]),this._v(" request.method")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"request-method-2"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#request-method-2","aria-hidden":"true"}},[this._v("#")]),this._v(" request.method=")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Set request method, useful for implementing middleware\nsuch as "),s("code",[this._v("methodOverride()")]),this._v(".")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"request-length"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#request-length","aria-hidden":"true"}},[this._v("#")]),this._v(" request.length")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Return request Content-Length as a number when present, or "),s("code",[this._v("undefined")]),this._v(".")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"request-url"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#request-url","aria-hidden":"true"}},[this._v("#")]),this._v(" request.url")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"request-url-2"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#request-url-2","aria-hidden":"true"}},[this._v("#")]),this._v(" request.url=")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"request-originalurl"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#request-originalurl","aria-hidden":"true"}},[this._v("#")]),this._v(" request.originalUrl")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"request-origin"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#request-origin","aria-hidden":"true"}},[this._v("#")]),this._v(" request.origin")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Get origin of URL, include "),s("code",[this._v("protocol")]),this._v(" and "),s("code",[this._v("host")]),this._v(".")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-js extra-class"},[s("pre",{pre:!0,attrs:{class:"language-js"}},[s("code",[s("span",{attrs:{class:"token keyword"}},[this._v("this")]),s("span",{attrs:{class:"token punctuation"}},[this._v(".")]),this._v("request"),s("span",{attrs:{class:"token punctuation"}},[this._v(".")]),this._v("origin\n"),s("span",{attrs:{class:"token comment"}},[this._v("// => http://example.com")]),this._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"request-href"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#request-href","aria-hidden":"true"}},[this._v("#")]),this._v(" request.href")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Get full request URL, include "),s("code",[this._v("protocol")]),this._v(", "),s("code",[this._v("host")]),this._v(" and "),s("code",[this._v("url")]),this._v(".")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-js extra-class"},[s("pre",{pre:!0,attrs:{class:"language-js"}},[s("code",[s("span",{attrs:{class:"token keyword"}},[this._v("this")]),s("span",{attrs:{class:"token punctuation"}},[this._v(".")]),this._v("request"),s("span",{attrs:{class:"token punctuation"}},[this._v(".")]),this._v("href\n"),s("span",{attrs:{class:"token comment"}},[this._v("// => http://example.com/foo/bar?q=1")]),this._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"request-path"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#request-path","aria-hidden":"true"}},[this._v("#")]),this._v(" request.path")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"request-path-2"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#request-path-2","aria-hidden":"true"}},[this._v("#")]),this._v(" request.path=")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"request-querystring"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#request-querystring","aria-hidden":"true"}},[this._v("#")]),this._v(" request.querystring")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Get raw query string void of "),s("code",[this._v("?")]),this._v(".")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"request-querystring-2"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#request-querystring-2","aria-hidden":"true"}},[this._v("#")]),this._v(" request.querystring=")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"request-search"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#request-search","aria-hidden":"true"}},[this._v("#")]),this._v(" request.search")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Get raw query string with the "),s("code",[this._v("?")]),this._v(".")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"request-search-2"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#request-search-2","aria-hidden":"true"}},[this._v("#")]),this._v(" request.search=")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"request-host"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#request-host","aria-hidden":"true"}},[this._v("#")]),this._v(" request.host")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("Get host (hostname:port) when present. Supports "),a("code",[t._v("X-Forwarded-Host")]),t._v("\nwhen "),a("code",[t._v("strapi.app.proxy")]),t._v(" is "),a("code",[t._v("true")]),t._v(", otherwise "),a("code",[t._v("Host")]),t._v(" is used.")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"request-hostname"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#request-hostname","aria-hidden":"true"}},[this._v("#")]),this._v(" request.hostname")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("Get hostname when present. Supports "),a("code",[t._v("X-Forwarded-Host")]),t._v("\nwhen "),a("code",[t._v("strapi.app.proxy")]),t._v(" is "),a("code",[t._v("true")]),t._v(", otherwise "),a("code",[t._v("Host")]),t._v(" is used.")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"request-type"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#request-type","aria-hidden":"true"}},[this._v("#")]),this._v(" request.type")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Get request "),s("code",[this._v("Content-Type")]),this._v(' void of parameters such as "charset".')])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("const")]),t._v(" ct "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("type"),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token comment"}},[t._v('// => "image/png"')]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"request-charset"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#request-charset","aria-hidden":"true"}},[this._v("#")]),this._v(" request.charset")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Get request charset when present, or "),s("code",[this._v("undefined")]),this._v(":")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-js extra-class"},[s("pre",{pre:!0,attrs:{class:"language-js"}},[s("code",[s("span",{attrs:{class:"token keyword"}},[this._v("this")]),s("span",{attrs:{class:"token punctuation"}},[this._v(".")]),this._v("request"),s("span",{attrs:{class:"token punctuation"}},[this._v(".")]),this._v("charset\n"),s("span",{attrs:{class:"token comment"}},[this._v('// => "utf-8"')]),this._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"request-query"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#request-query","aria-hidden":"true"}},[this._v("#")]),this._v(" request.query")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Get parsed query-string, returning an empty object when no\nquery-string is present. Note that this getter does "),s("em",[this._v("not")]),this._v("\nsupport nested parsing.")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n color"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'blue'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n size"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'small'")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"request-query-2"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#request-query-2","aria-hidden":"true"}},[this._v("#")]),this._v(" request.query=")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Set query-string to the given object. Note that this\nsetter does "),s("em",[this._v("not")]),this._v(" support nested objects.")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("query "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" next"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'/login'")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"request-fresh"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#request-fresh","aria-hidden":"true"}},[this._v("#")]),this._v(" request.fresh")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v('Check if a request cache is "fresh", aka the contents have not changed. This\nmethod is for cache negotiation between '),a("code",[t._v("If-None-Match")]),t._v(" / "),a("code",[t._v("ETag")]),t._v(", and\n"),a("code",[t._v("If-Modified-Since")]),t._v(" and "),a("code",[t._v("Last-Modified")]),t._v(". It should be referenced after setting\none or more of these response headers.")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token comment"}},[t._v("// freshness check requires status 20x or 304")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("status "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("200")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token keyword"}},[t._v("set")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'ETag'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'123'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{attrs:{class:"token comment"}},[t._v("// cache is ok")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("fresh"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("status "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("304")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("return")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{attrs:{class:"token comment"}},[t._v("// cache is stale")]),t._v("\n"),a("span",{attrs:{class:"token comment"}},[t._v("// fetch new data")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("body "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("yield")]),t._v(" db"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("find")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'something'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"request-stale"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#request-stale","aria-hidden":"true"}},[this._v("#")]),this._v(" request.stale")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Inverse of "),s("code",[this._v("request.fresh")]),this._v(".")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"request-protocol"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#request-protocol","aria-hidden":"true"}},[this._v("#")]),this._v(" request.protocol")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v('Return request protocol, "https" or "http". Supports '),s("code",[this._v("X-Forwarded-Proto")]),this._v("\nwhen "),s("code",[this._v("strapi.app.proxy")]),this._v(" is "),s("code",[this._v("true")]),this._v(".")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"request-secure"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#request-secure","aria-hidden":"true"}},[this._v("#")]),this._v(" request.secure")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Shorthand for "),s("code",[this._v('this.protocol == "https"')]),this._v(" to check if a request was\nissued via TLS.")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"request-ip"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#request-ip","aria-hidden":"true"}},[this._v("#")]),this._v(" request.ip")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Request remote address. Supports "),s("code",[this._v("X-Forwarded-For")]),this._v(" when "),s("code",[this._v("strapi.app.proxy")]),this._v("\nis "),s("code",[this._v("true")]),this._v(".")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"request-ips"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#request-ips","aria-hidden":"true"}},[this._v("#")]),this._v(" request.ips")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("When "),s("code",[this._v("X-Forwarded-For")]),this._v(" is present and "),s("code",[this._v("strapi.app.proxy")]),this._v(" is enabled an array\nof these ips is returned, ordered from upstream -> downstream. When disabled\nan empty array is returned.")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"request-subdomains"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#request-subdomains","aria-hidden":"true"}},[this._v("#")]),this._v(" request.subdomains")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Subdomains are the dot-separated parts of the host before the main domain of\nthe app. By default, the domain of the app is assumed to be the last two\nparts of the host. This can be changed by setting "),s("code",[this._v("strapi.app.subdomainOffset")]),this._v(".")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v('For example, if the domain is "tobi.ferrets.example.com":\nIf '),a("code",[t._v("strapi.app.subdomainOffset")]),t._v(" is not set, this.subdomains is "),a("code",[t._v('["ferrets", "tobi"]')]),t._v(".\nIf "),a("code",[t._v("strapi.app.subdomainOffset")]),t._v(" is 3, this.subdomains is "),a("code",[t._v('["tobi"]')]),t._v(".")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"request-is-types"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#request-is-types","aria-hidden":"true"}},[this._v("#")]),this._v(" request.is(types...)")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v('Check if the incoming request contains the "Content-Type"\nheader field, and it contains any of the give mime '),s("code",[this._v("type")]),this._v("s.\nIf there is no request body, "),s("code",[this._v("undefined")]),this._v(" is returned.\nIf there is no content type, or the match fails "),s("code",[this._v("false")]),this._v(" is returned.\nOtherwise, it returns the matching content-type.")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token comment"}},[t._v("// With Content-Type: text/html; charset=utf-8")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("is")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'html'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),a("span",{attrs:{class:"token comment"}},[t._v("// => 'html'")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("is")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'text/html'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),a("span",{attrs:{class:"token comment"}},[t._v("// => 'text/html'")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("is")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'text/*'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'text/html'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),a("span",{attrs:{class:"token comment"}},[t._v("// => 'text/html'")]),t._v("\n\n"),a("span",{attrs:{class:"token operator"}},[t._v("/")]),a("span",{attrs:{class:"token operator"}},[t._v("/")]),t._v(" When Content"),a("span",{attrs:{class:"token operator"}},[t._v("-")]),t._v("Type is application"),a("span",{attrs:{class:"token operator"}},[t._v("/")]),t._v("json\n"),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("is")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'json'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'urlencoded'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("/")]),a("span",{attrs:{class:"token operator"}},[t._v("/")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'json'")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("is")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'application/json'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("/")]),a("span",{attrs:{class:"token operator"}},[t._v("/")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'application/json'")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("is")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'html'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'application/*'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),a("span",{attrs:{class:"token comment"}},[t._v("// => 'application/json'")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("is")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'html'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("/")]),a("span",{attrs:{class:"token operator"}},[t._v("/")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),a("span",{attrs:{class:"token boolean"}},[t._v("false")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("is")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'image/*'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token comment"}},[t._v("// process")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token keyword"}},[t._v("throw")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token number"}},[t._v("415")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'images only!'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"content-negotiation"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#content-negotiation","aria-hidden":"true"}},[this._v("#")]),this._v(" Content Negotiation")])},function(){var t=this.$createElement,s=this._self._c||t;return s("ul",[s("li",[s("code",[this._v("request.accepts(types)")])]),this._v(" "),s("li",[s("code",[this._v("request.acceptsEncodings(types)")])]),this._v(" "),s("li",[s("code",[this._v("request.acceptsCharsets(charsets)")])]),this._v(" "),s("li",[s("code",[this._v("request.acceptsLanguages(langs)")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("If multiple types are supplied, the best match will be returned. If no matches are found,\na "),s("code",[this._v("false")]),this._v(" is returned, and you should send a "),s("code",[this._v('406 "Not Acceptable"')]),this._v(" response to the client.")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"request-accepts-types"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#request-accepts-types","aria-hidden":"true"}},[this._v("#")]),this._v(" request.accepts(types)")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("Check if the given "),a("code",[t._v("type(s)")]),t._v(" is acceptable, returning the best match when true, otherwise\n"),a("code",[t._v("false")]),t._v(". The "),a("code",[t._v("type")]),t._v(' value may be one or more mime type string\nsuch as "application/json", the extension name\nsuch as "json", or an array '),a("code",[t._v('["json", "html", "text/plain"]')]),t._v(".")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token comment"}},[t._v("// Accept: text/html")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("accepts")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'html'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token comment"}},[t._v('// => "html"')]),t._v("\n\n"),a("span",{attrs:{class:"token comment"}},[t._v("// Accept: text/*, application/json")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("accepts")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'html'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token comment"}},[t._v('// => "html"')]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("accepts")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'text/html'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token comment"}},[t._v('// => "text/html"')]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("accepts")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'json'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'text'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token comment"}},[t._v('// => "json"')]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("accepts")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'application/json'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token comment"}},[t._v('// => "application/json"')]),t._v("\n\n"),a("span",{attrs:{class:"token comment"}},[t._v("// Accept: text/*, application/json")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("accepts")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'image/png'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("accepts")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'png'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token comment"}},[t._v("// => false")]),t._v("\n\n"),a("span",{attrs:{class:"token comment"}},[t._v("// Accept: text/*;q=.5, application/json")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("accepts")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{attrs:{class:"token string"}},[t._v("'html'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'json'")]),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("accepts")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'html'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'json'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token comment"}},[t._v('// => "json"')]),t._v("\n\n"),a("span",{attrs:{class:"token comment"}},[t._v("// No Accept header")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("accepts")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'html'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'json'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token comment"}},[t._v('// => "html"')]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("accepts")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'json'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'html'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token comment"}},[t._v('// => "json"')]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("You may call "),s("code",[this._v("this.accepts()")]),this._v(" as many times as you like,\nor use a switch:")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("switch")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("accepts")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'json'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'html'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'text'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'json'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("break")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'html'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("break")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'text'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("break")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("default")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token keyword"}},[t._v("throw")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token number"}},[t._v("406")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'json, html, or text only'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"request-acceptsencodings-encodings"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#request-acceptsencodings-encodings","aria-hidden":"true"}},[this._v("#")]),this._v(" request.acceptsEncodings(encodings)")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Check if "),s("code",[this._v("encodings")]),this._v(" are acceptable, returning the best match when true, otherwise "),s("code",[this._v("false")]),this._v(".\nNote that you should include "),s("code",[this._v("identity")]),this._v(" as one of the encodings!")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token comment"}},[t._v("// Accept-Encoding: gzip")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("acceptsEncodings")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'gzip'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'deflate'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'identity'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token comment"}},[t._v('// => "gzip"')]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("acceptsEncodings")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{attrs:{class:"token string"}},[t._v("'gzip'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'deflate'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'identity'")]),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token comment"}},[t._v('// => "gzip"')]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token comment"}},[t._v("// Accept-Encoding: gzip, deflate")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("acceptsEncodings")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token comment"}},[t._v('// => ["gzip", "deflate", "identity"]')]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Note that the "),s("code",[this._v("identity")]),this._v(" encoding (which means no encoding) could be unacceptable if\nthe client explicitly sends "),s("code",[this._v("identity;q=0")]),this._v(". Although this is an edge case, you should\nstill handle the case where this method returns "),s("code",[this._v("false")]),this._v(".")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"request-acceptscharsets-charsets"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#request-acceptscharsets-charsets","aria-hidden":"true"}},[this._v("#")]),this._v(" request.acceptsCharsets(charsets)")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Check if "),s("code",[this._v("charsets")]),this._v(" are acceptable, returning\nthe best match when true, otherwise "),s("code",[this._v("false")]),this._v(".")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token comment"}},[t._v("// Accept-Charset: utf-8, iso-8859-1;q=0.2, utf-7;q=0.5")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("acceptsCharsets")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'utf-8'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'utf-7'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token comment"}},[t._v('// => "utf-8"')]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("acceptsCharsets")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{attrs:{class:"token string"}},[t._v("'utf-7'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'utf-8'")]),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token comment"}},[t._v('// => "utf-8"')]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token comment"}},[t._v("// Accept-Charset: utf-8, iso-8859-1;q=0.2, utf-7;q=0.5")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("acceptsCharsets")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token comment"}},[t._v('// => ["utf-8", "utf-7", "iso-8859-1"]')]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"request-acceptslanguages-langs"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#request-acceptslanguages-langs","aria-hidden":"true"}},[this._v("#")]),this._v(" request.acceptsLanguages(langs)")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Check if "),s("code",[this._v("langs")]),this._v(" are acceptable, returning\nthe best match when true, otherwise "),s("code",[this._v("false")]),this._v(".")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token comment"}},[t._v("// Accept-Language: en;q=0.8, es, pt")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("acceptsLanguages")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'es'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'en'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token comment"}},[t._v('// => "es"')]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("acceptsLanguages")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{attrs:{class:"token string"}},[t._v("'en'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'es'")]),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token comment"}},[t._v('// => "es"')]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token comment"}},[t._v("// Accept-Language: en;q=0.8, es, pt")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("acceptsLanguages")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token comment"}},[t._v('// => ["es", "pt", "en"]')]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"request-idempotent"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#request-idempotent","aria-hidden":"true"}},[this._v("#")]),this._v(" request.idempotent")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"request-socket"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#request-socket","aria-hidden":"true"}},[this._v("#")]),this._v(" request.socket")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"request-get-field"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#request-get-field","aria-hidden":"true"}},[this._v("#")]),this._v(" request.get(field)")])}],!1,null,null,null);n.options.__file="request.md";s.default=n.exports}}]); \ No newline at end of file diff --git a/docs/.vuepress/dist/assets/js/19.8d1b00cd.js b/docs/.vuepress/dist/assets/js/19.8d1b00cd.js new file mode 100644 index 0000000000..03d98c928c --- /dev/null +++ b/docs/.vuepress/dist/assets/js/19.8d1b00cd.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[19],{220:function(t,s,a){"use strict";a.r(s);var e=a(0),n=Object(e.a)({},function(){this.$createElement;this._self._c;return this._m(0)},[function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"content"},[a("h1",{attrs:{id:"response"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#response","aria-hidden":"true"}},[t._v("#")]),t._v(" Response")]),t._v(" "),a("p",[t._v("A Strapi "),a("code",[t._v("Response")]),t._v(" object is an abstraction on top of Node's vanilla response object,\nproviding additional functionality that is useful for every day HTTP server\ndevelopment.")]),t._v(" "),a("h2",{attrs:{id:"api"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#api","aria-hidden":"true"}},[t._v("#")]),t._v(" API")]),t._v(" "),a("h3",{attrs:{id:"response-header"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#response-header","aria-hidden":"true"}},[t._v("#")]),t._v(" response.header")]),t._v(" "),a("p",[t._v("Response header object.")]),t._v(" "),a("h3",{attrs:{id:"response-headers"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#response-headers","aria-hidden":"true"}},[t._v("#")]),t._v(" response.headers")]),t._v(" "),a("p",[t._v("Response header object. Alias as "),a("code",[t._v("response.header")]),t._v(".")]),t._v(" "),a("h3",{attrs:{id:"response-socket"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#response-socket","aria-hidden":"true"}},[t._v("#")]),t._v(" response.socket")]),t._v(" "),a("p",[t._v("Request socket.")]),t._v(" "),a("h3",{attrs:{id:"response-status"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#response-status","aria-hidden":"true"}},[t._v("#")]),t._v(" response.status")]),t._v(" "),a("p",[t._v("Get response status. By default, "),a("code",[t._v("response.status")]),t._v(" is not set unlike Node's\n"),a("code",[t._v("res.statusCode")]),t._v(" which defaults to "),a("code",[t._v("200")]),t._v(".")]),t._v(" "),a("h3",{attrs:{id:"response-status-2"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#response-status-2","aria-hidden":"true"}},[t._v("#")]),t._v(" response.status=")]),t._v(" "),a("p",[t._v("Set response status via numeric code:")]),t._v(" "),a("ul",[a("li",[t._v('100 "continue"')]),t._v(" "),a("li",[t._v('101 "switching protocols"')]),t._v(" "),a("li",[t._v('102 "processing"')]),t._v(" "),a("li",[t._v('200 "ok"')]),t._v(" "),a("li",[t._v('201 "created"')]),t._v(" "),a("li",[t._v('202 "accepted"')]),t._v(" "),a("li",[t._v('203 "non-authoritative information"')]),t._v(" "),a("li",[t._v('204 "no content"')]),t._v(" "),a("li",[t._v('205 "reset content"')]),t._v(" "),a("li",[t._v('206 "partial content"')]),t._v(" "),a("li",[t._v('207 "multi-status"')]),t._v(" "),a("li",[t._v('300 "multiple choices"')]),t._v(" "),a("li",[t._v('301 "moved permanently"')]),t._v(" "),a("li",[t._v('302 "moved temporarily"')]),t._v(" "),a("li",[t._v('303 "see other"')]),t._v(" "),a("li",[t._v('304 "not modified"')]),t._v(" "),a("li",[t._v('305 "use proxy"')]),t._v(" "),a("li",[t._v('307 "temporary redirect"')]),t._v(" "),a("li",[t._v('400 "bad request"')]),t._v(" "),a("li",[t._v('401 "unauthorized"')]),t._v(" "),a("li",[t._v('402 "payment required"')]),t._v(" "),a("li",[t._v('403 "forbidden"')]),t._v(" "),a("li",[t._v('404 "not found"')]),t._v(" "),a("li",[t._v('405 "method not allowed"')]),t._v(" "),a("li",[t._v('406 "not acceptable"')]),t._v(" "),a("li",[t._v('407 "proxy authentication required"')]),t._v(" "),a("li",[t._v('408 "request time-out"')]),t._v(" "),a("li",[t._v('409 "conflict"')]),t._v(" "),a("li",[t._v('410 "gone"')]),t._v(" "),a("li",[t._v('411 "length required"')]),t._v(" "),a("li",[t._v('412 "precondition failed"')]),t._v(" "),a("li",[t._v('413 "request entity too large"')]),t._v(" "),a("li",[t._v('414 "request-uri too large"')]),t._v(" "),a("li",[t._v('415 "unsupported media type"')]),t._v(" "),a("li",[t._v('416 "requested range not satisfiable"')]),t._v(" "),a("li",[t._v('417 "expectation failed"')]),t._v(" "),a("li",[t._v('418 "i\'m a teapot"')]),t._v(" "),a("li",[t._v('422 "unprocessable entity"')]),t._v(" "),a("li",[t._v('423 "locked"')]),t._v(" "),a("li",[t._v('424 "failed dependency"')]),t._v(" "),a("li",[t._v('425 "unordered collection"')]),t._v(" "),a("li",[t._v('426 "upgrade required"')]),t._v(" "),a("li",[t._v('428 "precondition required"')]),t._v(" "),a("li",[t._v('429 "too many requests"')]),t._v(" "),a("li",[t._v('431 "request header fields too large"')]),t._v(" "),a("li",[t._v('500 "internal server error"')]),t._v(" "),a("li",[t._v('501 "not implemented"')]),t._v(" "),a("li",[t._v('502 "bad gateway"')]),t._v(" "),a("li",[t._v('503 "service unavailable"')]),t._v(" "),a("li",[t._v('504 "gateway time-out"')]),t._v(" "),a("li",[t._v('505 "http version not supported"')]),t._v(" "),a("li",[t._v('506 "variant also negotiates"')]),t._v(" "),a("li",[t._v('507 "insufficient storage"')]),t._v(" "),a("li",[t._v('509 "bandwidth limit exceeded"')]),t._v(" "),a("li",[t._v('510 "not extended"')]),t._v(" "),a("li",[t._v('511 "network authentication required"')])]),t._v(" "),a("p",[t._v("Don't worry too much about memorizing these strings, if you have a typo an error will be thrown,\ndisplaying this list so you can make a correction.")]),t._v(" "),a("h3",{attrs:{id:"response-message"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#response-message","aria-hidden":"true"}},[t._v("#")]),t._v(" response.message")]),t._v(" "),a("p",[t._v("Get response status message. By default, "),a("code",[t._v("response.message")]),t._v(" is\nassociated with "),a("code",[t._v("response.status")]),t._v(".")]),t._v(" "),a("h3",{attrs:{id:"response-message-2"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#response-message-2","aria-hidden":"true"}},[t._v("#")]),t._v(" response.message=")]),t._v(" "),a("p",[t._v("Set response status message to the given value.")]),t._v(" "),a("h3",{attrs:{id:"response-length"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#response-length","aria-hidden":"true"}},[t._v("#")]),t._v(" response.length=")]),t._v(" "),a("p",[t._v("Set response Content-Length to the given value.")]),t._v(" "),a("h3",{attrs:{id:"response-length-2"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#response-length-2","aria-hidden":"true"}},[t._v("#")]),t._v(" response.length")]),t._v(" "),a("p",[t._v("Return response Content-Length as a number when present, or deduce\nfrom "),a("code",[t._v("this.body")]),t._v(" when possible, or "),a("code",[t._v("undefined")]),t._v(".")]),t._v(" "),a("h3",{attrs:{id:"response-body"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#response-body","aria-hidden":"true"}},[t._v("#")]),t._v(" response.body")]),t._v(" "),a("p",[t._v("Get response body.")]),t._v(" "),a("h3",{attrs:{id:"response-body-2"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#response-body-2","aria-hidden":"true"}},[t._v("#")]),t._v(" response.body=")]),t._v(" "),a("p",[t._v("Set response body to one of the following:")]),t._v(" "),a("ul",[a("li",[a("code",[t._v("string")]),t._v(" written")]),t._v(" "),a("li",[a("code",[t._v("Buffer")]),t._v(" written")]),t._v(" "),a("li",[a("code",[t._v("Stream")]),t._v(" piped")]),t._v(" "),a("li",[a("code",[t._v("Object")]),t._v(" json-stringified")]),t._v(" "),a("li",[a("code",[t._v("null")]),t._v(" no content response")])]),t._v(" "),a("p",[t._v("If "),a("code",[t._v("response.status")]),t._v(" has not been set, Strapi will automatically set the status to "),a("code",[t._v("200")]),t._v(" or "),a("code",[t._v("204")]),t._v(".")]),t._v(" "),a("h4",{attrs:{id:"string"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#string","aria-hidden":"true"}},[t._v("#")]),t._v(" String")]),t._v(" "),a("p",[t._v("The Content-Type is defaulted to text/html or text/plain, both with\na default charset of utf-8. The Content-Length field is also set.")]),t._v(" "),a("h4",{attrs:{id:"buffer"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#buffer","aria-hidden":"true"}},[t._v("#")]),t._v(" Buffer")]),t._v(" "),a("p",[t._v("The Content-Type is defaulted to application/octet-stream, and Content-Length\nis also set.")]),t._v(" "),a("h4",{attrs:{id:"stream"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#stream","aria-hidden":"true"}},[t._v("#")]),t._v(" Stream")]),t._v(" "),a("p",[t._v("The Content-Type is defaulted to application/octet-stream.")]),t._v(" "),a("h4",{attrs:{id:"object"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#object","aria-hidden":"true"}},[t._v("#")]),t._v(" Object")]),t._v(" "),a("p",[t._v("The Content-Type is defaulted to application/json.")]),t._v(" "),a("h3",{attrs:{id:"response-get-field"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#response-get-field","aria-hidden":"true"}},[t._v("#")]),t._v(" response.get(field)")]),t._v(" "),a("p",[t._v("Get a response header field value with case-insensitive "),a("code",[t._v("field")]),t._v(".")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("const")]),t._v(" etag "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token keyword"}},[t._v("get")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'ETag'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),a("h3",{attrs:{id:"response-set-field-value"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#response-set-field-value","aria-hidden":"true"}},[t._v("#")]),t._v(" response.set(field, value)")]),t._v(" "),a("p",[t._v("Set response header "),a("code",[t._v("field")]),t._v(" to "),a("code",[t._v("value")]),t._v(":")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token keyword"}},[t._v("set")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'Cache-Control'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'no-cache'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),a("h3",{attrs:{id:"response-append-field-value"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#response-append-field-value","aria-hidden":"true"}},[t._v("#")]),t._v(" response.append(field, value)")]),t._v(" "),a("p",[t._v("Append additional header "),a("code",[t._v("field")]),t._v(" with value "),a("code",[t._v("val")]),t._v(".")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("append")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'Link'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("''")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),a("h3",{attrs:{id:"response-set-fields"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#response-set-fields","aria-hidden":"true"}},[t._v("#")]),t._v(" response.set(fields)")]),t._v(" "),a("p",[t._v("Set several response header "),a("code",[t._v("fields")]),t._v(" with an object:")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token keyword"}},[t._v("set")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v("'Etag'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'1234'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v("'Last-Modified'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" date\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),a("h3",{attrs:{id:"response-remove-field"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#response-remove-field","aria-hidden":"true"}},[t._v("#")]),t._v(" response.remove(field)")]),t._v(" "),a("p",[t._v("Remove header "),a("code",[t._v("field")]),t._v(".")]),t._v(" "),a("h3",{attrs:{id:"response-type"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#response-type","aria-hidden":"true"}},[t._v("#")]),t._v(" response.type")]),t._v(" "),a("p",[t._v("Get response "),a("code",[t._v("Content-Type")]),t._v(' void of parameters such as "charset".')]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("const")]),t._v(" ct "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("type"),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token comment"}},[t._v('// => "image/png"')]),t._v("\n")])])]),a("h3",{attrs:{id:"response-type-2"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#response-type-2","aria-hidden":"true"}},[t._v("#")]),t._v(" response.type=")]),t._v(" "),a("p",[t._v("Set response "),a("code",[t._v("Content-Type")]),t._v(" via mime string or file extension.")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("type "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'text/plain; charset=utf-8'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("type "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'image/png'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("type "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'.png'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("type "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'png'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),a("p",[t._v("Note: when appropriate a "),a("code",[t._v("charset")]),t._v(" is selected for you, for\nexample "),a("code",[t._v("response.type = 'html'")]),t._v(' will default to "utf-8", however\nwhen explicitly defined in full as '),a("code",[t._v("response.type = 'text/html'")]),t._v("\nno charset is assigned.")]),t._v(" "),a("h3",{attrs:{id:"response-is-types"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#response-is-types","aria-hidden":"true"}},[t._v("#")]),t._v(" response.is(types...)")]),t._v(" "),a("p",[t._v("Very similar to "),a("code",[t._v("this.request.is()")]),t._v(".\nCheck whether the response type is one of the supplied types.\nThis is particularly useful for creating middleware that\nmanipulate responses.")]),t._v(" "),a("p",[t._v("For example, this is a middleware that minifies\nall HTML responses except for streams.")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("const")]),t._v(" minify "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("require")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'html-minifier'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\nstrapi"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("app"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("use")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token keyword"}},[t._v("function")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("*")]),a("span",{attrs:{class:"token function"}},[t._v("minifyHTML")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("next"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("yield")]),t._v(" next"),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("!")]),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("response"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("is")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'html'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("return")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),a("span",{attrs:{class:"token keyword"}},[t._v("const")]),t._v(" body "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("body"),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("!")]),t._v("body "),a("span",{attrs:{class:"token operator"}},[t._v("||")]),t._v(" body"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("pipe"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("return")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),a("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Buffer"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("isBuffer")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("body"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n body "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" body"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("toString")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("body "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("minify")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("body"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),a("h3",{attrs:{id:"response-redirect-url-alt"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#response-redirect-url-alt","aria-hidden":"true"}},[t._v("#")]),t._v(" response.redirect(url, [alt])")]),t._v(" "),a("p",[t._v("Perform a [302] redirect to "),a("code",[t._v("url")]),t._v(".")]),t._v(" "),a("p",[t._v('The string "back" is special-cased\nto provide Referrer support, when Referrer\nis not present '),a("code",[t._v("alt")]),t._v(' or "/" is used.')]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("redirect")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'back'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("redirect")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'back'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'/index.html'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("redirect")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'/login'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("redirect")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'http://google.com'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),a("p",[t._v("To alter the default status of "),a("code",[t._v("302")]),t._v(", simply assign the status\nbefore or after this call. To alter the body, assign it after this call:")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("status "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("301")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("redirect")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'/cart'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("body "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'Redirecting to shopping cart'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),a("h3",{attrs:{id:"response-attachment-filename"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#response-attachment-filename","aria-hidden":"true"}},[t._v("#")]),t._v(" response.attachment([filename])")]),t._v(" "),a("p",[t._v("Set "),a("code",[t._v("Content-Disposition")]),t._v(' to "attachment" to signal the client\nto prompt for download. Optionally specify the '),a("code",[t._v("filename")]),t._v(" of the\ndownload.")]),t._v(" "),a("h3",{attrs:{id:"response-headersent"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#response-headersent","aria-hidden":"true"}},[t._v("#")]),t._v(" response.headerSent")]),t._v(" "),a("p",[t._v("Check if a response header has already been sent. Useful for seeing\nif the client may be notified on error.")]),t._v(" "),a("h3",{attrs:{id:"response-lastmodified"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#response-lastmodified","aria-hidden":"true"}},[t._v("#")]),t._v(" response.lastModified")]),t._v(" "),a("p",[t._v("Return the "),a("code",[t._v("Last-Modified")]),t._v(" header as a "),a("code",[t._v("Date")]),t._v(", if it exists.")]),t._v(" "),a("h3",{attrs:{id:"response-lastmodified-2"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#response-lastmodified-2","aria-hidden":"true"}},[t._v("#")]),t._v(" response.lastModified=")]),t._v(" "),a("p",[t._v("Set the "),a("code",[t._v("Last-Modified")]),t._v(" header as an appropriate UTC string.\nYou can either set it as a "),a("code",[t._v("Date")]),t._v(" or date string.")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("response"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("lastModified "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("new")]),t._v(" "),a("span",{attrs:{class:"token class-name"}},[t._v("Date")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),a("h3",{attrs:{id:"response-etag"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#response-etag","aria-hidden":"true"}},[t._v("#")]),t._v(" response.etag=")]),t._v(" "),a("p",[t._v("Set the ETag of a response including the wrapped "),a("code",[t._v('"')]),t._v("s.\nNote that there is no corresponding "),a("code",[t._v("response.etag")]),t._v(" getter.")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("response"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("etag "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" crypto"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("createHash")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'md5'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("update")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("body"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("digest")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'hex'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),a("h3",{attrs:{id:"response-vary-field"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#response-vary-field","aria-hidden":"true"}},[t._v("#")]),t._v(" response.vary(field)")]),t._v(" "),a("p",[t._v("Vary on "),a("code",[t._v("field")]),t._v(".")])])}],!1,null,null,null);n.options.__file="response.md";s.default=n.exports}}]); \ No newline at end of file diff --git a/docs/.vuepress/dist/assets/js/2.08038ddb.js b/docs/.vuepress/dist/assets/js/2.08038ddb.js new file mode 100644 index 0000000000..39eba92a07 --- /dev/null +++ b/docs/.vuepress/dist/assets/js/2.08038ddb.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[2],{159:function(t,e,n){},161:function(t,e,n){"use strict";var a=n(159);n.n(a).a},209:function(t,e,n){"use strict";n.r(e);var a={functional:!0,props:{type:{type:String,default:"tip"},text:String,vertical:{type:String,default:"top"}},render:function(t,e){var n=e.props,a=e.slots;return t("span",{class:["badge",n.type,n.vertical]},n.text||a().default)}},i=(n(161),n(0)),o=Object(i.a)(a,void 0,void 0,!1,null,"099ab69c",null);o.options.__file="Badge.vue";e.default=o.exports}}]); \ No newline at end of file diff --git a/docs/.vuepress/dist/assets/js/20.856d7bdd.js b/docs/.vuepress/dist/assets/js/20.856d7bdd.js new file mode 100644 index 0000000000..c22013c044 --- /dev/null +++ b/docs/.vuepress/dist/assets/js/20.856d7bdd.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[20],{240:function(t,s,a){"use strict";a.r(s);var n=a(0),o=Object(n.a)({},function(){this.$createElement;this._self._c;return this._m(0)},[function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"content"},[a("h1",{attrs:{id:"router"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#router","aria-hidden":"true"}},[t._v("#")]),t._v(" Router")]),t._v(" "),a("p",[t._v("The most basic feature of any web application is the ability to interpret a request sent to a URL,\nthen send back a response. In order to do this, your application has to be able to distinguish one URL\nfrom another.")]),t._v(" "),a("p",[t._v("Like most web frameworks, Strapi provides a router: a mechanism for mapping URLs to controllers.\nRoutes are rules that tell Strapi what to do when faced with an incoming request.")]),t._v(" "),a("p",[t._v("Routes can be found in "),a("code",[t._v("./api//config/routes.json")]),t._v(".")]),t._v(" "),a("h2",{attrs:{id:"route-format"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#route-format","aria-hidden":"true"}},[t._v("#")]),t._v(" Route format")]),t._v(" "),a("p",[t._v("Each route consists of an address (as a key) and a target (as an object value).\nThe address is a URL path and a specific HTTP method. The target is defined by an object with a\n"),a("code",[t._v("controller")]),t._v(" and an "),a("code",[t._v("action")]),t._v(". When the router receives an incoming request, it checks the address\nof all routes for matches. If a matching route is found, the request is then passed to its target.")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"routes"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"VERB /endpoint/:param"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"controller"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"controllerName"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"action"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"actionName"')]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("For example to manage your "),a("code",[t._v("Post")]),t._v(" records with a CRUD, your route should look like this:")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"routes"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"GET /post"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"controller"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"Post"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"action"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"find"')]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"GET /post/:id"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"controller"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"Post"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"action"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"findOne"')]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"POST /post"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"controller"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"Post"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"action"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"create"')]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"PUT /post/:id"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"controller"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"Post"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"action"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"update"')]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"DELETE /post/:id"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"controller"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"Post"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"action"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"delete"')]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("h2",{attrs:{id:"route-parameters"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#route-parameters","aria-hidden":"true"}},[t._v("#")]),t._v(" Route parameters")]),t._v(" "),a("p",[t._v("Route paths will be translated to regular expressions used to match requests.\nQuery strings will not be considered when matching requests.")]),t._v(" "),a("p",[t._v("Route parameters are captured and added to "),a("code",[t._v("ctx.params")]),t._v(" or "),a("code",[t._v("ctx.request.body")]),t._v(".")]),t._v(" "),a("p",[t._v("By taking the previous example, your "),a("code",[t._v("Post")]),t._v(" controller should look like this:")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[t._v("module"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("exports "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n "),a("span",{attrs:{class:"token comment"}},[t._v("// GET request")]),t._v("\n find"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("function")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("*")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("try")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("body "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("yield")]),t._v(" Post"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("find")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("params"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("catch")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token class-name"}},[t._v("error")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("body "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" error"),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\n findOne"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("function")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("*")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("try")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("body "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("yield")]),t._v(" Post"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("findOne")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("params"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("catch")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token class-name"}},[t._v("error")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("body "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" error"),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\n "),a("span",{attrs:{class:"token comment"}},[t._v("// POST request")]),t._v("\n create"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("function")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("*")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("try")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("body "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("yield")]),t._v(" Post"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("create")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("body"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("catch")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token class-name"}},[t._v("error")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("body "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" error"),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\n "),a("span",{attrs:{class:"token comment"}},[t._v("// PUT request")]),t._v("\n update"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("function")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("*")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("try")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("body "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("yield")]),t._v(" Post"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("update")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("params"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("id"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("request"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("body"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("catch")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token class-name"}},[t._v("error")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("body "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" error"),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\n "),a("span",{attrs:{class:"token comment"}},[t._v("// DELETE request")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("delete")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("function")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("*")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("try")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("body "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("yield")]),t._v(" Post"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("destroy")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("params"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("catch")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token class-name"}},[t._v("error")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("body "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" error"),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" \n\n")])])]),a("h2",{attrs:{id:"router-prefix"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#router-prefix","aria-hidden":"true"}},[t._v("#")]),t._v(" Router prefix")]),t._v(" "),a("p",[t._v("Keep in mind routes can automatically be prefixed in "),a("code",[t._v("./config/general.json")]),t._v(" with the "),a("code",[t._v("prefix")]),t._v(" key.\nLet an empty string if you don't want to prefix your API. The prefix must starts with a "),a("code",[t._v("/")]),t._v(", e.g. "),a("code",[t._v("/api")]),t._v(".")]),t._v(" "),a("h2",{attrs:{id:"policies-and-route-process"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#policies-and-route-process","aria-hidden":"true"}},[t._v("#")]),t._v(" Policies and route process")]),t._v(" "),a("p",[t._v("Just because a request matches a route address doesn't necessarily mean it will be passed to that\nroute's target directly. The request will need to pass through any configured policies first.\nPolicies are versatile tools for authorization and access control. They let you allow or deny\naccess to your controllers down to a fine level of granularity.")]),t._v(" "),a("p",[t._v("Policies are defined in the "),a("code",[t._v("policies")]),t._v(" directory of every of your APIs.")]),t._v(" "),a("p",[t._v("Each policy file should contain a single function. When it comes down to it, policies are\nreally just functions which run before your controllers. You can chain as many of them\ntogether as you like. In fact they're designed to be used this way. Ideally, each middleware\nfunction should really check just one thing.")]),t._v(" "),a("p",[t._v("For example to access "),a("code",[t._v("DELETE /post/:id")]),t._v(", the request will go through the "),a("code",[t._v("isAdmin")]),t._v(" policy first.\nIf the policy allows the request, then the "),a("code",[t._v("delete")]),t._v(" action from the "),a("code",[t._v("Post")]),t._v(" controller is executed.")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"routes"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"DELETE /post/:id"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"controller"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"Post"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"action"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"delete"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"policies"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{attrs:{class:"token string"}},[t._v('"isAdmin"')]),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("Do not forget to yield "),a("code",[t._v("next")]),t._v(" when you need to move on.")])])}],!1,null,null,null);o.options.__file="router.md";s.default=o.exports}}]); \ No newline at end of file diff --git a/docs/.vuepress/dist/assets/js/21.6f851286.js b/docs/.vuepress/dist/assets/js/21.6f851286.js new file mode 100644 index 0000000000..4b10715fd1 --- /dev/null +++ b/docs/.vuepress/dist/assets/js/21.6f851286.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[21],{218:function(t,s,n){"use strict";n.r(s);var a=n(0),o=Object(a.a)({},function(){this.$createElement;this._self._c;return this._m(0)},[function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"content"},[n("h1",{attrs:{id:"services"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#services","aria-hidden":"true"}},[t._v("#")]),t._v(" Services")]),t._v(" "),n("p",[t._v("Services can be thought of as libraries which contain functions that you might want to use\nin many places of your application. For example, you might have an "),n("code",[t._v("Email")]),t._v(" service which\nwraps some default email message boilerplate code that you would want to use in many parts\nof your application.")]),t._v(" "),n("p",[t._v("Simply create a JavaScript file containing a function or an object into your\n"),n("code",[t._v("./api//services")]),t._v(" directory.")]),t._v(" "),n("p",[t._v("For example, you could have an "),n("code",[t._v("Email service")]),t._v(" like this:")]),t._v(" "),n("div",{staticClass:"language-js extra-class"},[n("pre",{pre:!0,attrs:{class:"language-js"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("const")]),t._v(" nodemailer "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("require")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v("'nodemailer'")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\nmodule"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("exports "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n sendEmail"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("function")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token keyword"}},[t._v("from")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" to"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" subject"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" text"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n "),n("span",{attrs:{class:"token comment"}},[t._v("// Create reusable transporter object using SMTP transport")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("const")]),t._v(" transporter "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" nodemailer"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("createTransport")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n service"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token string"}},[t._v("'Gmail'")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n auth"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n user"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token string"}},[t._v("'gmail.user@gmail.com'")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n pass"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token string"}},[t._v("'userpass'")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token comment"}},[t._v("// Setup e-mail data")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("const")]),t._v(" options "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("from")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("from")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n to"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" to"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n subject"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" subject"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n text"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" text\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token comment"}},[t._v("// Send mail")]),t._v("\n transporter"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("sendMail")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("options"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("function")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("error"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" info"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("error"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n console"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("log")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("error"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{attrs:{class:"token boolean"}},[t._v("false")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n console"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("log")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v("'Message sent: '")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" info"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("response"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])])}],!1,null,null,null);o.options.__file="services.md";s.default=o.exports}}]); \ No newline at end of file diff --git a/docs/.vuepress/dist/assets/js/22.7ddb4e1d.js b/docs/.vuepress/dist/assets/js/22.7ddb4e1d.js new file mode 100644 index 0000000000..b5f476ef82 --- /dev/null +++ b/docs/.vuepress/dist/assets/js/22.7ddb4e1d.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[22],{217:function(s,t,a){"use strict";a.r(t);var n=a(0),o=Object(n.a)({},function(){this.$createElement;this._self._c;return this._m(0)},[function(){var s=this,t=s.$createElement,a=s._self._c||t;return a("div",{staticClass:"content"},[a("h1",{attrs:{id:"sessions"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#sessions","aria-hidden":"true"}},[s._v("#")]),s._v(" Sessions")]),s._v(" "),a("p",[s._v("Since HTTP driven applications are stateless, sessions provide a way to store information\nabout the user across requests.")]),s._v(" "),a("p",[s._v('Strapi provides "guest" sessions, meaning any visitor will have a session,\nauthenticated or not. If a session is new a '),a("code",[s._v("Set-Cookie")]),s._v(" will be produced regardless\nof populating the session.")]),s._v(" "),a("p",[s._v("Strapi only supports cookie sessions, for now.")]),s._v(" "),a("p",[s._v("The current session is available in "),a("code",[s._v("this.session")]),s._v(" inside a controller action.")]),s._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[s._v("module"),a("span",{attrs:{class:"token punctuation"}},[s._v(".")]),s._v("exports "),a("span",{attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),a("span",{attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n find"),a("span",{attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),a("span",{attrs:{class:"token keyword"}},[s._v("function")]),s._v(" "),a("span",{attrs:{class:"token operator"}},[s._v("*")]),a("span",{attrs:{class:"token punctuation"}},[s._v("(")]),a("span",{attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),a("span",{attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n\n "),a("span",{attrs:{class:"token comment"}},[s._v("// Limit request rate to 100")]),s._v("\n "),a("span",{attrs:{class:"token keyword"}},[s._v("if")]),s._v(" "),a("span",{attrs:{class:"token punctuation"}},[s._v("(")]),a("span",{attrs:{class:"token keyword"}},[s._v("this")]),a("span",{attrs:{class:"token punctuation"}},[s._v(".")]),s._v("session"),a("span",{attrs:{class:"token punctuation"}},[s._v(".")]),s._v("views "),a("span",{attrs:{class:"token operator"}},[s._v("<")]),s._v(" "),a("span",{attrs:{class:"token number"}},[s._v("100")]),a("span",{attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),a("span",{attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),a("span",{attrs:{class:"token keyword"}},[s._v("try")]),s._v(" "),a("span",{attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),a("span",{attrs:{class:"token keyword"}},[s._v("this")]),a("span",{attrs:{class:"token punctuation"}},[s._v(".")]),s._v("session"),a("span",{attrs:{class:"token punctuation"}},[s._v(".")]),s._v("views"),a("span",{attrs:{class:"token operator"}},[s._v("++")]),a("span",{attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),a("span",{attrs:{class:"token keyword"}},[s._v("this")]),a("span",{attrs:{class:"token punctuation"}},[s._v(".")]),s._v("body "),a("span",{attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),a("span",{attrs:{class:"token keyword"}},[s._v("yield")]),s._v(" Post"),a("span",{attrs:{class:"token punctuation"}},[s._v(".")]),a("span",{attrs:{class:"token function"}},[s._v("find")]),a("span",{attrs:{class:"token punctuation"}},[s._v("(")]),a("span",{attrs:{class:"token keyword"}},[s._v("this")]),a("span",{attrs:{class:"token punctuation"}},[s._v(".")]),s._v("params"),a("span",{attrs:{class:"token punctuation"}},[s._v(")")]),a("span",{attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),a("span",{attrs:{class:"token punctuation"}},[s._v("}")]),s._v(" "),a("span",{attrs:{class:"token keyword"}},[s._v("catch")]),s._v(" "),a("span",{attrs:{class:"token punctuation"}},[s._v("(")]),a("span",{attrs:{class:"token class-name"}},[s._v("error")]),a("span",{attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),a("span",{attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),a("span",{attrs:{class:"token keyword"}},[s._v("this")]),a("span",{attrs:{class:"token punctuation"}},[s._v(".")]),s._v("body "),a("span",{attrs:{class:"token operator"}},[s._v("=")]),s._v(" error"),a("span",{attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),a("span",{attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n "),a("span",{attrs:{class:"token punctuation"}},[s._v("}")]),s._v(" "),a("span",{attrs:{class:"token keyword"}},[s._v("else")]),s._v(" "),a("span",{attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),a("span",{attrs:{class:"token keyword"}},[s._v("this")]),a("span",{attrs:{class:"token punctuation"}},[s._v(".")]),s._v("body "),a("span",{attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),a("span",{attrs:{class:"token string"}},[s._v("'You have reached your request rate limit'")]),a("span",{attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),a("span",{attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n "),a("span",{attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n"),a("span",{attrs:{class:"token punctuation"}},[s._v("}")]),a("span",{attrs:{class:"token punctuation"}},[s._v(";")]),s._v(" \n")])])]),a("p",[s._v("To destroy an active session, simply set it to "),a("code",[s._v("null")]),s._v(":")]),s._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[s._v("module"),a("span",{attrs:{class:"token punctuation"}},[s._v(".")]),s._v("exports "),a("span",{attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),a("span",{attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n logout"),a("span",{attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),a("span",{attrs:{class:"token keyword"}},[s._v("function")]),s._v(" "),a("span",{attrs:{class:"token punctuation"}},[s._v("(")]),a("span",{attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),a("span",{attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),a("span",{attrs:{class:"token keyword"}},[s._v("try")]),s._v(" "),a("span",{attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),a("span",{attrs:{class:"token keyword"}},[s._v("this")]),a("span",{attrs:{class:"token punctuation"}},[s._v(".")]),s._v("session "),a("span",{attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),a("span",{attrs:{class:"token keyword"}},[s._v("null")]),a("span",{attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),a("span",{attrs:{class:"token keyword"}},[s._v("this")]),a("span",{attrs:{class:"token punctuation"}},[s._v(".")]),a("span",{attrs:{class:"token function"}},[s._v("redirect")]),a("span",{attrs:{class:"token punctuation"}},[s._v("(")]),a("span",{attrs:{class:"token string"}},[s._v("'./'")]),a("span",{attrs:{class:"token punctuation"}},[s._v(")")]),a("span",{attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),a("span",{attrs:{class:"token punctuation"}},[s._v("}")]),s._v(" "),a("span",{attrs:{class:"token keyword"}},[s._v("catch")]),s._v(" "),a("span",{attrs:{class:"token punctuation"}},[s._v("(")]),a("span",{attrs:{class:"token class-name"}},[s._v("error")]),a("span",{attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),a("span",{attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),a("span",{attrs:{class:"token keyword"}},[s._v("this")]),a("span",{attrs:{class:"token punctuation"}},[s._v(".")]),s._v("body "),a("span",{attrs:{class:"token operator"}},[s._v("=")]),s._v(" error"),a("span",{attrs:{class:"token punctuation"}},[s._v(";")]),s._v("\n "),a("span",{attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n "),a("span",{attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n"),a("span",{attrs:{class:"token punctuation"}},[s._v("}")]),a("span",{attrs:{class:"token punctuation"}},[s._v(";")]),s._v(" \n")])])])])}],!1,null,null,null);o.options.__file="sessions.md";t.default=o.exports}}]); \ No newline at end of file diff --git a/docs/.vuepress/dist/assets/js/23.15e5a0c3.js b/docs/.vuepress/dist/assets/js/23.15e5a0c3.js new file mode 100644 index 0000000000..7b46d9df82 --- /dev/null +++ b/docs/.vuepress/dist/assets/js/23.15e5a0c3.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[23],{216:function(t,s,n){"use strict";n.r(s);var a=n(0),e=Object(a.a)({},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"content"},[t._m(0),t._v(" "),n("p",[t._v("Strapi's test suite is written using "),n("a",{attrs:{href:"https://mochajs.org/",target:"_blank",rel:"noopener noreferrer"}},[t._v("mocha"),n("OutboundLink")],1),t._v(" and although\nStrapi doesn't impose any testing framework for your apps, in this example we\nwill setup tests using the mocha framework.")]),t._v(" "),t._m(1),t._v(" "),n("p",[t._v("Before writing tests, you should setup a basic directory structure, like this:")]),t._v(" "),t._m(2),t._m(3),t._v(" "),t._m(4),t._v(" "),t._m(5),t._v(" "),t._m(6),t._m(7),t._v(" "),n("p",[t._v("Once you have setup your directory structure, you can start writing your tests.\nIn this example we use "),n("a",{attrs:{href:"https://github.com/avbel/co-supertest",target:"_blank",rel:"noopener noreferrer"}},[t._v("co-supertest"),n("OutboundLink")],1),t._v(",\na "),n("code",[t._v("co")]),t._v(" and "),n("code",[t._v("Supertest")]),t._v(" integration library.\n"),n("a",{attrs:{href:"https://github.com/visionmedia/supertest",target:"_blank",rel:"noopener noreferrer"}},[t._v("Supertest"),n("OutboundLink")],1),t._v(" provides several useful\nmethods for testing HTTP requests."),n("br"),t._v("\nIf you want to test an api endpoint, you can do it like this:")]),t._v(" "),t._m(8),t._v(" "),t._m(9),t._m(10),t._v(" "),t._m(11),t._v(" "),t._m(12),t._v(" "),t._m(13),t._m(14)])},[function(){var t=this.$createElement,s=this._self._c||t;return s("h1",{attrs:{id:"testing"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#testing","aria-hidden":"true"}},[this._v("#")]),this._v(" Testing")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"setup"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#setup","aria-hidden":"true"}},[this._v("#")]),this._v(" Setup")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language- extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("./strapiApp\n├── api/\n├── ...\n├── test/\n│ ├── integration/\n│ │ ├── controllers/\n│ │ │ └── my_endpoint.test.js\n│ │ ├── models/\n│ │ │ └── my_model.test.js\n│ │ └── ...\n| ├── ...\n│ ├── bootstrap.js\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"boostrap"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#boostrap","aria-hidden":"true"}},[this._v("#")]),this._v(" Boostrap")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("We are going to setup a "),n("code",[t._v("bootstrap.js")]),t._v(" with "),n("code",[t._v("before")]),t._v(" and "),n("code",[t._v("after")]),t._v(" hooks to\nperform any actions before and after our tests."),n("br"),t._v("\nIn this example, the app server is started before running any tests an stop\nthe server after tests are completed.")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("em",[this._v("./test/bootstrap.js")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-js extra-class"},[n("pre",{pre:!0,attrs:{class:"language-js"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("const")]),t._v(" strapi "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("require")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v("'strapi'")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),n("span",{attrs:{class:"token function"}},[t._v("before")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token keyword"}},[t._v("function")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("done"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n strapi"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("start")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("function")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("done")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),n("span",{attrs:{class:"token function"}},[t._v("done")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" strapi"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),n("span",{attrs:{class:"token function"}},[t._v("after")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token keyword"}},[t._v("function")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("done"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n strapi"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("stop")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token function"}},[t._v("done")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"writing-tests"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#writing-tests","aria-hidden":"true"}},[this._v("#")]),this._v(" Writing tests")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("em",[this._v("./test/integration/controllers/my_endpoint.js")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-js extra-class"},[n("pre",{pre:!0,attrs:{class:"language-js"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("const")]),t._v(" request "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("require")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v("'co-supertest'")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),n("span",{attrs:{class:"token function"}},[t._v("describe")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v("'MyEndpoint Controller Integration'")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("function")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("describe")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v("'GET /my_endpoint'")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("function")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("it")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v("'should return 200 status code'")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("function")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("*")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("yield")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("request")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("strapi"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("config"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("url"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token keyword"}},[t._v("get")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v("'/my_endpoint'")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("expect")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token number"}},[t._v("200")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("expect")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v("'Content-Type'")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token regex"}},[t._v("/json/")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("end")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"running-tests"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#running-tests","aria-hidden":"true"}},[this._v("#")]),this._v(" Running tests")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("In order to run tests you can use "),s("code",[this._v("npm test")]),this._v(". In your "),s("code",[this._v("package.json")]),this._v(", in the\n"),s("code",[this._v("scripts")]),this._v(" section, add this:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("em",[this._v("./package.json")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-js extra-class"},[n("pre",{pre:!0,attrs:{class:"language-js"}},[n("code",[n("span",{attrs:{class:"token string"}},[t._v('"scripts"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token string"}},[t._v('"test"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token string"}},[t._v('"mocha --require co-mocha test/bootstrap.js test/**/*.test.js"')]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Remember to run "),s("code",[this._v("test/bootstrap.js")]),this._v(" before any other tests and, if you want,\nuse the "),s("code",[this._v("--require")]),this._v(" option to pass any required dependencies you need available\nin your tests.")])}],!1,null,null,null);e.options.__file="testing.md";s.default=e.exports}}]); \ No newline at end of file diff --git a/docs/.vuepress/dist/assets/js/24.6fdf34d0.js b/docs/.vuepress/dist/assets/js/24.6fdf34d0.js new file mode 100644 index 0000000000..365ff551cd --- /dev/null +++ b/docs/.vuepress/dist/assets/js/24.6fdf34d0.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[24],{215:function(t,s,a){"use strict";a.r(s);var n=a(0),e=Object(n.a)({},function(){this.$createElement;this._self._c;return this._m(0)},[function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"content"},[a("h1",{attrs:{id:"upload"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#upload","aria-hidden":"true"}},[t._v("#")]),t._v(" Upload")]),t._v(" "),a("p",[t._v("Strapi contains a set of tools to upload files.")]),t._v(" "),a("h2",{attrs:{id:"upload-config"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#upload-config","aria-hidden":"true"}},[t._v("#")]),t._v(" Upload config")]),t._v(" "),a("p",[t._v("To change the upload config, edit the "),a("code",[t._v("./api/upload/config/settings.json")]),t._v(" file.")]),t._v(" "),a("p",[t._v("For the config bellow, please use refer to the "),a("code",[t._v("[co-busboy](https://github.com/cojs/busboy)")]),t._v(" node module documentation.")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"upload"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"folder"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"public/upload"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"acceptedExtensions"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"*"')]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"headers"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"highWaterMark"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('""')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"fileHwm"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('""')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"defCharset"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('""')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"preservePath"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('""')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"limits"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"fieldNameSize"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('""')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"fieldSize"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('""')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"fields"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('""')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"fileSize"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('""')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"files"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('""')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"parts"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('""')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"headerPairs"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('""')]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("h2",{attrs:{id:"upload-service"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#upload-service","aria-hidden":"true"}},[t._v("#")]),t._v(" Upload service")]),t._v(" "),a("p",[t._v("The upload service allows you to easily upload files from anywhere in your application.")]),t._v(" "),a("p",[t._v("Usage as a promise (yieldable) :")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("yield")]),t._v(" strapi"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("api"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("upload"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("services"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("upload"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("upload")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("part"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),a("h2",{attrs:{id:"upload-api"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#upload-api","aria-hidden":"true"}},[t._v("#")]),t._v(" Upload API")]),t._v(" "),a("p",[t._v("The upload API is a simple API which can be used from your client\n(front-end, mobile...) application to upload files.")]),t._v(" "),a("p",[t._v("Route used to upload files:")]),t._v(" "),a("div",{staticClass:"language-bash extra-class"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[t._v("POST /upload\n")])])]),a("p",[t._v("To use this route, you have to submit a HTML form with "),a("code",[t._v("multipart/*")]),t._v(" enctype\n(or fake it if you are using a web front-end framework like AngularJS).")]),t._v(" "),a("p",[t._v("Response payload:")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"readable"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token boolean"}},[t._v("true")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"domain"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("null")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"truncated"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token boolean"}},[t._v("false")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"fieldname"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"file"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"filename"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"1445421755771-image.jpg"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"encoding"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"7bit"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"transferEncoding"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"7bit"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"mime"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"image/jpeg"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"mimeType"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"image/jpeg"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"originalFilenameFormatted"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"image.jpg"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"originalFilename"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"image.jpg"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"template"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"default"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"lang"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"en"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"createdAt"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"2015-10-21T10:02:35.776Z"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"updatedAt"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"2015-10-21T10:02:35.776Z"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"id"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("2")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n")])])]),a("h2",{attrs:{id:"upload-model"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#upload-model","aria-hidden":"true"}},[t._v("#")]),t._v(" Upload model")]),t._v(" "),a("p",[t._v("Each uploaded file description is registered in the database. So you can retrieve\nthem whenever you want. However, you can disable this option by overriding the\nupload service logic.")])])}],!1,null,null,null);e.options.__file="upload.md";s.default=e.exports}}]); \ No newline at end of file diff --git a/docs/.vuepress/dist/assets/js/25.21c9a549.js b/docs/.vuepress/dist/assets/js/25.21c9a549.js new file mode 100644 index 0000000000..13cdcd7ed8 --- /dev/null +++ b/docs/.vuepress/dist/assets/js/25.21c9a549.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[25],{214:function(t,s,a){"use strict";a.r(s);var n=a(0),e=Object(n.a)({},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"content"},[t._m(0),t._v(" "),a("p",[t._v("Most of the web applications require a user management system: registration, login,\nreset password, etc.")]),t._v(" "),a("p",[t._v("To avoid you to reinvent the wheel, Strapi embedded a full featured user management\nsystem powered by "),a("a",{attrs:{href:"https://github.com/simov/grant",target:"_blank",rel:"noopener noreferrer"}},[t._v("Grant"),a("OutboundLink")],1),t._v(" and JSON Web Token (JWT).")]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),a("p",[t._v("Request payload:")]),t._v(" "),t._m(3),a("p",[t._v("Response payload:")]),t._v(" "),t._m(4),t._m(5),t._v(" "),t._m(6),t._v(" "),a("p",[t._v("Request payload:")]),t._v(" "),t._m(7),a("p",[t._v("Response payload:")]),t._v(" "),t._m(8),t._m(9),t._v(" "),t._m(10),t._v(" "),t._m(11),t._v(" "),t._m(12),t._v(" "),a("p",[t._v("Thanks to "),a("a",{attrs:{href:"https://github.com/simov/grant",target:"_blank",rel:"noopener noreferrer"}},[t._v("Grant"),a("OutboundLink")],1),t._v(" and "),a("a",{attrs:{href:"https://github.com/simov/purest",target:"_blank",rel:"noopener noreferrer"}},[t._v("Purest"),a("OutboundLink")],1),t._v(", you can easily use OAuth and OAuth2\nproviders to enable authentication in your application. By default,\nStrapi comes with four providers:")]),t._v(" "),t._m(13),t._v(" "),t._m(14),t._v(" "),t._m(15),t._v(" "),t._m(16),t._v(" "),a("p",[t._v("Response payload:")]),t._v(" "),t._m(17),t._m(18),t._v(" "),a("p",[t._v("Strapi comes with 5 providers. If you want to add another one, it can be easily done thanks to "),a("a",{attrs:{href:"https://github.com/simov/purest",target:"_blank",rel:"noopener noreferrer"}},[t._v("Purest"),a("OutboundLink")],1),t._v(", by adding it in the Grant service.")]),t._v(" "),t._m(19),t._v(" "),t._m(20),t._v(" "),a("p",[t._v("Request payload:")]),t._v(" "),t._m(21),t._m(22),t._v(" "),t._m(23),t._v(" "),a("p",[t._v("Request payload:")]),t._v(" "),t._m(24),a("p",[t._v("Response payload:")]),t._v(" "),t._m(25),t._m(26),t._v(" "),t._m(27)])},[function(){var t=this.$createElement,s=this._self._c||t;return s("h1",{attrs:{id:"users"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#users","aria-hidden":"true"}},[this._v("#")]),this._v(" Users")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"local-registration"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#local-registration","aria-hidden":"true"}},[this._v("#")]),this._v(" Local Registration")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Route used to register a user to your application: "),s("code",[this._v("POST /auth/local/register")]),this._v(".")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"username"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"John DOE"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"email"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"contact@company.com"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"password"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"123456"')]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"user"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"jwt"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('""')]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"local-login"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#local-login","aria-hidden":"true"}},[this._v("#")]),this._v(" Local Login")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Route used to login a user to your application: "),s("code",[this._v("POST /auth/local")]),this._v(".")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"identifier"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"contact@company.com"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"password"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"123456"')]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"user"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"jwt"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('""')]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"authentication"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#authentication","aria-hidden":"true"}},[this._v("#")]),this._v(" Authentication")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("JWT does not use session. Once you get the token, it has to be stored in front (for\nexample in the "),s("code",[this._v("localstorage")]),this._v("), and sent within each request. The token can be sent:")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("ul",[a("li",[t._v("in the header ("),a("code",[t._v("Bearer")]),t._v(")")]),t._v(" "),a("li",[t._v("in the body ("),a("code",[t._v("token")]),t._v(" field)")]),t._v(" "),a("li",[t._v("in the querystring ("),a("code",[t._v("token")]),t._v(" field)")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"providers"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#providers","aria-hidden":"true"}},[this._v("#")]),this._v(" Providers")])},function(){var t=this.$createElement,s=this._self._c||t;return s("ul",[s("li",[this._v("Facebook")]),this._v(" "),s("li",[this._v("Google")]),this._v(" "),s("li",[this._v("Github")]),this._v(" "),s("li",[this._v("Linkedin2 (Oauth2 Provider for Linkedin)")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("To use the providers authentication, set your credentials in\n"),s("code",[this._v("./api/user/config/environments/development/grant.json")]),this._v(".")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Redirect your user to: "),s("code",[this._v("GET /connect/:provider")]),this._v(".")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("After his approval, he will be redirected to "),s("code",[this._v("/auth/:provider/callback")]),this._v(". The jwt and user will be available in the querystring.")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"user"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"jwt"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('""')]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"custom-providers"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#custom-providers","aria-hidden":"true"}},[this._v("#")]),this._v(" Custom providers")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"forgot-password"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#forgot-password","aria-hidden":"true"}},[this._v("#")]),this._v(" Forgot password")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Send an email to the user with an activation code: "),s("code",[this._v("POST /auth/forgot-password")]),this._v(".")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"email"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"contact@company.com"')]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"change-password"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#change-password","aria-hidden":"true"}},[this._v("#")]),this._v(" Change password")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v('Route used to update the password of a user after he asked for a\n"forgot-password" email: '),s("code",[this._v("POST /auth/change-password")]),this._v(".")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"code"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('""')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"password"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"123456"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"passwordConfirmation"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"123456"')]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"user"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"jwt"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('""')]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"accessing-user-from-requests"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#accessing-user-from-requests","aria-hidden":"true"}},[this._v("#")]),this._v(" Accessing user from requests.")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("If you want to access attributes of the logged in user, you can use "),s("code",[this._v("this.user")]),this._v(" inside of your controller action.")])}],!1,null,null,null);e.options.__file="users.md";s.default=e.exports}}]); \ No newline at end of file diff --git a/docs/.vuepress/dist/assets/js/26.6608295c.js b/docs/.vuepress/dist/assets/js/26.6608295c.js new file mode 100644 index 0000000000..3e8011f154 --- /dev/null +++ b/docs/.vuepress/dist/assets/js/26.6608295c.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[26],{213:function(t,s,n){"use strict";n.r(s);var e=n(0),a=Object(e.a)({},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"content"},[t._m(0),t._v(" "),n("p",[t._v("In Strapi, views are markup templates that are compiled on the server into HTML pages.\nIn most cases, views are used as the response to an incoming HTTP request.")]),t._v(" "),n("p",[t._v("By default, Strapi doesn't use views. The philosophy of the framework is to\nseparate the reusable backend application logic from the frontend.")]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),t._m(3),t._m(4),t._v(" "),t._m(5),t._v(" "),t._m(6),t._v(" "),n("p",[t._v("You don't need to specify the view extension if you use the default one sets in config.")]),t._v(" "),t._m(7),t._v(" "),t._m(8),t._m(9),t._m(10),t._v(" "),t._m(11),t._m(12),t._v(" "),t._m(13),t._v(" "),t._m(14),n("p",[t._v("Strapi supports all of those view engines:")]),t._v(" "),n("ul",[n("li",[n("a",{attrs:{href:"https://github.com/soywiz/atpl.js",target:"_blank",rel:"noopener noreferrer"}},[t._v("atpl"),n("OutboundLink")],1)]),t._v(" "),n("li",[n("a",{attrs:{href:"https://github.com/olado/doT",target:"_blank",rel:"noopener noreferrer"}},[t._v("doT.js"),n("OutboundLink")],1)]),t._v(" "),n("li",[n("a",{attrs:{href:"https://github.com/akdubya/dustjs",target:"_blank",rel:"noopener noreferrer"}},[t._v("dust (unmaintained)"),n("OutboundLink")],1)]),t._v(" "),n("li",[n("a",{attrs:{href:"https://github.com/linkedin/dustjs",target:"_blank",rel:"noopener noreferrer"}},[t._v("dustjs-linkedin (maintained fork of dust)"),n("OutboundLink")],1)]),t._v(" "),n("li",[n("a",{attrs:{href:"https://github.com/sstephenson/eco",target:"_blank",rel:"noopener noreferrer"}},[t._v("eco"),n("OutboundLink")],1)]),t._v(" "),n("li",[n("a",{attrs:{href:"https://github.com/baryshev/ect",target:"_blank",rel:"noopener noreferrer"}},[t._v("ect"),n("OutboundLink")],1)]),t._v(" "),n("li",[n("a",{attrs:{href:"https://github.com/visionmedia/ejs",target:"_blank",rel:"noopener noreferrer"}},[t._v("ejs"),n("OutboundLink")],1)]),t._v(" "),n("li",[n("a",{attrs:{href:"https://github.com/visionmedia/haml.js",target:"_blank",rel:"noopener noreferrer"}},[t._v("haml"),n("OutboundLink")],1)]),t._v(" "),n("li",[n("a",{attrs:{href:"https://github.com/9elements/haml-coffee",target:"_blank",rel:"noopener noreferrer"}},[t._v("haml-coffee"),n("OutboundLink")],1)]),t._v(" "),n("li",[n("a",{attrs:{href:"https://github.com/gregwebs/hamlet.js",target:"_blank",rel:"noopener noreferrer"}},[t._v("hamlet"),n("OutboundLink")],1)]),t._v(" "),n("li",[n("a",{attrs:{href:"https://github.com/wycats/handlebars.js/",target:"_blank",rel:"noopener noreferrer"}},[t._v("handlebars"),n("OutboundLink")],1)]),t._v(" "),n("li",[n("a",{attrs:{href:"https://github.com/twitter/hogan.js",target:"_blank",rel:"noopener noreferrer"}},[t._v("hogan"),n("OutboundLink")],1)]),t._v(" "),n("li",[n("a",{attrs:{href:"https://github.com/codemix/htmling",target:"_blank",rel:"noopener noreferrer"}},[t._v("htmling"),n("OutboundLink")],1)]),t._v(" "),n("li",[n("a",{attrs:{href:"https://github.com/visionmedia/jade",target:"_blank",rel:"noopener noreferrer"}},[t._v("jade"),n("OutboundLink")],1)]),t._v(" "),n("li",[n("a",{attrs:{href:"https://github.com/shinetech/jazz",target:"_blank",rel:"noopener noreferrer"}},[t._v("jazz"),n("OutboundLink")],1)]),t._v(" "),n("li",[n("a",{attrs:{href:"https://github.com/kof/node-jqtpl",target:"_blank",rel:"noopener noreferrer"}},[t._v("jqtpl"),n("OutboundLink")],1)]),t._v(" "),n("li",[n("a",{attrs:{href:"https://github.com/baryshev/just",target:"_blank",rel:"noopener noreferrer"}},[t._v("JUST"),n("OutboundLink")],1)]),t._v(" "),n("li",[n("a",{attrs:{href:"https://github.com/chjj/liquor",target:"_blank",rel:"noopener noreferrer"}},[t._v("liquor"),n("OutboundLink")],1)]),t._v(" "),n("li",[n("a",{attrs:{href:"https://github.com/bestiejs/lodash",target:"_blank",rel:"noopener noreferrer"}},[t._v("lodash"),n("OutboundLink")],1)]),t._v(" "),n("li",[n("a",{attrs:{href:"https://github.com/satchmorun/mote",target:"_blank",rel:"noopener noreferrer"}},[t._v("mote"),n("OutboundLink")],1)]),t._v(" "),n("li",[n("a",{attrs:{href:"https://github.com/janl/mustache.js",target:"_blank",rel:"noopener noreferrer"}},[t._v("mustache"),n("OutboundLink")],1)]),t._v(" "),n("li",[n("a",{attrs:{href:"https://github.com/mozilla/nunjucks",target:"_blank",rel:"noopener noreferrer"}},[t._v("nunjucks"),n("OutboundLink")],1)]),t._v(" "),n("li",[n("a",{attrs:{href:"https://github.com/jepso/QEJS",target:"_blank",rel:"noopener noreferrer"}},[t._v("QEJS"),n("OutboundLink")],1)]),t._v(" "),n("li",[n("a",{attrs:{href:"https://github.com/Rich-Harris/Ractive",target:"_blank",rel:"noopener noreferrer"}},[t._v("ractive"),n("OutboundLink")],1)]),t._v(" "),n("li",[n("a",{attrs:{href:"https://github.com/facebook/react",target:"_blank",rel:"noopener noreferrer"}},[t._v("react"),n("OutboundLink")],1)]),t._v(" "),n("li",[n("a",{attrs:{href:"https://github.com/paularmstrong/swig",target:"_blank",rel:"noopener noreferrer"}},[t._v("swig"),n("OutboundLink")],1)]),t._v(" "),n("li",[n("a",{attrs:{href:"http://archan937.github.com/templayed.js/",target:"_blank",rel:"noopener noreferrer"}},[t._v("templayed"),n("OutboundLink")],1)]),t._v(" "),n("li",[n("a",{attrs:{href:"https://github.com/leizongmin/tinyliquid",target:"_blank",rel:"noopener noreferrer"}},[t._v("liquid"),n("OutboundLink")],1)]),t._v(" "),n("li",[n("a",{attrs:{href:"https://github.com/malgorithms/toffee",target:"_blank",rel:"noopener noreferrer"}},[t._v("toffee"),n("OutboundLink")],1)]),t._v(" "),n("li",[n("a",{attrs:{href:"https://github.com/documentcloud/underscore",target:"_blank",rel:"noopener noreferrer"}},[t._v("underscore"),n("OutboundLink")],1)]),t._v(" "),n("li",[n("a",{attrs:{href:"https://github.com/jeremyruppel/walrus",target:"_blank",rel:"noopener noreferrer"}},[t._v("walrus"),n("OutboundLink")],1)]),t._v(" "),n("li",[n("a",{attrs:{href:"https://github.com/gsf/whiskers.js",target:"_blank",rel:"noopener noreferrer"}},[t._v("whiskers"),n("OutboundLink")],1)])])])},[function(){var t=this.$createElement,s=this._self._c||t;return s("h1",{attrs:{id:"views"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#views","aria-hidden":"true"}},[this._v("#")]),this._v(" Views")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("If you want to activate views, set the "),s("code",[this._v("views")]),this._v(" in "),s("code",[this._v("./config/general.json")]),this._v(".")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("For example, if you want to use "),s("code",[this._v("lodash")]),this._v(" for "),s("code",[this._v(".html")]),this._v(" files and use it by default,\nyou may set up your "),s("code",[this._v("views")]),this._v(" object as below:")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-js extra-class"},[n("pre",{pre:!0,attrs:{class:"language-js"}},[n("code",[n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token string"}},[t._v('"views"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token string"}},[t._v('"map"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token string"}},[t._v('"html"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token string"}},[t._v('"lodash"')]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token string"}},[t._v('"default"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token string"}},[t._v('"html"')]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Views are defined in your application's "),s("code",[this._v("./views")]),this._v(" directory.")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"render-a-view"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#render-a-view","aria-hidden":"true"}},[this._v("#")]),this._v(" Render a view")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Simply use "),s("code",[this._v("this.render")]),this._v(" instead of "),s("code",[this._v("this.body")]),this._v(" to render a view.")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("Using the config we wrote above with "),n("code",[t._v("lodash")]),t._v(" for "),n("code",[t._v(".html")]),t._v(" files and use the "),n("code",[t._v("html")]),t._v("\nextension by default, this example will render "),n("code",[t._v("./views/user.html")]),t._v(" with\nLodash as template engine.")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-js extra-class"},[n("pre",{pre:!0,attrs:{class:"language-js"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("yield")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("this")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("render")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v("'user'")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n firstname"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token string"}},[t._v("'John'")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n lastname"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token string"}},[t._v("'Doe'")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-html extra-class"},[n("pre",{pre:!0,attrs:{class:"language-html"}},[n("code",[n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token punctuation"}},[t._v("<")]),t._v("html")]),n("span",{attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token punctuation"}},[t._v("<")]),t._v("head")]),n("span",{attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("..."),n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token punctuation"}},[t._v("<")]),t._v("body")]),n("span",{attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),n("span",{attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("Firstname: <% firstname %>"),n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token punctuation"}},[t._v("<")]),t._v("br")]),n("span",{attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("Lastname: <% lastname %>"),n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Here is the same example with the "),s("code",[this._v("jade")]),this._v(" extension, not used by default:")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-js extra-class"},[n("pre",{pre:!0,attrs:{class:"language-js"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("yield")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("this")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("render")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v("'user.jade'")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n firstname"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token string"}},[t._v("'John'")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n lastname"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token string"}},[t._v("'Doe'")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"supported-template-engines"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#supported-template-engines","aria-hidden":"true"}},[this._v("#")]),this._v(" Supported template engines")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("To use a view engine, you should use npm to install it in your project and\nset the "),n("code",[t._v("map")]),t._v(" object in "),n("code",[t._v("strapi.config.views")]),t._v(". For example, if you want to use\n"),n("code",[t._v("swig")]),t._v(" for "),n("code",[t._v(".html")]),t._v(" files and "),n("code",[t._v("hogan")]),t._v(" for "),n("code",[t._v(".md")]),t._v(" files, you may configure the\n"),n("code",[t._v("map")]),t._v(" object as below:")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-js extra-class"},[n("pre",{pre:!0,attrs:{class:"language-js"}},[n("code",[n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token string"}},[t._v('"views"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token string"}},[t._v('"map"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token string"}},[t._v('"html"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token string"}},[t._v('"swig"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token string"}},[t._v('"md"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token string"}},[t._v('"hogan"')]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])}],!1,null,null,null);a.options.__file="views.md";s.default=a.exports}}]); \ No newline at end of file diff --git a/docs/.vuepress/dist/assets/js/27.2c9596ea.js b/docs/.vuepress/dist/assets/js/27.2c9596ea.js new file mode 100644 index 0000000000..d686f06f04 --- /dev/null +++ b/docs/.vuepress/dist/assets/js/27.2c9596ea.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[27],{212:function(t,e,r){"use strict";r.r(e);var a=r(0),n=Object(a.a)({},function(){var t=this,e=t.$createElement,r=t._self._c||e;return r("div",{staticClass:"content"},[r("div",{staticClass:"intro custom-block"},[t._m(0),t._v(" "),t._m(1),t._v(" "),r("p",[t._v("The most advanced open-source Content Management Framework to build powerful API with no effort.")]),t._v(" "),r("p",{staticClass:"flex justify-around"},[r("a",{attrs:{href:"https://www.npmjs.org/package/strapi",target:"_blank",rel:"noopener noreferrer"}},[r("img",{attrs:{src:"https://img.shields.io/npm/v/strapi.svg",alt:"npm version"}}),r("OutboundLink")],1),t._v(" "),r("a",{attrs:{href:"https://www.npmjs.org/package/strapi",target:"_blank",rel:"noopener noreferrer"}},[r("img",{attrs:{src:"https://img.shields.io/npm/dm/strapi.svg",alt:"npm downloads"}}),r("OutboundLink")],1),t._v(" "),r("a",{attrs:{href:"https://travis-ci.org/strapi/strapi",target:"_blank",rel:"noopener noreferrer"}},[r("img",{attrs:{src:"https://travis-ci.org/strapi/strapi.svg?branch=master",alt:"Build status"}}),r("OutboundLink")],1),t._v(" "),r("a",{attrs:{href:"http://slack.strapi.io",target:"_blank",rel:"noopener noreferrer"}},[r("img",{attrs:{src:"http://strapi-slack.herokuapp.com/badge.svg",alt:"Slack status"}}),r("OutboundLink")],1),t._v(" "),r("a",{attrs:{href:"https://heroku.com/deploy?template=https://github.com/strapi/strapi-heroku-app",target:"_blank",rel:"noopener noreferrer"}},[r("img",{attrs:{src:"https://www.herokucdn.com/deploy/button.svg",alt:"Heroku Deploy",height:"20"}}),r("OutboundLink")],1)])]),t._v(" "),t._m(2),t._v(" "),r("p",[t._v("We've been working on a major update for Strapi during the past months, rewriting the core framework and the dashboard.")]),t._v(" "),r("p",[t._v("This documentation is only related to Strapi v3@alpha.13 ("),r("a",{attrs:{href:"http://strapi.io/documentation/1.x.x",target:"_blank",rel:"noopener noreferrer"}},[t._v("v1 documentation is still available"),r("OutboundLink")],1),t._v(").")]),t._v(" "),r("p",[r("strong",[r("router-link",{attrs:{to:"./getting-started/installation.html"}},[t._v("Get Started")])],1),r("br"),t._v("\nLearn how to install Strapi and start developing your API.")]),t._v(" "),r("p",[r("strong",[r("router-link",{attrs:{to:"./cli/CLI.html"}},[t._v("Command Line Interface")])],1),r("br"),t._v("\nGet to know our CLI to make features the hacker way!")]),t._v(" "),r("p",[r("strong",[r("router-link",{attrs:{to:"./concepts/concepts.html"}},[t._v("Concepts")])],1),r("br"),t._v("\nGet to know more about Strapi and how it works.")]),t._v(" "),r("p",[r("strong",[r("router-link",{attrs:{to:"./configurations/configurations.html"}},[t._v("Guides")])],1),r("br"),t._v("\nGet familiar with Strapi. Discover concrete examples on how to develop the features you need.")]),t._v(" "),r("p",[r("strong",[r("router-link",{attrs:{to:"./plugin-development/quick-start.html"}},[t._v("Plugin Development")])],1),r("br"),t._v("\nUnderstand how to develop your own plugin.")]),t._v(" "),r("p",[r("strong",[r("router-link",{attrs:{to:"./api-reference/reference.html"}},[t._v("API Reference")])],1),r("br"),t._v("\nLearn about Strapi's API, the "),r("code",[t._v("strapi")]),t._v(" object that is available in your backend.")]),t._v(" "),r("p",[r("strong",[r("router-link",{attrs:{to:"./migration/migration-guide.html"}},[t._v("Migration guide")])],1),r("br")])])},[function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("img",{attrs:{src:"https://cldup.com/7umchwdUBh.png",alt:"Logo"}})])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"api-creation-made-simple-secure-and-fast"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#api-creation-made-simple-secure-and-fast","aria-hidden":"true"}},[this._v("#")]),this._v(" API creation made simple, secure and fast.")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"v3-alpha-13-is-available"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#v3-alpha-13-is-available","aria-hidden":"true"}},[this._v("#")]),this._v(" v3@alpha.13 is available!")])}],!1,null,null,null);n.options.__file="README.md";e.default=n.exports}}]); \ No newline at end of file diff --git a/docs/.vuepress/dist/assets/js/28.9b077c15.js b/docs/.vuepress/dist/assets/js/28.9b077c15.js new file mode 100644 index 0000000000..32aa13c44d --- /dev/null +++ b/docs/.vuepress/dist/assets/js/28.9b077c15.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[28],{211:function(t,e,r){"use strict";r.r(e);var i=r(0),s=Object(i.a)({},function(){var t=this,e=t.$createElement,r=t._self._c||e;return r("div",{staticClass:"content"},[t._m(0),t._v(" "),t._m(1),t._v(" "),r("ul",[r("li",[r("a",{attrs:{href:"http://strapi.io",target:"_blank",rel:"noopener noreferrer"}},[t._v("Strapi Website"),r("OutboundLink")],1)]),t._v(" "),r("li",[r("a",{attrs:{href:"https://github.com/strapi/strapi",target:"_blank",rel:"noopener noreferrer"}},[t._v("GitHub Repository"),r("OutboundLink")],1)]),t._v(" "),r("li",[r("a",{attrs:{href:"https://github.com/strapi/strapi/blob/master/CONTRIBUTING.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("Contributing Guide"),r("OutboundLink")],1)])]),t._v(" "),t._m(2),t._v(" "),r("ul",[r("li",[r("router-link",{attrs:{to:"./getting-started/installation.html"}},[t._v("Installation")])],1),t._v(" "),r("li",[r("router-link",{attrs:{to:"./getting-started/quick-start.html"}},[t._v("Quick start")])],1)]),t._v(" "),t._m(3),t._v(" "),r("ul",[r("li",[r("router-link",{attrs:{to:"./cli/CLI.html"}},[t._v("Command line interface")])],1)]),t._v(" "),t._m(4),t._v(" "),r("ul",[r("li",[r("router-link",{attrs:{to:"./concepts/concepts.html"}},[t._v("Table of contents")])],1)]),t._v(" "),t._m(5),t._v(" "),r("ul",[r("li",[r("router-link",{attrs:{to:"./guides/authentication.html"}},[t._v("Authentication")])],1),t._v(" "),r("li",[r("router-link",{attrs:{to:"./configurations/configurations.html"}},[t._v("Configurations")])],1),t._v(" "),r("li",[r("router-link",{attrs:{to:"./guides/controllers.html"}},[t._v("Controllers")])],1),t._v(" "),r("li",[r("router-link",{attrs:{to:"./guides/deployment.html"}},[t._v("Deployment")])],1),t._v(" "),r("li",[r("router-link",{attrs:{to:"./guides/email.html"}},[t._v("Email")])],1),t._v(" "),r("li",[r("router-link",{attrs:{to:"./guides/upload.html"}},[t._v("File Upload")])],1),t._v(" "),r("li",[r("router-link",{attrs:{to:"./guides/filters.html"}},[t._v("Filters")])],1),t._v(" "),r("li",[r("router-link",{attrs:{to:"./guides/graphql.html"}},[t._v("GraphQL")])],1),t._v(" "),r("li",[r("router-link",{attrs:{to:"./guides/i18n.html"}},[t._v("Internationalization")])],1),t._v(" "),r("li",[r("router-link",{attrs:{to:"./guides/models.html"}},[t._v("Models")])],1),t._v(" "),r("li",[r("router-link",{attrs:{to:"./guides/policies.html"}},[t._v("Policies")])],1),t._v(" "),r("li",[r("router-link",{attrs:{to:"./guides/public-assets.html"}},[t._v("Public Assets")])],1),t._v(" "),r("li",[r("router-link",{attrs:{to:"./guides/requests.html"}},[t._v("Requests")])],1),t._v(" "),r("li",[r("router-link",{attrs:{to:"./guides/responses.html"}},[t._v("Responses")])],1),t._v(" "),r("li",[r("router-link",{attrs:{to:"./guides/routing.html"}},[t._v("Routing")])],1),t._v(" "),r("li",[r("router-link",{attrs:{to:"./guides/services.html"}},[t._v("Services")])],1)]),t._v(" "),t._m(6),t._v(" "),r("ul",[r("li",[r("router-link",{attrs:{to:"./plugin-development/quick-start.html"}},[t._v("Quick start")])],1),t._v(" "),r("li",[r("router-link",{attrs:{to:"./plugin-development/plugin-architecture.html"}},[t._v("Plugin Folders and Files Architecture")])],1),t._v(" "),r("li",[r("router-link",{attrs:{to:"./plugin-development/backend-development.html"}},[t._v("Back-end Development")])],1),t._v(" "),r("li",[r("router-link",{attrs:{to:"./plugin-development/frontend-development.html"}},[t._v("Front-end Development")])],1),t._v(" "),r("li",[r("router-link",{attrs:{to:"./plugin-development/frontend-use-cases.html"}},[t._v("Front-end Use Cases")])],1),t._v(" "),r("li",[r("router-link",{attrs:{to:"./plugin-development/utils.html"}},[t._v("Front-end Helpers")])],1),t._v(" "),r("li",[r("router-link",{attrs:{to:"./plugin-development/ui-components.html"}},[t._v("Front-end UI Components")])],1)]),t._v(" "),t._m(7),t._v(" "),r("ul",[r("li",[r("router-link",{attrs:{to:"./advanced/customize-admin.html"}},[t._v("Admin panel")])],1),t._v(" "),r("li",[r("router-link",{attrs:{to:"./advanced/logging.html"}},[t._v("Logging")])],1),t._v(" "),r("li",[r("router-link",{attrs:{to:"./advanced/hooks.html"}},[t._v("Hooks")])],1),t._v(" "),r("li",[r("router-link",{attrs:{to:"./advanced/middlewares.html"}},[t._v("Middlewares")])],1),t._v(" "),r("li",[r("router-link",{attrs:{to:"./advanced/usage-tracking.html"}},[t._v("Tracking usage")])],1)]),t._v(" "),t._m(8),t._v(" "),r("ul",[r("li",[r("router-link",{attrs:{to:"./api-reference/reference.html"}},[t._v("Table of contents")])],1)]),t._v(" "),t._m(9),t._v(" "),r("ul",[r("li",[r("router-link",{attrs:{to:"./tutorials/"}},[t._v("Table of contents")])],1)]),t._v(" "),t._m(10),t._v(" "),r("ul",[r("li",[r("a",{attrs:{href:"https://github.com/strapi/strapi/wiki",target:"_blank",rel:"noopener noreferrer"}},[t._v("Migration guides"),r("OutboundLink")],1)])])])},[function(){var t=this.$createElement,e=this._self._c||t;return e("h1",{attrs:{id:"summary"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#summary","aria-hidden":"true"}},[this._v("#")]),this._v(" Summary")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"useful-links"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#useful-links","aria-hidden":"true"}},[this._v("#")]),this._v(" Useful links")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"getting-started"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#getting-started","aria-hidden":"true"}},[this._v("#")]),this._v(" Getting started")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"cli"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#cli","aria-hidden":"true"}},[this._v("#")]),this._v(" CLI")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"concepts"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#concepts","aria-hidden":"true"}},[this._v("#")]),this._v(" Concepts")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"guides"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#guides","aria-hidden":"true"}},[this._v("#")]),this._v(" Guides")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"plugin-development"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#plugin-development","aria-hidden":"true"}},[this._v("#")]),this._v(" Plugin Development")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"advanced-usage"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#advanced-usage","aria-hidden":"true"}},[this._v("#")]),this._v(" Advanced Usage")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"api-reference"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#api-reference","aria-hidden":"true"}},[this._v("#")]),this._v(" API Reference")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"tutorials"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#tutorials","aria-hidden":"true"}},[this._v("#")]),this._v(" Tutorials")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"migration"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#migration","aria-hidden":"true"}},[this._v("#")]),this._v(" Migration")])}],!1,null,null,null);s.options.__file="SUMMARY.md";e.default=s.exports}}]); \ No newline at end of file diff --git a/docs/.vuepress/dist/assets/js/29.8ea8ecc1.js b/docs/.vuepress/dist/assets/js/29.8ea8ecc1.js new file mode 100644 index 0000000000..6accb642b4 --- /dev/null +++ b/docs/.vuepress/dist/assets/js/29.8ea8ecc1.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[29],{210:function(t,s,a){"use strict";a.r(s);var n=a(0),e=Object(n.a)({},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"content"},[t._m(0),t._v(" "),a("p",[t._v("One of Strapi's main feature is its fully extendable and customizable admin panel. This section explains how the admin panel section is structured and how to customize it.")]),t._v(" "),a("p",[t._v("See the "),a("a",{attrs:{href:"https://github.com/strapi/strapi/blob/master/CONTRIBUTING.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("Contributing Guide"),a("OutboundLink")],1),t._v(" for informations on how to develop the Strapi's admin interface.")]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),t._m(3),a("hr"),t._v(" "),t._m(4),t._v(" "),a("p",[t._v("The administration panel can be customised according to your needs, so you can make it reflects your identity: colors, fonts, logo, etc.")]),t._v(" "),t._m(5),t._v(" "),a("p",[t._v("By default, the administration panel is exposed via "),a("a",{attrs:{href:"http://localhost:1337/admin",target:"_blank",rel:"noopener noreferrer"}},[t._v("http://localhost:1337/admin"),a("OutboundLink")],1),t._v(". However, for security reasons, you can easily update this path.")]),t._v(" "),t._m(6),t._v(" "),t._m(7),a("p",[t._v("The panel will be available through "),a("a",{attrs:{href:"http://localhost:1337/dashboard",target:"_blank",rel:"noopener noreferrer"}},[t._v("http://localhost:1337/dashboard"),a("OutboundLink")],1),t._v(" with the configurations above.")]),t._v(" "),t._m(8),t._v(" "),t._m(9),t._v(" "),t._m(10),t._v(" "),t._m(11),t._v(" "),t._m(12),t._v(" "),t._m(13),t._v(" "),t._m(14),t._v(" "),a("p",[t._v("You should be able to see the admin at "),a("a",{attrs:{href:"http://localhost:4000/admin",target:"_blank",rel:"noopener noreferrer"}},[t._v("http://localhost:4000/admin"),a("OutboundLink")],1),t._v(".")]),t._v(" "),t._m(15),t._v(" "),t._m(16),t._v(" "),a("p",[t._v("Admin's styles use "),a("a",{attrs:{href:"https://github.com/postcss/postcss",target:"_blank",rel:"noopener noreferrer"}},[t._v("PostCSS"),a("OutboundLink")],1),t._v(", and more precisely "),a("a",{attrs:{href:"https://github.com/postcss/postcss-scss",target:"_blank",rel:"noopener noreferrer"}},[t._v("PostCSS-SCSS"),a("OutboundLink")],1),t._v(". In this way, colors are stored in variables. The values of these variables can be easily changed in files located in "),a("code",[t._v("./admin/admin/src/styles/variables/")]),t._v(".")]),t._v(" "),a("p",[t._v("The changes should be automatically visible.")]),t._v(" "),t._m(17),t._v(" "),a("p",[t._v("Fonts can also be overridden:")]),t._v(" "),t._m(18),t._v(" "),t._m(19),t._v(" "),t._m(20),t._v(" "),t._m(21),t._v(" "),a("hr"),t._v(" "),t._m(22),t._v(" "),a("p",[t._v("To build the administration, run the following command from the root directory of your project.")]),t._v(" "),t._m(23),t._m(24),t._v(" "),a("p",[t._v("After you have built the admininistration you can now create a new project to develop your API with the changes implemented.")]),t._v(" "),t._m(25),t._v(" "),a("hr"),t._v(" "),t._m(26),t._v(" "),a("p",[t._v("The administration is nothing more than a React front-end application calling an API. The front-end and the back-end are independent and can be deployed on different servers which brings us to different scenarios:")]),t._v(" "),t._m(27),t._v(" "),a("p",[t._v("Let's dive into the build configurations for each case.")]),t._v(" "),t._m(28),t._v(" "),a("p",[t._v("You don't need to touch anything in your configuration file. This is the default behaviour and the build configurations will be automatically set. The server will start on the defined port and the administration panel will be accessible through http://yourdomain.com:1337/dashboard.")]),t._v(" "),a("p",[t._v("You might want to change the path to access to the administration panel. Here the required configurations to change the path:")]),t._v(" "),t._m(29),t._v(" "),t._m(30),a("p",[a("strong",[t._v("You have to rebuild the administration panel to make this work.")]),t._v(" Please follow the "),a("router-link",{attrs:{to:"./../guides/deployment.html"}},[t._v("step #2 of the deployment guide")]),t._v(".")],1),t._v(" "),t._m(31),t._v(" "),a("p",[t._v("It's very common to deploy the front-end and the back-end on different servers. Here the required configurations to handle this case:")]),t._v(" "),t._m(32),t._v(" "),t._m(33),t._m(34),t._v(" "),t._m(35),t._v(" "),a("p",[t._v("The DOM should look like this:")]),t._v(" "),t._m(36),t._v(" "),t._m(37),t._m(38),t._v(" "),t._m(39),t._v(" "),a("p",[t._v("In this case, we suppose that you decided to put your administration and the plugins on the same server but on a different server as the API.")]),t._v(" "),t._m(40),t._v(" "),t._m(41),t._m(42),t._v(" "),t._m(43),t._v(" "),t._m(44),t._m(45),t._v(" "),t._m(46),t._v(" "),t._m(47),t._m(48)])},[function(){var t=this.$createElement,s=this._self._c||t;return s("h1",{attrs:{id:"admin-panel"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#admin-panel","aria-hidden":"true"}},[this._v("#")]),this._v(" Admin panel")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"files-structure"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#files-structure","aria-hidden":"true"}},[this._v("#")]),this._v(" Files structure")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("The entire logic of the admin panel is located in a single folder named "),s("code",[this._v("./admin/")]),this._v(". This directory contains the following structure:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language- extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("/admin\n└─── admin\n| └─── build // Webpack generated build of the front-end\n| └─── src // Front-end directory\n| └─── app.js // Entry point of the React application\n| └─── assets // Assets directory containing images,...\n| └─── components // Admin's React components directory\n| └─── containers // Admin's high level components directory\n| └─── favicon.ico // Favicon displayed in web browser\n| └─── i18n.js // Internalization logic\n| └─── index.html // Basic html file in which are injected necessary styles and scripts\n| └─── reducers.js // Redux reducers logic\n| └─── store.js // Redux store logic\n| └─── styles // Directory containing the global styles. Specific styles are defined at the component level\n| └─── translations // Directory containing text messages for each supported languages\n└─── config\n| └─── routes.json // Admin's API routes\n| └─── layout.json // Admin's specific settings\n└─── controllers // Admin's API controllers\n└─── services // Admin's API services\n└─── packages.json // Admin's npm dependencies\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"customization"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#customization","aria-hidden":"true"}},[this._v("#")]),this._v(" Customization")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"change-access-url"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#change-access-url","aria-hidden":"true"}},[this._v("#")]),this._v(" Change access URL")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("strong",[this._v("Path —")]),this._v(" "),s("code",[this._v("./config/environment/**/server.json")]),this._v(".")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-json extra-class"},[a("pre",{pre:!0,attrs:{class:"language-json"}},[a("code",[a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token property"}},[t._v('"host"')]),a("span",{attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"localhost"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token property"}},[t._v('"port"')]),a("span",{attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("1337")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token property"}},[t._v('"autoReload"')]),a("span",{attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token property"}},[t._v('"enabled"')]),a("span",{attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token boolean"}},[t._v("true")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token property"}},[t._v('"cron"')]),a("span",{attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token property"}},[t._v('"enabled"')]),a("span",{attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token boolean"}},[t._v("false")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token property"}},[t._v('"admin"')]),a("span",{attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token property"}},[t._v('"path"')]),a("span",{attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"/dashboard"')]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"development-mode"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#development-mode","aria-hidden":"true"}},[this._v("#")]),this._v(" Development mode")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Note that to modify the administration panel, your project needs to be created with using the "),s("code",[this._v("dev")]),this._v(" flag, an example of such would be: "),s("code",[this._v("strapi new strapi --dev")]),this._v(".")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("strong",[this._v("#1 — Install its own dependencies")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Run "),s("code",[this._v("npm install")]),this._v(" from the "),s("code",[this._v("./admin")]),this._v(" folder.")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("strong",[this._v("#2 — Launch the development server")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Run "),s("code",[this._v("npm start")]),this._v(" from the "),s("code",[this._v("./admin")]),this._v(" folder. That's all.")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("strong",[this._v("#3 — Go to the browser")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"note custom-block"},[s("p",[this._v("In development, all the plugins of your app are mounted in the same build as the administration panel.")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"colors"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#colors","aria-hidden":"true"}},[this._v("#")]),this._v(" Colors")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"fonts"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#fonts","aria-hidden":"true"}},[this._v("#")]),this._v(" Fonts")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("ul",[a("li",[t._v("Add the fonts files you need in "),a("code",[t._v("./admin/admin/src/styles/fonts")]),t._v(".")]),t._v(" "),a("li",[t._v("Import them from "),a("code",[t._v("./admin/admin/src/styles/base/fonts.scss")]),t._v(".")]),t._v(" "),a("li",[t._v("Use them by replacing the variables' values in "),a("code",[t._v("./admin/admin/src/styles/variables/variables.bootstrap.scss")]),t._v(".")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"logo"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#logo","aria-hidden":"true"}},[this._v("#")]),this._v(" Logo")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("To change the top-left displayed admin panel's logo, replace the image located at "),s("code",[this._v("./admin/admin/src/assets/images/logo-strapi.png")]),this._v(".")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"note custom-block"},[s("p",[this._v("make sure the size of your image is the same as the existing one (434px x 120px).")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"build"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#build","aria-hidden":"true"}},[this._v("#")]),this._v(" Build")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language- extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("npm run setup\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("This will replace the folder's content located at "),s("code",[this._v("./admin/admin/build")]),this._v(". Visit http://localhost:1337/admin/ to make sure your updates have been taken in account.")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"note custom-block"},[s("p",[this._v("You should now create a project without "),s("code",[this._v("--dev")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"deployment"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#deployment","aria-hidden":"true"}},[this._v("#")]),this._v(" Deployment")])},function(){var t=this.$createElement,s=this._self._c||t;return s("ol",[s("li",[this._v("Deploy the entire project on the same server.")]),this._v(" "),s("li",[this._v("Deploy the administration panel on another server (AWS S3, Azure, etc) than the API.")]),this._v(" "),s("li",[this._v("Deploy the administration panel and the plugins on another server than the API.")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"deploy-the-entire-project-on-the-same-server"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#deploy-the-entire-project-on-the-same-server","aria-hidden":"true"}},[this._v("#")]),this._v(" Deploy the entire project on the same server.")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("strong",[this._v("Path —")]),this._v(" "),s("code",[this._v("./config/environment/**/server.json")]),this._v(".")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"host"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"localhost"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"port"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("1337")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"autoReload"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"enabled"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token boolean"}},[t._v("false")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"cron"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"enabled"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token boolean"}},[t._v("false")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"admin"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"path"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"/dashboard"')]),t._v(" "),a("span",{attrs:{class:"token comment"}},[t._v("// We change the path to access to the admin (highly recommended for security reasons).")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"deploy-the-administration-panel-on-another-server-aws-s3-azure-etc-than-the-api"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#deploy-the-administration-panel-on-another-server-aws-s3-azure-etc-than-the-api","aria-hidden":"true"}},[this._v("#")]),this._v(" Deploy the administration panel on another server (AWS S3, Azure, etc) than the API.")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("strong",[this._v("Path —")]),this._v(" "),s("code",[this._v("./config/environment/**/server.json")]),this._v(".")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"host"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"localhost"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"port"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("1337")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"autoReload"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"enabled"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token boolean"}},[t._v("false")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"cron"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"enabled"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token boolean"}},[t._v("false")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"admin"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"path"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"/dashboard"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"build"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"host"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"/"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token comment"}},[t._v("// Note: The administration will be accessible from the root of the domain (ex: http//yourfrontend.com/)")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"backend"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"http://yourbackend.com"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"plugins"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"source"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"backend"')]),t._v(" "),a("span",{attrs:{class:"token comment"}},[t._v("// What does it means? The script tags in the index.html will use the backend value to load the plugins (ex: http://yourbackend.com/dashboard/content-manager/main.js).")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("The administration URL will be http://yourfrontend.com and every request from the panel will hit the backend at http://yourbackend.com. The plugins will be injected through the "),s("code",[this._v("origin")]),this._v(" (means the API itself). In other words, the plugins URLs will be "),s("code",[this._v("http://yourbackend.com/dashboard/content-manager/main.js")]),this._v(".")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"note custom-block"},[s("p",[this._v("How it is possible? The API (the Strapi server) owns the plugin and these plugins are exposed through "),s("code",[this._v("http://yourbackend.com/admin/**/main.js")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("strong",[this._v("Path —")]),this._v(" "),s("code",[this._v("./admin/admin/build/index.html")]),this._v(".")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-html extra-class"},[a("pre",{pre:!0,attrs:{class:"language-html"}},[a("code",[a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token punctuation"}},[t._v("<")]),t._v("html")]),a("span",{attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token punctuation"}},[t._v("<")]),t._v("head")]),a("span",{attrs:{class:"token punctuation"}},[t._v(">")])]),a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token punctuation"}},[t._v("<")]),t._v("body")]),a("span",{attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token punctuation"}},[t._v("<")]),t._v("div")]),t._v(" "),a("span",{attrs:{class:"token attr-name"}},[t._v("id")]),a("span",{attrs:{class:"token attr-value"}},[a("span",{attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{attrs:{class:"token punctuation"}},[t._v('"')]),t._v("app"),a("span",{attrs:{class:"token punctuation"}},[t._v('"')])]),a("span",{attrs:{class:"token punctuation"}},[t._v(">")])]),a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token punctuation"}},[t._v("<")]),t._v("script")]),t._v(" "),a("span",{attrs:{class:"token attr-name"}},[t._v("type")]),a("span",{attrs:{class:"token attr-value"}},[a("span",{attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{attrs:{class:"token punctuation"}},[t._v('"')]),t._v("text/javascript"),a("span",{attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),a("span",{attrs:{class:"token attr-name"}},[t._v("src")]),a("span",{attrs:{class:"token attr-value"}},[a("span",{attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{attrs:{class:"token punctuation"}},[t._v('"')]),t._v("/vendor.dll.js"),a("span",{attrs:{class:"token punctuation"}},[t._v('"')])]),a("span",{attrs:{class:"token punctuation"}},[t._v(">")])]),a("span",{attrs:{class:"token script language-javascript"}}),a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token punctuation"}},[t._v("<")]),t._v("script")]),t._v(" "),a("span",{attrs:{class:"token attr-name"}},[t._v("type")]),a("span",{attrs:{class:"token attr-value"}},[a("span",{attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{attrs:{class:"token punctuation"}},[t._v('"')]),t._v("text/javascript"),a("span",{attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),a("span",{attrs:{class:"token attr-name"}},[t._v("src")]),a("span",{attrs:{class:"token attr-value"}},[a("span",{attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{attrs:{class:"token punctuation"}},[t._v('"')]),t._v("/main.js"),a("span",{attrs:{class:"token punctuation"}},[t._v('"')])]),a("span",{attrs:{class:"token punctuation"}},[t._v(">")])]),a("span",{attrs:{class:"token script language-javascript"}}),a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token punctuation"}},[t._v("<")]),t._v("script")]),t._v(" "),a("span",{attrs:{class:"token attr-name"}},[t._v("src")]),a("span",{attrs:{class:"token attr-value"}},[a("span",{attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{attrs:{class:"token punctuation"}},[t._v('"')]),t._v("http://yourbackend.com/dashboard/content-manager/main.js"),a("span",{attrs:{class:"token punctuation"}},[t._v('"')])]),a("span",{attrs:{class:"token punctuation"}},[t._v(">")])]),a("span",{attrs:{class:"token script language-javascript"}}),a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token punctuation"}},[t._v("<")]),t._v("script")]),t._v(" "),a("span",{attrs:{class:"token attr-name"}},[t._v("src")]),a("span",{attrs:{class:"token attr-value"}},[a("span",{attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{attrs:{class:"token punctuation"}},[t._v('"')]),t._v("http://yourbackend.com/dashboard/settings-manager/main.js"),a("span",{attrs:{class:"token punctuation"}},[t._v('"')])]),a("span",{attrs:{class:"token punctuation"}},[t._v(">")])]),a("span",{attrs:{class:"token script language-javascript"}}),a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token punctuation"}},[t._v("<")]),t._v("script")]),t._v(" "),a("span",{attrs:{class:"token attr-name"}},[t._v("src")]),a("span",{attrs:{class:"token attr-value"}},[a("span",{attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{attrs:{class:"token punctuation"}},[t._v('"')]),t._v("http://yourbackend.com/dashboard/content-type-builder/main.js"),a("span",{attrs:{class:"token punctuation"}},[t._v('"')])]),a("span",{attrs:{class:"token punctuation"}},[t._v(">")])]),a("span",{attrs:{class:"token script language-javascript"}}),a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"note custom-block"},[s("p",[this._v("The plugins are injected using the "),s("code",[this._v("./admin/admin/build/config/plugins.json")]),this._v(". To see the plugins URLs in the "),s("code",[this._v("index.html")]),this._v(", you need to launch the administration panel in the browser.")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"deploy-the-administration-panel-and-the-plugins-on-another-server-than-the-api"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#deploy-the-administration-panel-and-the-plugins-on-another-server-than-the-api","aria-hidden":"true"}},[this._v("#")]),this._v(" Deploy the administration panel and the plugins on another server than the API")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("strong",[this._v("Path —")]),this._v(" "),s("code",[this._v("./config/environment/**/server.json")]),this._v(".")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"host"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"localhost"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"port"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("1337")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"autoReload"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"enabled"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token boolean"}},[t._v("false")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"cron"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"enabled"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token boolean"}},[t._v("false")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"admin"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"build"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"host"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"http://yourfrontend.com/dashboard"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token comment"}},[t._v("// Note: The custom path has moved directly in the host URL.")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"backend"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"http://yourbackend.com"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"plugins"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"source"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"host"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token comment"}},[t._v("// What does it means? The script tags in the index.html will use the host value to load the plugins (ex: http://yourfrontend.com/dashboard/plugins/content-manager/main.js).")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"folder"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"/plugins"')]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("The administration URL will be http://yourfrontend.com/dashboard and every request from the panel will hit the backend at http://yourbackend.com. The plugins will be injected through the "),s("code",[this._v("host")]),this._v(". It means that the plugins URLs will use the host URL as the origin. So the plugins URLs will be "),s("code",[this._v("http://yourfrontend.com/dashboard/plugins/content-manager/main.js")]),this._v(".")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("We also added a "),s("code",[this._v("folder")]),this._v(" setting to separate the plugins from the administration build. In your server, the files structure should look like this:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language- extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("- src/\n - 0bd35bad03d09ca61ac6cce225112e36.svg\n - 1d51d8767683a24635702f720cda4e26.svg\n - af3aefd0529349e40e4817c87c620836.png\n - config/\n - plugins.json\n - main.js\n - main.js.map\n - plugins/\n - content-type-builder/\n - 0bd35bad03d09ca61ac6cce225112e36.svg\n - 1d51d8767683a24635702f720cda4e26.svg\n - af3aefd0529349e40e4817c87c620836.png\n - main.js\n - main.js.map\n - content-manager/\n - ...\n - main.js\n - main.js.map\n - settings-manager/\n - ...\n - main.js\n - main.js.map\n - vendor.dll.js\n - vendor.dll.js.map\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("The generated "),s("code",[this._v("index.html")]),this._v(" will look like this:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("strong",[this._v("Path —")]),this._v(" "),s("code",[this._v("./admin/admin/build/index.html")]),this._v(".")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-html extra-class"},[a("pre",{pre:!0,attrs:{class:"language-html"}},[a("code",[a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token punctuation"}},[t._v("<")]),t._v("html")]),a("span",{attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token punctuation"}},[t._v("<")]),t._v("head")]),a("span",{attrs:{class:"token punctuation"}},[t._v(">")])]),a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token punctuation"}},[t._v("<")]),t._v("body")]),a("span",{attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token punctuation"}},[t._v("<")]),t._v("div")]),t._v(" "),a("span",{attrs:{class:"token attr-name"}},[t._v("id")]),a("span",{attrs:{class:"token attr-value"}},[a("span",{attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{attrs:{class:"token punctuation"}},[t._v('"')]),t._v("app"),a("span",{attrs:{class:"token punctuation"}},[t._v('"')])]),a("span",{attrs:{class:"token punctuation"}},[t._v(">")])]),a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token punctuation"}},[t._v("<")]),t._v("script")]),t._v(" "),a("span",{attrs:{class:"token attr-name"}},[t._v("type")]),a("span",{attrs:{class:"token attr-value"}},[a("span",{attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{attrs:{class:"token punctuation"}},[t._v('"')]),t._v("text/javascript"),a("span",{attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),a("span",{attrs:{class:"token attr-name"}},[t._v("src")]),a("span",{attrs:{class:"token attr-value"}},[a("span",{attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{attrs:{class:"token punctuation"}},[t._v('"')]),t._v("/dashboard/vendor.dll.js"),a("span",{attrs:{class:"token punctuation"}},[t._v('"')])]),a("span",{attrs:{class:"token punctuation"}},[t._v(">")])]),a("span",{attrs:{class:"token script language-javascript"}}),a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token punctuation"}},[t._v("<")]),t._v("script")]),t._v(" "),a("span",{attrs:{class:"token attr-name"}},[t._v("type")]),a("span",{attrs:{class:"token attr-value"}},[a("span",{attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{attrs:{class:"token punctuation"}},[t._v('"')]),t._v("text/javascript"),a("span",{attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),a("span",{attrs:{class:"token attr-name"}},[t._v("src")]),a("span",{attrs:{class:"token attr-value"}},[a("span",{attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{attrs:{class:"token punctuation"}},[t._v('"')]),t._v("/dashboard/main.js"),a("span",{attrs:{class:"token punctuation"}},[t._v('"')])]),a("span",{attrs:{class:"token punctuation"}},[t._v(">")])]),a("span",{attrs:{class:"token script language-javascript"}}),a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token punctuation"}},[t._v("<")]),t._v("script")]),t._v(" "),a("span",{attrs:{class:"token attr-name"}},[t._v("src")]),a("span",{attrs:{class:"token attr-value"}},[a("span",{attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{attrs:{class:"token punctuation"}},[t._v('"')]),t._v("/dashboard/plugins/content-manager/main.js"),a("span",{attrs:{class:"token punctuation"}},[t._v('"')])]),a("span",{attrs:{class:"token punctuation"}},[t._v(">")])]),a("span",{attrs:{class:"token script language-javascript"}}),a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token punctuation"}},[t._v("<")]),t._v("script")]),t._v(" "),a("span",{attrs:{class:"token attr-name"}},[t._v("src")]),a("span",{attrs:{class:"token attr-value"}},[a("span",{attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{attrs:{class:"token punctuation"}},[t._v('"')]),t._v("/dashboard/plugins/settings-manager/main.js"),a("span",{attrs:{class:"token punctuation"}},[t._v('"')])]),a("span",{attrs:{class:"token punctuation"}},[t._v(">")])]),a("span",{attrs:{class:"token script language-javascript"}}),a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token punctuation"}},[t._v("<")]),t._v("script")]),t._v(" "),a("span",{attrs:{class:"token attr-name"}},[t._v("src")]),a("span",{attrs:{class:"token attr-value"}},[a("span",{attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{attrs:{class:"token punctuation"}},[t._v('"')]),t._v("/dashboard/plugins/content-type-builder/main.js"),a("span",{attrs:{class:"token punctuation"}},[t._v('"')])]),a("span",{attrs:{class:"token punctuation"}},[t._v(">")])]),a("span",{attrs:{class:"token script language-javascript"}}),a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token tag"}},[a("span",{attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"note custom-block"},[s("p",[this._v("The plugins are injected using the "),s("code",[this._v("./admin/admin/build/config/plugins.json")]),this._v(". To see the plugins URLs in the "),s("code",[this._v("index.html")]),this._v(", you need to launch the administration panel in the browser.")])])}],!1,null,null,null);e.options.__file="customize-admin.md";s.default=e.exports}}]); \ No newline at end of file diff --git a/docs/.vuepress/dist/assets/js/3.4d92d5e3.js b/docs/.vuepress/dist/assets/js/3.4d92d5e3.js new file mode 100644 index 0000000000..2d847c73db --- /dev/null +++ b/docs/.vuepress/dist/assets/js/3.4d92d5e3.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[3],{236:function(t,e,a){"use strict";a.r(e);var s=a(0),r=Object(s.a)({},function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"content"},[a("div",{staticClass:"intro custom-block"},[t._m(0),t._v(" "),a("p",[a("a",{attrs:{href:"https://travis-ci.org/wistityhq/strapi",target:"_blank",rel:"noopener noreferrer"}},[a("img",{attrs:{src:"https://travis-ci.org/wistityhq/strapi.svg?branch=master",alt:"Build Status"}}),a("OutboundLink")],1),t._v(" "),a("a",{attrs:{href:"http://slack.strapi.io",target:"_blank",rel:"noopener noreferrer"}},[a("img",{attrs:{src:"http://strapi-slack.herokuapp.com/badge.svg",alt:"Slack Status"}}),a("OutboundLink")],1)]),t._v(" "),a("p",[t._v("Strapi is an open-source Node.js rich framework for building applications and services.")])]),t._v(" "),a("p",[t._v("Strapi enables developers to focus on writing reusable application logic instead of spending time\nbuilding infrastructure. It is designed for building practical, production-ready Node.js applications\nin a matter of hours instead of weeks.")]),t._v(" "),a("p",[t._v("The framework sits on top of "),a("a",{attrs:{href:"http://koajs.com/",target:"_blank",rel:"noopener noreferrer"}},[t._v("Koa"),a("OutboundLink")],1),t._v(". Its ensemble of small modules work\ntogether to provide simplicity, maintainability, and structural conventions to Node.js applications.")]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),t._m(3),t._v(" "),a("p",[t._v("Install the latest stable release with the npm command-line tool:")]),t._v(" "),t._m(4),t._m(5),t._v(" "),a("blockquote",[a("p",[t._v("We advise you to use our Studio to build APIs. To do so, you need to create a Strapi account.\n"),a("a",{attrs:{href:"http://studio.strapi.io",target:"_blank",rel:"noopener noreferrer"}},[t._v("Go to the Strapi Studio to signup"),a("OutboundLink")],1),t._v(".\nStudio is dedicated to developers to build applications without writing\nany single line of code thanks to its powerful set of tools.")])]),t._v(" "),a("p",[t._v("After creating an account on the Strapi Studio, you are able to link your machine to your\nStrapi Studio account to get access to all features offered by the Strapi ecosystem.\nUse your Strapi account credentials.")]),t._v(" "),t._m(6),t._m(7),t._v(" "),a("p",[t._v("You now are able to use the Strapi CLI. Simply create your first application and start the server:")]),t._v(" "),t._m(8),t._m(9),t._v(" "),t._m(10),a("p",[t._v("This will generate a Strapi application without:")]),t._v(" "),t._m(11),t._v(" "),a("p",[t._v("This feature allows you to only use Strapi for your HTTP server structure if you want to.")]),t._v(" "),t._m(12),t._v(" "),t._m(13),a("p",[t._v("The default home page is accessible at "),a("a",{attrs:{href:"http://localhost:1337/",target:"_blank",rel:"noopener noreferrer"}},[t._v("http://localhost:1337/"),a("OutboundLink")],1),t._v(".")]),t._v(" "),t._m(14),t._v(" "),a("p",[t._v("The Strapi ecosystem offers you two possibilities to create a complete RESTful API.")]),t._v(" "),t._m(15),t._v(" "),t._m(16),t._m(17),t._v(" "),t._m(18),t._m(19),t._v(" "),a("p",[t._v("The Strapi Studio allows you to easily build and manage your application environment\nthanks to a powerful User Interface.")]),t._v(" "),a("p",[t._v("Log into the Strapi Studio with your user account ("),a("a",{attrs:{href:"http://studio.strapi.io",target:"_blank",rel:"noopener noreferrer"}},[t._v("http://studio.strapi.io"),a("OutboundLink")],1),t._v(")\nand follow the instructions to start the experience.")]),t._v(" "),t._m(20),t._v(" "),t._m(21),t._v(" "),a("p",[t._v("Strapi comes with a simple but yet powerful dashboard.")]),t._v(" "),t._m(22),t._v(" "),t._m(23),t._v(" "),t._m(24),t._v(" "),t._m(25),t._v(" "),t._m(26),t._v(" "),a("ul",[a("li",[a("a",{attrs:{href:"https://strapi.io/",target:"_blank",rel:"noopener noreferrer"}},[t._v("Strapi website"),a("OutboundLink")],1)]),t._v(" "),a("li",[a("a",{attrs:{href:"https://twitter.com/strapijs",target:"_blank",rel:"noopener noreferrer"}},[t._v("Strapi news on Twitter"),a("OutboundLink")],1)])])])},[function(){var t=this.$createElement,e=this._self._c||t;return e("h1",{attrs:{id:"strapi"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#strapi","aria-hidden":"true"}},[this._v("#")]),this._v(" Strapi")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("strong",[this._v("DISCLAIMER")]),this._v(": "),e("em",[this._v("This version is maintained for criticals issues only")]),this._v(".")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"getting-started-in-a-minute"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#getting-started-in-a-minute","aria-hidden":"true"}},[this._v("#")]),this._v(" Getting started in a minute")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"installation"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#installation","aria-hidden":"true"}},[this._v("#")]),this._v(" Installation")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[this._v("$ "),e("span",{attrs:{class:"token function"}},[this._v("npm")]),this._v(" "),e("span",{attrs:{class:"token function"}},[this._v("install")]),this._v(" strapi -g\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"link-to-the-strapi-studio"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#link-to-the-strapi-studio","aria-hidden":"true"}},[this._v("#")]),this._v(" Link to the Strapi Studio")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[this._v("$ strapi login\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"create-your-first-project"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#create-your-first-project","aria-hidden":"true"}},[this._v("#")]),this._v(" Create your first project")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[this._v("$ strapi new "),e("span",{attrs:{class:"token operator"}},[this._v("<")]),this._v("appName"),e("span",{attrs:{class:"token operator"}},[this._v(">")]),this._v("\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("Note that you can generate a dry application using the "),e("code",[this._v("dry")]),this._v(" option:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[this._v("$ strapi new "),e("span",{attrs:{class:"token operator"}},[this._v("<")]),this._v("appName"),e("span",{attrs:{class:"token operator"}},[this._v(">")]),this._v(" --dry\n")])])])},function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("ul",[a("li",[t._v("the built-in "),a("code",[t._v("user")]),t._v(", "),a("code",[t._v("email")]),t._v(" and "),a("code",[t._v("upload")]),t._v(" APIs,")]),t._v(" "),a("li",[t._v("the "),a("code",[t._v("grant")]),t._v(" hook,")]),t._v(" "),a("li",[t._v("the open-source admin panel,")]),t._v(" "),a("li",[t._v("the Waterline ORM ("),a("code",[t._v("waterline")]),t._v(" and "),a("code",[t._v("blueprints")]),t._v(" hooks disabled),")]),t._v(" "),a("li",[t._v("the Strapi Studio connection ("),a("code",[t._v("studio")]),t._v(" hook disabled).")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"start-your-application"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#start-your-application","aria-hidden":"true"}},[this._v("#")]),this._v(" Start your application")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[this._v("$ "),e("span",{attrs:{class:"token function"}},[this._v("cd")]),this._v(" "),e("span",{attrs:{class:"token operator"}},[this._v("<")]),this._v("appName"),e("span",{attrs:{class:"token operator"}},[this._v(">")]),this._v("\n$ strapi start\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"create-your-first-api"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#create-your-first-api","aria-hidden":"true"}},[this._v("#")]),this._v(" Create your first API")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h4",{attrs:{id:"via-the-cli"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#via-the-cli","aria-hidden":"true"}},[this._v("#")]),this._v(" Via the CLI")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[this._v("$ strapi generate api "),e("span",{attrs:{class:"token operator"}},[this._v("<")]),this._v("apiName"),e("span",{attrs:{class:"token operator"}},[this._v(">")]),this._v("\n")])])])},function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("p",[t._v("For example, you can create a "),a("code",[t._v("car")]),t._v(" API with a name ("),a("code",[t._v("name")]),t._v("), year ("),a("code",[t._v("year")]),t._v(") and\nthe license plate ("),a("code",[t._v("license")]),t._v(") with:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[this._v("$ strapi generate api car name:string year:integer license:string\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h4",{attrs:{id:"via-the-strapi-studio"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#via-the-strapi-studio","aria-hidden":"true"}},[this._v("#")]),this._v(" Via the Strapi Studio")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("img",{attrs:{src:"http://strapi.io/assets/screenshots/studio.png",alt:"Strapi Studio",title:"Strapi Studio"}}),this._v(" "),e("em",[this._v("Simply manage your APIs and relations thanks to the Strapi Studio.")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"manage-your-data"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#manage-your-data","aria-hidden":"true"}},[this._v("#")]),this._v(" Manage your data")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("img",{attrs:{src:"http://strapi.io/assets/screenshots/create.png",alt:"Strapi Dashboard",title:"Strapi Dashboard"}}),this._v(" "),e("em",[this._v("Create, read, update and delete your data.")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("img",{attrs:{src:"http://strapi.io/assets/screenshots/permissions.png",alt:"Strapi Dashboard",title:"Strapi Dashboard"}}),this._v(" "),e("em",[this._v("Manage user settings, login, registration, groups and permissions on the fly.")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"resources"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#resources","aria-hidden":"true"}},[this._v("#")]),this._v(" Resources")])},function(){var t=this.$createElement,e=this._self._c||t;return e("ul",[e("li",[e("a",{attrs:{href:"(https://github.com/strapi/strapi/blob/master/CONTRIBUTING.md)"}},[this._v("Contributing guide")])]),this._v(" "),e("li",[e("a",{attrs:{href:"(https://github.com/strapi/strapi/blob/master/LICENSE.md)"}},[this._v("MIT License")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"links"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#links","aria-hidden":"true"}},[this._v("#")]),this._v(" Links")])}],!1,null,null,null);r.options.__file="README.md";e.default=r.exports}}]); \ No newline at end of file diff --git a/docs/.vuepress/dist/assets/js/30.5d2829b8.js b/docs/.vuepress/dist/assets/js/30.5d2829b8.js new file mode 100644 index 0000000000..17c4a7c341 --- /dev/null +++ b/docs/.vuepress/dist/assets/js/30.5d2829b8.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[30],{244:function(t,s,a){"use strict";a.r(s);var n=a(0),o=Object(n.a)({},function(){this.$createElement;this._self._c;return this._m(0)},[function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"content"},[a("h1",{attrs:{id:"hooks"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#hooks","aria-hidden":"true"}},[t._v("#")]),t._v(" Hooks")]),t._v(" "),a("p",[t._v("The hooks are modules that add functionality to the core. They are loaded during the server boot. For example, if your project needs to work with a SQL database, your will have to add the hook "),a("code",[t._v("strapi-hook-bookshelf")]),t._v(" to be able to connect your app with your database.")]),t._v(" "),a("p",[a("strong",[t._v("Path —")]),t._v(" "),a("code",[t._v("./hooks/documentation/lib/index.js")]),t._v(".")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("const")]),t._v(" fs "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("require")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'fs'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("const")]),t._v(" path "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("require")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'path'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\nmodule"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function-variable function"}},[t._v("exports")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" strapi "),a("span",{attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("const")]),t._v(" hook "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n "),a("span",{attrs:{class:"token comment"}},[t._v("/**\n * Default options\n */")]),t._v("\n\n defaults"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n documentation"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n path"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'/public/documentation'")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\n "),a("span",{attrs:{class:"token comment"}},[t._v("/**\n * Initialize the hook\n */")]),t._v("\n\n initialize"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" cb "),a("span",{attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("try")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token comment"}},[t._v("// Check if documentation folder exist.")]),t._v("\n fs"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("accessSync")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("path"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("resolve")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("process"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("cwd")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("defaults"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("documentation"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("path"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("catch")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token class-name"}},[t._v("e")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token comment"}},[t._v("// Otherwise, create the folder.")]),t._v("\n fs"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("mkdirSync")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("path"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("resolve")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("process"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("cwd")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("defaults"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("documentation"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("path"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),a("span",{attrs:{class:"token comment"}},[t._v("// This function doesn't really exist,")]),t._v("\n "),a("span",{attrs:{class:"token comment"}},[t._v("// it's just an example to tell you that you")]),t._v("\n "),a("span",{attrs:{class:"token comment"}},[t._v("// run your business logic and when it's done")]),t._v("\n "),a("span",{attrs:{class:"token comment"}},[t._v("// you just need to call the callback `cb`")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("generateDocumentation")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("path"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("resolve")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("process"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("cwd")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("this")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("defaults"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("documentation"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("path"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("function")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token comment"}},[t._v("// Error: it will display the error to the user")]),t._v("\n "),a("span",{attrs:{class:"token comment"}},[t._v("// and the hook won't be loaded.")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("cb")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),a("span",{attrs:{class:"token comment"}},[t._v("// Success.")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("cb")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token keyword"}},[t._v("return")]),t._v(" hook"),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),a("ul",[a("li",[a("code",[t._v("defaults")]),t._v(" (object): Contains the defaults configurations. This object is merged to "),a("code",[t._v("strapi.config.hook.settings.**")]),t._v(".")]),t._v(" "),a("li",[a("code",[t._v("initialize")]),t._v(" (function): Called during the server boot. The callback "),a("code",[t._v("cb")]),t._v(" needs to be called. Otherwise, the hook won't be loaded.")])]),t._v(" "),a("p",[t._v("Every folder that follows this name pattern "),a("code",[t._v("strapi-*")]),t._v(" in your "),a("code",[t._v("./node_modules")]),t._v(" folder will be loaded as a hook. The hooks are accessible through the "),a("code",[t._v("strapi.hook")]),t._v(" variable.")]),t._v(" "),a("h2",{attrs:{id:"structure"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#structure","aria-hidden":"true"}},[t._v("#")]),t._v(" Structure")]),t._v(" "),a("p",[t._v("A hook needs to follow the structure below:")]),t._v(" "),a("div",{staticClass:"language- extra-class"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[t._v("/hook\n└─── lib\n - index.js\n- LICENSE.md\n- package.json\n- README.md\n")])])]),a("p",[t._v("The "),a("code",[t._v("index.js")]),t._v(" is the entry point to your hook. It should look like the example above.")]),t._v(" "),a("h2",{attrs:{id:"dependencies"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#dependencies","aria-hidden":"true"}},[t._v("#")]),t._v(" Dependencies")]),t._v(" "),a("p",[t._v("It happens that a hook has a dependency to another one. For example, the "),a("code",[t._v("strapi-hook-bookshelf")]),t._v(" has a dependency to "),a("code",[t._v("strapi-hook-knex")]),t._v(". Without it, the "),a("code",[t._v("strapi-hook-bookshelf")]),t._v(" can't work correctly. It also means that the "),a("code",[t._v("strapi-hook-knex")]),t._v(" hook has to be loaded before.")]),t._v(" "),a("p",[t._v("To handle this case, you need to update the "),a("code",[t._v("package.json")]),t._v(" at the root of your hook.")]),t._v(" "),a("div",{staticClass:"language-json extra-class"},[a("pre",{pre:!0,attrs:{class:"language-json"}},[a("code",[a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token property"}},[t._v('"name"')]),a("span",{attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"strapi-hook-bookshelf"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token property"}},[t._v('"version"')]),a("span",{attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"x.x.x"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token property"}},[t._v('"description"')]),a("span",{attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"Bookshelf hook for the Strapi framework"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token property"}},[t._v('"dependencies"')]),a("span",{attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n ...\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token property"}},[t._v('"strapi"')]),a("span",{attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token property"}},[t._v('"dependencies"')]),a("span",{attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"strapi-hook-knex"')]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("h2",{attrs:{id:"custom-hooks"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#custom-hooks","aria-hidden":"true"}},[t._v("#")]),t._v(" Custom hooks")]),t._v(" "),a("p",[t._v("The framework allows to load hooks from the project directly without having to install them from npm. It's great way to take advantage of the features of the hooks system for code that doesn't need to be shared between apps. To achieve this, you have to create a "),a("code",[t._v("./hooks")]),t._v(" folder at the root of your project and put the hooks into it.")]),t._v(" "),a("div",{staticClass:"language- extra-class"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[t._v("/project\n└─── admin\n└─── api\n└─── config\n└─── hooks\n│ └─── strapi-documentation\n│ - index.js\n│ └─── strapi-server-side-rendering\n│ - index.js\n└─── plugins\n└─── public\n- favicon.ico\n- package.json\n- server.js\n")])])])])}],!1,null,null,null);o.options.__file="hooks.md";s.default=o.exports}}]); \ No newline at end of file diff --git a/docs/.vuepress/dist/assets/js/31.fad00a3a.js b/docs/.vuepress/dist/assets/js/31.fad00a3a.js new file mode 100644 index 0000000000..c281480e56 --- /dev/null +++ b/docs/.vuepress/dist/assets/js/31.fad00a3a.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[31],{208:function(t,s,n){"use strict";n.r(s);var a=n(0),e=Object(a.a)({},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"content"},[t._m(0),t._v(" "),t._m(1),t._v(" "),t._m(2),t._m(3),t._v(" "),n("p",[t._v("The global logger is configured by environment variables.")]),t._v(" "),t._m(4),t._v(" "),t._m(5),t._v(" "),t._m(6),t._v(" "),t._m(7),t._m(8),t._v(" "),n("p",[t._v("To find more details about the logger API, please refer to the "),n("a",{attrs:{href:"http://getpino.io/#/",target:"_blank",rel:"noopener noreferrer"}},[t._v("Pino documentation"),n("OutboundLink")],1),t._v(".")])])},[function(){var t=this.$createElement,s=this._self._c||t;return s("h1",{attrs:{id:"logging"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#logging","aria-hidden":"true"}},[this._v("#")]),this._v(" Logging")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Strapi relies on an extremely fast Node.js logger called Pino that includes a shell utility to pretty-print its log files. It provides great performances and doesn't slow down your app. The logger is accessible through the global variable "),s("code",[this._v("strapi.log")]),this._v(" or the request's context "),s("code",[this._v("ctx.log")]),this._v(" if enabled.")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-js extra-class"},[n("pre",{pre:!0,attrs:{class:"language-js"}},[n("code",[n("span",{attrs:{class:"token comment"}},[t._v("// Folder.js controller")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("const")]),t._v(" fs "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("require")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v("'fs'")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("const")]),t._v(" path "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("require")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v("'path'")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\nmodule"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("exports "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n "),n("span",{attrs:{class:"token comment"}},[t._v("/**\n * Retrieve app's folders.\n *\n * @return {Object|Array}\n */")]),t._v("\n\n findFolders"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("async")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ctx"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("try")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("const")]),t._v(" folders "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" fs"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("readdirSync")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("path"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("resolve")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("process"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("cwd")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n strapi"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("log"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("info")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("folders"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),n("span",{attrs:{class:"token comment"}},[t._v("// ctx.log.info(folders);")]),t._v("\n\n ctx"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("send")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("folders"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("catch")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token class-name"}},[t._v("error")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n strapi"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("log"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("fatal")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("error"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),n("span",{attrs:{class:"token comment"}},[t._v("// ctx.log.fatal(error);")]),t._v("\n ctx"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("badImplementation")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("error"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("message"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"global-logger-configuration"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#global-logger-configuration","aria-hidden":"true"}},[this._v("#")]),this._v(" Global logger configuration")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[n("code",[t._v("STRAPI_LOG_LEVEL")]),t._v(": Can be 'fatal', 'error', 'warn', 'info', 'debug' or 'trace'.\n"),n("code",[t._v("STRAPI_LOG_TIMESTAMP")]),t._v(": Can be true/false\n"),n("code",[t._v("STRAPI_LOG_PRETTY_PRINT")]),t._v(": Can be true/false\n"),n("code",[t._v("STRAPI_LOG_FORCE_COLOR")]),t._v(": Can be true/false")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"request-logging-middleware"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#request-logging-middleware","aria-hidden":"true"}},[this._v("#")]),this._v(" Request logging middleware")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("To configure the request-logger middleware, you have to edit the following file "),s("code",[this._v("./config/environments/*/request.json")]),this._v(".")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-json extra-class"},[n("pre",{pre:!0,attrs:{class:"language-json"}},[n("code",[n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n ...\n "),n("span",{attrs:{class:"token property"}},[t._v('"logger"')]),n("span",{attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token property"}},[t._v('"level"')]),n("span",{attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token string"}},[t._v('"debug"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token property"}},[t._v('"exposeInContext"')]),n("span",{attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token boolean"}},[t._v("true")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token property"}},[t._v('"requests"')]),n("span",{attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token boolean"}},[t._v("true")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n ...\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("ul",[n("li",[n("code",[t._v("level")]),t._v(": defines the desired logging level (fatal, error, warn, info, debug, trace).")]),t._v(" "),n("li",[n("code",[t._v("exposeInContext")]),t._v(": allows access to the logger through the context.")]),t._v(" "),n("li",[n("code",[t._v("requests")]),t._v(": incoming HTTP requests will be logged.")])])}],!1,null,null,null);e.options.__file="logging.md";s.default=e.exports}}]); \ No newline at end of file diff --git a/docs/.vuepress/dist/assets/js/32.a6900221.js b/docs/.vuepress/dist/assets/js/32.a6900221.js new file mode 100644 index 0000000000..8297923103 --- /dev/null +++ b/docs/.vuepress/dist/assets/js/32.a6900221.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[32],{207:function(t,s,a){"use strict";a.r(s);var n=a(0),e=Object(n.a)({},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"content"},[t._m(0),t._v(" "),a("p",[t._v("The middlewares are functions which are composed and executed in a stack-like manner upon request. If you are not familiar with the middleware stack in Koa, we highly recommend you to read the "),a("a",{attrs:{href:"http://koajs.com/#introduction",target:"_blank",rel:"noopener noreferrer"}},[t._v("Koa's documentation introduction"),a("OutboundLink")],1),t._v(".")]),t._v(" "),a("p",[t._v("Enable the middleware in environments settings")]),t._v(" "),t._m(1),t._v(" "),t._m(2),a("p",[a("strong",[t._v("Path —")]),t._v(" "),a("a",{attrs:{href:"https://github.com/strapi/strapi/blob/master/packages/strapi/lib/middlewares/responseTime/index.js",target:"_blank",rel:"noopener noreferrer"}},[a("code",[t._v("strapi/lib/middlewares/responseTime/index.js")]),a("OutboundLink")],1),t._v(".")]),t._v(" "),t._m(3),t._m(4),t._v(" "),a("p",[t._v("The core of Strapi embraces a small list of middlewares for performances, security and great error handling.")]),t._v(" "),t._m(5),t._v(" "),t._m(6),t._v(" "),t._m(7),t._v(" "),a("p",[t._v("A middleware needs to follow the structure below:")]),t._v(" "),t._m(8),t._m(9),t._v(" "),t._m(10),t._v(" "),t._m(11),t._v(" "),t._m(12),t._m(13),t._v(" "),t._m(14),t._v(" "),t._m(15),t._v(" "),t._m(16),t._v(" "),t._m(17),t._m(18),t._v(" "),t._m(19),t._v(" "),t._m(20),t._v(" "),t._m(21),t._v(" "),t._m(22),t._m(23),t._v(" "),t._m(24),t._v(" "),t._m(25),t._v(" "),t._m(26),t._m(27),t._v(" "),t._m(28),t._v(" "),t._m(29),t._v(" "),t._m(30),t._m(31),t._v(" "),t._m(32),t._v(" "),a("p",[t._v("For this example, we are going to imagine that we have 10 middlewares to load:")]),t._v(" "),t._m(33),t._v(" "),t._m(34),t._v(" "),t._m(35),a("p",[t._v("Here is the loader order:")]),t._v(" "),t._m(36)])},[function(){var t=this.$createElement,s=this._self._c||t;return s("h1",{attrs:{id:"middlewares"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#middlewares","aria-hidden":"true"}},[this._v("#")]),this._v(" Middlewares")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("strong",[this._v("Path —")]),this._v(" ["),s("code",[this._v("config/environments/**")]),this._v("]")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-json extra-class"},[a("pre",{pre:!0,attrs:{class:"language-json"}},[a("code",[t._v(" "),a("span",{attrs:{class:"token property"}},[t._v('"urlReader"')]),a("span",{attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token property"}},[t._v('"enabled"')]),a("span",{attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token boolean"}},[t._v("true")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[t._v("module"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function-variable function"}},[t._v("exports")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" strapi "),a("span",{attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n initialize"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("function")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("cb"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n strapi"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("app"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("use")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token keyword"}},[t._v("async")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ctx"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" next"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("const")]),t._v(" start "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Date"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("now")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token keyword"}},[t._v("await")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("next")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token keyword"}},[t._v("const")]),t._v(" delta "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Math"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("ceil")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Date"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("now")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("-")]),t._v(" start"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token comment"}},[t._v("// Set X-Response-Time header")]),t._v("\n ctx"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token keyword"}},[t._v("set")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v("'X-Response-Time'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" delta "),a("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v("'ms'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token function"}},[t._v("cb")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("ul",[s("li",[s("code",[this._v("initialize")]),this._v(" (function): Called during the server boot. The callback "),s("code",[this._v("cb")]),this._v(" needs to be called. Otherwise, the middleware won't be loaded into the stack.")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("ul",[a("li",[t._v("boom")]),t._v(" "),a("li",[t._v("cors")]),t._v(" "),a("li",[t._v("cron")]),t._v(" "),a("li",[t._v("csp")]),t._v(" "),a("li",[t._v("csrf")]),t._v(" "),a("li",[t._v("favicon")]),t._v(" "),a("li",[t._v("gzip")]),t._v(" "),a("li",[t._v("hsts")]),t._v(" "),a("li",[t._v("ip")]),t._v(" "),a("li",[t._v("language")]),t._v(" "),a("li",[t._v("logger")]),t._v(" "),a("li",[t._v("p3p")]),t._v(" "),a("li",[t._v("parser")]),t._v(" "),a("li",[t._v("public")]),t._v(" "),a("li",[t._v("responses")]),t._v(" "),a("li",[t._v("responseTime")]),t._v(" "),a("li",[t._v("router")]),t._v(" "),a("li",[t._v("session")]),t._v(" "),a("li",[t._v("xframe")]),t._v(" "),a("li",[t._v("xss")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"note custom-block"},[s("p",[this._v("The following middlewares cannot be disabled: responses, router, logger and boom.")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"structure"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#structure","aria-hidden":"true"}},[this._v("#")]),this._v(" Structure")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language- extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("/middleware\n└─── lib\n - index.js\n- LICENSE.md\n- package.json\n- README.md\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("The "),s("code",[this._v("index.js")]),this._v(" is the entry point to your middleware. It should look like the example above.")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"custom-middlewares"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#custom-middlewares","aria-hidden":"true"}},[this._v("#")]),this._v(" Custom middlewares")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("The framework allows the application to override the default middlewares and add new ones. You have to create a "),s("code",[this._v("./middlewares")]),this._v(" folder at the root of your project and put the middlewares into it.")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language- extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("/project\n└─── admin\n└─── api\n└─── config\n└─── middlewares\n│ └─── responseTime // It will override the core default responseTime middleware\n│ - index.js\n│ └─── views // It will be added into the stack of middleware\n│ - index.js\n└─── plugins\n└─── public\n- favicon.ico\n- package.json\n- server.js\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Every middleware will be injected into the Koa stack. To manage the load order, please refer to the "),s("a",{attrs:{href:"#load-order"}},[this._v("Middleware order section")]),this._v(".")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"load-order"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#load-order","aria-hidden":"true"}},[this._v("#")]),this._v(" Load order")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("The middlewares are injected into the Koa stack asynchronously. Sometimes it happens that some of these middlewares need to be loaded in a specific order. To define a load order, we created a dedicated file located in "),s("code",[this._v("./config/middleware.json")]),this._v(".")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("strong",[this._v("Path —")]),this._v(" "),s("code",[this._v("./config/middleware.json")]),this._v(".")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-json extra-class"},[a("pre",{pre:!0,attrs:{class:"language-json"}},[a("code",[a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token property"}},[t._v('"timeout"')]),a("span",{attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("100")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token property"}},[t._v('"load"')]),a("span",{attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token property"}},[t._v('"before"')]),a("span",{attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"responseTime"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"logger"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"cors"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"responses"')]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token property"}},[t._v('"order"')]),a("span",{attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"Define the middlewares\' load order by putting their name in this array in the right order"')]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token property"}},[t._v('"after"')]),a("span",{attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"parser"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"router"')]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("ul",[a("li",[a("code",[t._v("timeout")]),t._v(": defines the maximum allowed milliseconds to load a middleware.")]),t._v(" "),a("li",[a("code",[t._v("load")]),t._v(":\n"),a("ul",[a("li",[a("code",[t._v("before")]),t._v(": array of middlewares that need to be loaded in the first place. The order of this array matters.")]),t._v(" "),a("li",[a("code",[t._v("order")]),t._v(": array of middlewares that need to be loaded in a specific order.")]),t._v(" "),a("li",[a("code",[t._v("after")]),t._v(": array of middlewares that need to be loaded at the end of the stack. The order of this array matters.")])])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"examples"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#examples","aria-hidden":"true"}},[this._v("#")]),this._v(" Examples")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("strong",[this._v("Load a middleware at the very first place")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("strong",[this._v("Path —")]),this._v(" "),s("code",[this._v("./config/middleware.json")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-json extra-class"},[a("pre",{pre:!0,attrs:{class:"language-json"}},[a("code",[t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token property"}},[t._v('"timeout"')]),a("span",{attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("100")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token property"}},[t._v('"load"')]),a("span",{attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token property"}},[t._v('"before"')]),a("span",{attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"responseTime"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"logger"')]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token property"}},[t._v('"order"')]),a("span",{attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token property"}},[t._v('"after"')]),a("span",{attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("The "),s("code",[this._v("responseTime")]),this._v(" middleware will be loaded first. Immediately followed by the "),s("code",[this._v("logger")]),this._v(" middleware. Then, the others middlewares will be loaded asynchronously.")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("strong",[this._v("Load a middleware after another one")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("strong",[this._v("Path —")]),this._v(" "),s("code",[this._v("./config/middleware.json")]),this._v(".")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-json extra-class"},[a("pre",{pre:!0,attrs:{class:"language-json"}},[a("code",[t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token property"}},[t._v('"timeout"')]),a("span",{attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("100")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token property"}},[t._v('"load"')]),a("span",{attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token property"}},[t._v('"before"')]),a("span",{attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token property"}},[t._v('"order"')]),a("span",{attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"p3p"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"gzip"')]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token property"}},[t._v('"after"')]),a("span",{attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("The "),s("code",[this._v("gzip")]),this._v(" middleware will be loaded after the "),s("code",[this._v("p3p")]),this._v(" middleware. All the others will be loaded asynchronously.")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("strong",[this._v("Load a middleware at the very end")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("strong",[this._v("Path —")]),this._v(" "),s("code",[this._v("./config/middleware.json")]),this._v(".")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-json extra-class"},[a("pre",{pre:!0,attrs:{class:"language-json"}},[a("code",[t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token property"}},[t._v('"timeout"')]),a("span",{attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("100")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token property"}},[t._v('"load"')]),a("span",{attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token property"}},[t._v('"before"')]),a("span",{attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),t._v("\n ...\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token property"}},[t._v('"order"')]),a("span",{attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token property"}},[t._v('"after"')]),a("span",{attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"parser"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"router"')]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("The "),s("code",[this._v("router")]),this._v(" middleware will be loaded at the very end. The "),s("code",[this._v("parser")]),this._v(" middleware will be loaded after all the others and just before the "),s("code",[this._v("router")]),this._v(" middleware.")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("strong",[this._v("Complete example")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("ul",[a("li",[t._v("cors")]),t._v(" "),a("li",[t._v("cron")]),t._v(" "),a("li",[t._v("favicon")]),t._v(" "),a("li",[t._v("gzip")]),t._v(" "),a("li",[t._v("logger")]),t._v(" "),a("li",[t._v("p3p")]),t._v(" "),a("li",[t._v("parser")]),t._v(" "),a("li",[t._v("response")]),t._v(" "),a("li",[t._v("responseTime")]),t._v(" "),a("li",[t._v("router")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("We assume that we set the "),s("code",[this._v("./config/middleware.json")]),this._v(" file like this:")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-json extra-class"},[a("pre",{pre:!0,attrs:{class:"language-json"}},[a("code",[t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token property"}},[t._v('"timeout"')]),a("span",{attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("100")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token property"}},[t._v('"load"')]),a("span",{attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token property"}},[t._v('"before"')]),a("span",{attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"responseTime"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"logger"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"cors"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token property"}},[t._v('"order"')]),a("span",{attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"p3p"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"gzip"')]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token property"}},[t._v('"after"')]),a("span",{attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"parser"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"router"')]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("ol",[a("li",[t._v("responseTime (loaded at the very first place)")]),t._v(" "),a("li",[t._v("logger")]),t._v(" "),a("li",[t._v("cors")]),t._v(" "),a("li",[t._v("favicon (position order not guarantee)")]),t._v(" "),a("li",[t._v("p3p")]),t._v(" "),a("li",[t._v("cron")]),t._v(" "),a("li",[t._v("gzip (loaded after the p3p middlewares)")]),t._v(" "),a("li",[t._v("response (position order not guarantee)")]),t._v(" "),a("li",[t._v("parser")]),t._v(" "),a("li",[t._v("router (loaded at the very end place)")])])}],!1,null,null,null);e.options.__file="middlewares.md";s.default=e.exports}}]); \ No newline at end of file diff --git a/docs/.vuepress/dist/assets/js/33.bbfb3084.js b/docs/.vuepress/dist/assets/js/33.bbfb3084.js new file mode 100644 index 0000000000..dca0213a57 --- /dev/null +++ b/docs/.vuepress/dist/assets/js/33.bbfb3084.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[33],{206:function(e,t,i){"use strict";i.r(t);var a=i(0),n=Object(a.a)({},function(){var e=this,t=e.$createElement,i=e._self._c||t;return i("div",{staticClass:"content"},[e._m(0),e._v(" "),i("p",[e._v("In order to improve the product and understand how the community is using it, we are collecting non-sensitive data.")]),e._v(" "),e._m(1),e._v(" "),i("p",[e._v("Here is the list of the collected data and why we need them.")]),e._v(" "),e._m(2),e._v(" "),i("p",[e._v("We are not collecting sensitive data such as databases configurations, environment or custom variables. The data are encrypted and anonymised.")]),e._v(" "),i("div",{staticClass:"warning custom-block"},[i("p",{staticClass:"custom-block-title"},[e._v("GDPR")]),e._v(" "),i("p",[e._v("The collected data are non-sensitive or personal data. We are compliant with the European recommendations (see our "),i("a",{attrs:{href:"https://strapi.io/privacy",target:"_blank",rel:"noopener noreferrer"}},[e._v("Privacy Policy"),i("OutboundLink")],1),e._v(").")])]),e._v(" "),e._m(3),e._v(" "),e._m(4)])},[function(){var e=this.$createElement,t=this._self._c||e;return t("h1",{attrs:{id:"usage-tracking"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#usage-tracking","aria-hidden":"true"}},[this._v("#")]),this._v(" Usage tracking")])},function(){var e=this.$createElement,t=this._self._c||e;return t("h2",{attrs:{id:"collected-data"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#collected-data","aria-hidden":"true"}},[this._v("#")]),this._v(" Collected data")])},function(){var e=this,t=e.$createElement,i=e._self._c||t;return i("ul",[i("li",[i("strong",[e._v("UUID")]),e._v(" "),i("em",[e._v("Identify the app with a unique identifier.")])]),e._v(" "),i("li",[i("strong",[e._v("Model names and attributes names")]),e._v(" "),i("em",[e._v("Understand what kind of APIs are built with Strapi (content or product or service?)")])]),e._v(" "),i("li",[i("strong",[e._v("Environment state (development, staging, production)")]),e._v(" "),i("em",[e._v("Understand how the developers are using the different configurations? How many projects are started in production mode?")])]),e._v(" "),i("li",[i("strong",[e._v("Node modules names")]),e._v(" "),i("em",[e._v("Are developers integrating Strapi with Stripe? It means that we should develop a plugin to simplify the development process with Stripe.\nAre developers using Strapi with strapi-hook-bookshelf or strapi-hook-mongoose? It helps us prioritize the issues.")])]),e._v(" "),i("li",[i("strong",[e._v("OS")]),e._v(" "),i("em",[e._v("Is the community using Windows, Linux or Mac? It helps us prioritize the issues.")])]),e._v(" "),i("li",[i("strong",[e._v("Build configurations")]),e._v(" "),i("em",[e._v("How many people are deploying the admin on another server?")])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("h2",{attrs:{id:"disable"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#disable","aria-hidden":"true"}},[this._v("#")]),this._v(" Disable")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("You can disable the tracking by removing the "),t("code",[this._v("uuid")]),this._v(" property in the "),t("code",[this._v("package.json")]),this._v(" file at the root of your project.")])}],!1,null,null,null);n.options.__file="usage-tracking.md";t.default=n.exports}}]); \ No newline at end of file diff --git a/docs/.vuepress/dist/assets/js/34.0eb2f8aa.js b/docs/.vuepress/dist/assets/js/34.0eb2f8aa.js new file mode 100644 index 0000000000..dfdf74f875 --- /dev/null +++ b/docs/.vuepress/dist/assets/js/34.0eb2f8aa.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[34],{205:function(t,e,r){"use strict";r.r(e);var s=r(0),a=Object(s.a)({},function(){var t=this,e=t.$createElement,r=t._self._c||e;return r("div",{staticClass:"content"},[t._m(0),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),t._m(3),t._v(" "),t._m(4),t._v(" "),r("p",[t._v("Returns the Koa instance.")]),t._v(" "),t._m(5),t._v(" "),t._m(6),t._v(" "),t._m(7),t._v(" "),t._m(8),t._v(" "),t._m(9),t._v(" "),t._m(10),t._v(" "),t._m(11),t._v(" "),t._m(12),t._v(" "),t._m(13),t._v(" "),t._m(14),t._v(" "),t._m(15),t._v(" "),t._m(16),t._v(" "),t._m(17),t._v(" "),t._m(18),t._v(" "),t._m(19),t._v(" "),r("p",[t._v("Returns the Logger (Pino) instance.")]),t._v(" "),t._m(20),t._v(" "),t._m(21),t._v(" "),t._m(22),t._v(" "),t._m(23),t._v(" "),t._m(24),t._v(" "),t._m(25),t._v(" "),t._m(26),t._v(" "),r("p",[t._v("Returns a function that will returns the available queries for this model. This feature is only available inside the plugin's files (controllers, services, custom functions). For more details, see the [ORM queries section](../plugin-development/backend-development.md#ORM queries).")]),t._v(" "),t._m(27),t._v(" "),r("p",[t._v("Returns a function that reloads the entire app (with downtime).")]),t._v(" "),t._m(28),t._v(" "),r("p",[t._v("Returns the Router (Joi router) instance.")]),t._v(" "),t._m(29),t._v(" "),r("p",[t._v("Returns the "),r("a",{attrs:{href:"https://nodejs.org/api/http.html#http_class_http_server",target:"_blank",rel:"noopener noreferrer"}},[r("code",[t._v("http.Server")]),r("OutboundLink")],1),t._v(" instance.")]),t._v(" "),t._m(30),t._v(" "),t._m(31),t._v(" "),t._m(32),t._v(" "),r("p",[t._v("Returns a function that loads the configurations, middlewares and hooks. Then, it executes the bootstrap file, freezes the global variable and listens the configured port.")]),t._v(" "),t._m(33),t._v(" "),r("p",[t._v("Returns a function that shuts down the server and destroys the current connections.")]),t._v(" "),t._m(34),t._v(" "),r("p",[t._v("Returns a set of utils.")])])},[function(){var t=this.$createElement,e=this._self._c||t;return e("h1",{attrs:{id:"api-reference"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#api-reference","aria-hidden":"true"}},[this._v("#")]),this._v(" API Reference")])},function(){var t=this,e=t.$createElement,r=t._self._c||e;return r("ul",[r("li",[t._v("strapi\n"),r("ul",[r("li",[r("a",{attrs:{href:"#strapiadmin"}},[t._v(".admin")])]),t._v(" "),r("li",[r("a",{attrs:{href:"#strapiapp"}},[t._v(".app")])]),t._v(" "),r("li",[r("a",{attrs:{href:"#strapibootstrap"}},[t._v(".bootstrap()")])]),t._v(" "),r("li",[r("a",{attrs:{href:"#strapiconfig"}},[t._v(".config")])]),t._v(" "),r("li",[r("a",{attrs:{href:"#strapicontrollers"}},[t._v(".controllers")])]),t._v(" "),r("li",[r("a",{attrs:{href:"#strapihook"}},[t._v(".hook")])]),t._v(" "),r("li",[r("a",{attrs:{href:"#strapikoaMiddlewares"}},[t._v(".koaMiddlewares")])]),t._v(" "),r("li",[r("a",{attrs:{href:"#strapiload"}},[t._v(".load()")])]),t._v(" "),r("li",[r("a",{attrs:{href:"#strapilog"}},[t._v(".log")])]),t._v(" "),r("li",[r("a",{attrs:{href:"#strapimiddleware"}},[t._v(".middleware")])]),t._v(" "),r("li",[r("a",{attrs:{href:"#strapimodels"}},[t._v(".models")])]),t._v(" "),r("li",[r("a",{attrs:{href:"#strapiplugins"}},[t._v(".plugins")])]),t._v(" "),r("li",[r("a",{attrs:{href:"#strapiquery"}},[t._v(".query()")])]),t._v(" "),r("li",[r("a",{attrs:{href:"#strapireload"}},[t._v(".reload()")])]),t._v(" "),r("li",[r("a",{attrs:{href:"#strapirouter"}},[t._v(".router")])]),t._v(" "),r("li",[r("a",{attrs:{href:"#strapiserver"}},[t._v(".server")])]),t._v(" "),r("li",[r("a",{attrs:{href:"#strapiservices"}},[t._v(".services")])]),t._v(" "),r("li",[r("a",{attrs:{href:"#strapistart"}},[t._v(".start()")])]),t._v(" "),r("li",[r("a",{attrs:{href:"#strapistop"}},[t._v(".stop()")])]),t._v(" "),r("li",[r("a",{attrs:{href:"#strapiutils"}},[t._v(".utils")])])])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"strapi-admin"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#strapi-admin","aria-hidden":"true"}},[this._v("#")]),this._v(" strapi.admin")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("This object contains the controllers, models, services and configurations contained in the "),e("code",[this._v("./admin")]),this._v(" folder.")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"strapi-app"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#strapi-app","aria-hidden":"true"}},[this._v("#")]),this._v(" strapi.app")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"strapi-bootstrap"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#strapi-bootstrap","aria-hidden":"true"}},[this._v("#")]),this._v(" strapi.bootstrap")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("Returns a "),e("code",[this._v("Promise")]),this._v(". When resolved, it means that the "),e("code",[this._v("./config/functions/bootstrap.js")]),this._v(" has been executed. Otherwise, it throws an error.")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"note custom-block"},[e("p",[this._v("You can also access to the bootstrap function through "),e("code",[this._v("strapi.config.functions.boostrap")]),this._v(".")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"strapi-config"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#strapi-config","aria-hidden":"true"}},[this._v("#")]),this._v(" strapi.config")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("Returns an object that represents the configurations of the project. Every JavaScript or JSON file located in the "),e("code",[this._v("./config")]),this._v(" folder will be parsed into the "),e("code",[this._v("strapi.config")]),this._v(" object.")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"strapi-controllers"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#strapi-controllers","aria-hidden":"true"}},[this._v("#")]),this._v(" strapi.controllers")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("Returns an object of the controllers wich is available in the project. Every JavaScript file located in the "),e("code",[this._v("./api/**/controllers")]),this._v(" folder will be parsed into the "),e("code",[this._v("strapi.controllers")]),this._v(" object. Thanks to this object, you can access to every controller's actions everywhere in the project.")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"note custom-block"},[e("p",[this._v("This object doesn't include the admin's controllers and plugin's controllers.")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"strapi-hook"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#strapi-hook","aria-hidden":"true"}},[this._v("#")]),this._v(" strapi.hook")])},function(){var t=this,e=t.$createElement,r=t._self._c||e;return r("p",[t._v("Returns an object of the hooks available in the project. Every folder that follows this pattern "),r("code",[t._v("strapi-*")]),t._v(" and located in the "),r("code",[t._v("./node_modules")]),t._v(" or "),r("code",[t._v("/hooks")]),t._v(" folder will be mounted into the "),r("code",[t._v("strapi.hook")]),t._v(" object.")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"strapi-koamiddlewares"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#strapi-koamiddlewares","aria-hidden":"true"}},[this._v("#")]),this._v(" strapi.koaMiddlewares")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("Returns an object of the Koa middlewares found in the "),e("code",[this._v("./node_modules")]),this._v(" folder of the project. This reference is very useful for the Strapi's core.")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"strapi-load"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#strapi-load","aria-hidden":"true"}},[this._v("#")]),this._v(" strapi.load")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("Returns a function that parses the configurations, hooks, middlewares and APIs of your app. It also loads the middlewares and hooks with the previously loaded configurations. This method could be useful to update references available through the "),e("code",[this._v("strapi")]),this._v(" global variable without having to restart the server. However, without restarting the server, the new configurations will not be taken in account.")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"strapi-log"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#strapi-log","aria-hidden":"true"}},[this._v("#")]),this._v(" strapi.log")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"strapi-middleware"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#strapi-middleware","aria-hidden":"true"}},[this._v("#")]),this._v(" strapi.middleware")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("Returns an object of the middlewares available in the project. Every folder in the "),e("code",[this._v("./middlewares")]),this._v(" folder will be also mounted into the "),e("code",[this._v("strapi.middleware")]),this._v(" object.")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"strapi-models"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#strapi-models","aria-hidden":"true"}},[this._v("#")]),this._v(" strapi.models")])},function(){var t=this,e=t.$createElement,r=t._self._c||e;return r("p",[t._v("Returns an object of models available in the project. Every JavaScript or JSON file located in the "),r("code",[t._v("./api/**/models")]),t._v(" folders will be parsed into the "),r("code",[t._v("strapi.models")]),t._v(" object. Also every "),r("code",[t._v("strapi.models.**")]),t._v(" object is merged with the model's instance returned by the ORM (Mongoose, Bookshelf). It allows to call the ORM methods through the "),r("code",[t._v("strapi.models.**")]),t._v(" object (ex: "),r("code",[t._v("strapi.models.users.find()")]),t._v(").")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"strapi-plugins"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#strapi-plugins","aria-hidden":"true"}},[this._v("#")]),this._v(" strapi.plugins")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("Returns an object of plugins available in the project. Each plugin object contains the associated controllers, models, services and configurations contained in the "),e("code",[this._v("./plugins/**/")]),this._v(" folder.")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"strapi-query"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#strapi-query","aria-hidden":"true"}},[this._v("#")]),this._v(" strapi.query")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"strapi-reload"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#strapi-reload","aria-hidden":"true"}},[this._v("#")]),this._v(" strapi.reload")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"strapi-router"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#strapi-router","aria-hidden":"true"}},[this._v("#")]),this._v(" strapi.router")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"strapi-server"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#strapi-server","aria-hidden":"true"}},[this._v("#")]),this._v(" strapi.server")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"strapi-services"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#strapi-services","aria-hidden":"true"}},[this._v("#")]),this._v(" strapi.services")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("Returns an object of services available in the project. Every JavaScript file located in the "),e("code",[this._v("./api/**/services")]),this._v(" folders will be parsed into the "),e("code",[this._v("strapi.services")]),this._v(" object.")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"strapi-start"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#strapi-start","aria-hidden":"true"}},[this._v("#")]),this._v(" strapi.start")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"strapi-stop"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#strapi-stop","aria-hidden":"true"}},[this._v("#")]),this._v(" strapi.stop")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"strapi-utils"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#strapi-utils","aria-hidden":"true"}},[this._v("#")]),this._v(" strapi.utils")])}],!1,null,null,null);a.options.__file="reference.md";e.default=a.exports}}]); \ No newline at end of file diff --git a/docs/.vuepress/dist/assets/js/35.76c29241.js b/docs/.vuepress/dist/assets/js/35.76c29241.js new file mode 100644 index 0000000000..c383101a55 --- /dev/null +++ b/docs/.vuepress/dist/assets/js/35.76c29241.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[35],{204:function(e,t,a){"use strict";a.r(t);var s=a(0),r=Object(s.a)({},function(){var e=this,t=e.$createElement,a=e._self._c||t;return a("div",{staticClass:"content"},[e._m(0),e._v(" "),a("p",[e._v("Strapi comes with a full featured Command Line Interface (CLI) which lets you scaffold and manage your project in seconds.")]),e._v(" "),a("hr"),e._v(" "),e._m(1),e._v(" "),a("p",[e._v("Create a new project")]),e._v(" "),e._m(2),a("ul",[e._m(3),e._v(" "),e._m(4),e._v(" "),a("li",[e._m(5),e._v(" "),a("p",[e._v("See the "),a("a",{attrs:{href:"https://github.com/strapi/strapi/blob/master/CONTRIBUTING.md",target:"_blank",rel:"noopener noreferrer"}},[e._v("CONTRIBUTING guide"),a("OutboundLink")],1),e._v(" for more details.")])])]),e._v(" "),a("hr"),e._v(" "),e._m(6),e._v(" "),a("p",[e._v("Scaffold a complete API with its configurations, controller, model and service.")]),e._v(" "),e._m(7),e._m(8),e._v(" "),e._m(9),e._v(" "),e._m(10),e._v(" "),a("p",[e._v("Create a new controller")]),e._v(" "),e._m(11),e._m(12),e._v(" "),e._m(13),e._v(" "),e._m(14),e._v(" "),a("p",[e._v("Create a new model")]),e._v(" "),e._m(15),e._m(16),e._v(" "),e._m(17),e._v(" "),e._m(18),e._v(" "),a("p",[e._v("Create a new service")]),e._v(" "),e._m(19),e._m(20),e._v(" "),e._m(21),e._v(" "),e._m(22),e._v(" "),a("p",[e._v("Create a new policy")]),e._v(" "),e._m(23),e._m(24),e._v(" "),e._m(25),e._v(" "),a("p",[e._v("Create a new plugin skeleton.")]),e._v(" "),e._m(26),e._m(27),e._v(" "),a("p",[e._v("Please refer to the "),a("router-link",{attrs:{to:"./../plugin-development/quick-start.html"}},[e._v("plugin develoment documentation")]),e._v(" to know more.")],1),e._v(" "),a("hr"),e._v(" "),e._m(28),e._v(" "),a("p",[e._v("Install a plugin in the project.")]),e._v(" "),e._m(29),e._m(30),e._v(" "),a("blockquote",[a("p",[e._v("Checkout the "),a("a",{attrs:{href:"https://github.com/strapi/strapi/blob/master/CONTRIBUTING.md",target:"_blank",rel:"noopener noreferrer"}},[e._v("CONTRIBUTING guide"),a("OutboundLink")],1),e._v(" for more details about the local Strapi development workflow.")])]),e._v(" "),e._m(31),e._v(" "),a("p",[e._v("Please refer to the "),a("router-link",{attrs:{to:"./../plugin-development/quick-start.html"}},[e._v("plugins documentation")]),e._v(" to know more.")],1),e._v(" "),a("hr"),e._v(" "),e._m(32),e._v(" "),a("p",[e._v("Uninstall a plugin from the project.")]),e._v(" "),e._m(33),e._m(34),e._v(" "),a("p",[e._v("Please refer to the "),a("router-link",{attrs:{to:"./../plugin-development/quick-start.html"}},[e._v("plugins documentation")]),e._v(" to know more.")],1),e._v(" "),a("hr"),e._v(" "),e._m(35),e._v(" "),a("p",[e._v("Print the current globally installed Strapi version.")]),e._v(" "),e._m(36),a("hr"),e._v(" "),e._m(37),e._v(" "),a("p",[e._v("List CLI commands.")]),e._v(" "),e._m(38)])},[function(){var e=this.$createElement,t=this._self._c||e;return t("h1",{attrs:{id:"command-line-interface-cli"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#command-line-interface-cli","aria-hidden":"true"}},[this._v("#")]),this._v(" Command Line Interface (CLI)")])},function(){var e=this.$createElement,t=this._self._c||e;return t("h2",{attrs:{id:"strapi-new"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#strapi-new","aria-hidden":"true"}},[this._v("#")]),this._v(" strapi new")])},function(){var e=this,t=e.$createElement,a=e._self._c||t;return a("div",{staticClass:"language-bash extra-class"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[e._v("strapi new "),a("span",{attrs:{class:"token operator"}},[e._v("<")]),e._v("name"),a("span",{attrs:{class:"token operator"}},[e._v(">")]),e._v("\n\noptions: "),a("span",{attrs:{class:"token punctuation"}},[e._v("[")]),e._v("--dev"),a("span",{attrs:{class:"token operator"}},[e._v("|")]),e._v("--dbclient"),a("span",{attrs:{class:"token operator"}},[e._v("=")]),a("span",{attrs:{class:"token operator"}},[e._v("<")]),e._v("dbclient"),a("span",{attrs:{class:"token operator"}},[e._v(">")]),e._v(" --dbhost"),a("span",{attrs:{class:"token operator"}},[e._v("=")]),a("span",{attrs:{class:"token operator"}},[e._v("<")]),e._v("dbhost"),a("span",{attrs:{class:"token operator"}},[e._v(">")]),e._v(" --dbport"),a("span",{attrs:{class:"token operator"}},[e._v("=")]),a("span",{attrs:{class:"token operator"}},[e._v("<")]),e._v("dbport"),a("span",{attrs:{class:"token operator"}},[e._v(">")]),e._v(" --dbname"),a("span",{attrs:{class:"token operator"}},[e._v("=")]),a("span",{attrs:{class:"token operator"}},[e._v("<")]),e._v("dbname"),a("span",{attrs:{class:"token operator"}},[e._v(">")]),e._v(" --dbusername"),a("span",{attrs:{class:"token operator"}},[e._v("=")]),a("span",{attrs:{class:"token operator"}},[e._v("<")]),e._v("dbusername"),a("span",{attrs:{class:"token operator"}},[e._v(">")]),e._v(" --dbpassword"),a("span",{attrs:{class:"token operator"}},[e._v("=")]),a("span",{attrs:{class:"token operator"}},[e._v("<")]),e._v("dbpassword"),a("span",{attrs:{class:"token operator"}},[e._v(">")]),e._v(" --dbssl"),a("span",{attrs:{class:"token operator"}},[e._v("=")]),a("span",{attrs:{class:"token operator"}},[e._v("<")]),e._v("dbssl"),a("span",{attrs:{class:"token operator"}},[e._v(">")]),e._v(" --dbauth"),a("span",{attrs:{class:"token operator"}},[e._v("=")]),a("span",{attrs:{class:"token operator"}},[e._v("<")]),e._v("dbauth"),a("span",{attrs:{class:"token operator"}},[e._v(">")]),a("span",{attrs:{class:"token punctuation"}},[e._v("]")]),e._v("\n")])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("li",[t("p",[t("strong",[this._v("strapi new ")]),t("br"),this._v("\nGenerates a new project called "),t("strong",[this._v("")]),this._v(" and installs the default plugins through the npm registry.")])])},function(){var e=this,t=e.$createElement,a=e._self._c||t;return a("li",[a("p",[a("strong",[e._v("strapi new --dev")]),a("br"),e._v("\nGenerates a new project called "),a("strong",[e._v("")]),e._v(" and creates symlinks for the "),a("code",[e._v("./admin")]),e._v(" folder and each plugin inside the "),a("code",[e._v("./plugin")]),e._v(" folder. It means that the Strapi's development workflow has been set up on the machine earlier.")])])},function(){var e=this,t=e.$createElement,a=e._self._c||t;return a("p",[a("strong",[e._v("strapi new --dbclient= --dbhost= --dbport= --dbname= --dbusername= --dbpassword= --dbssl= --dbauth=")]),a("br"),e._v("\nGenerates a new project called "),a("strong",[e._v("")]),e._v(" and skip the interactive database configuration and initilize with these options. "),a("strong",[e._v("")]),e._v(" can be "),a("code",[e._v("mongo")]),e._v(", "),a("code",[e._v("postgres")]),e._v(", "),a("code",[e._v("mysql")]),e._v(", "),a("code",[e._v("sqlite3")]),e._v(" or "),a("code",[e._v("redis")]),e._v(". "),a("strong",[e._v("")]),e._v(" and "),a("strong",[e._v("")]),e._v(" are optional.")])},function(){var e=this.$createElement,t=this._self._c||e;return t("h2",{attrs:{id:"strapi-generate-api"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#strapi-generate-api","aria-hidden":"true"}},[this._v("#")]),this._v(" strapi generate:api")])},function(){var e=this,t=e.$createElement,a=e._self._c||t;return a("div",{staticClass:"language-bash extra-class"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[e._v("strapi generate:api "),a("span",{attrs:{class:"token operator"}},[e._v("<")]),e._v("name"),a("span",{attrs:{class:"token operator"}},[e._v(">")]),e._v(" "),a("span",{attrs:{class:"token punctuation"}},[e._v("[")]),a("span",{attrs:{class:"token operator"}},[e._v("<")]),e._v("attribute:type"),a("span",{attrs:{class:"token operator"}},[e._v(">")]),a("span",{attrs:{class:"token punctuation"}},[e._v("]")]),e._v("\n\noptions: "),a("span",{attrs:{class:"token punctuation"}},[e._v("[")]),e._v("--tpl "),a("span",{attrs:{class:"token operator"}},[e._v("<")]),e._v("name"),a("span",{attrs:{class:"token operator"}},[e._v(">")]),a("span",{attrs:{class:"token operator"}},[e._v("|")]),e._v("--plugin "),a("span",{attrs:{class:"token operator"}},[e._v("<")]),e._v("name"),a("span",{attrs:{class:"token operator"}},[e._v(">")]),a("span",{attrs:{class:"token punctuation"}},[e._v("]")]),e._v("\n")])])])},function(){var e=this,t=e.$createElement,a=e._self._c||t;return a("ul",[a("li",[a("p",[a("strong",[e._v("strapi generate:api ")]),a("br"),e._v("\nGenerates an API called "),a("strong",[e._v("")]),e._v(" in the "),a("code",[e._v("./api")]),e._v(" folder at the root of your project.")])]),e._v(" "),a("li",[a("p",[a("strong",[e._v("strapi generate:api ")]),a("br"),e._v("\nGenerates an API called "),a("strong",[e._v("")]),e._v(" in the "),a("code",[e._v("./api")]),e._v(" folder at the root of your project. The model will already contain an attribute called "),a("strong",[e._v("")]),e._v(" with the type property set to "),a("strong",[e._v("")]),e._v(".")]),e._v(" "),a("p",[e._v("Example: "),a("code",[e._v("strapi generate:api product name:string description:text price:integer")])])]),e._v(" "),a("li",[a("p",[a("strong",[e._v("strapi generate:api --plugin ")]),a("br"),e._v("\nGenerates an API called "),a("strong",[e._v("")]),e._v(" in the "),a("code",[e._v("./plugins/")]),e._v(" folder.")]),e._v(" "),a("p",[e._v("Example: "),a("code",[e._v("strapi generate:api product --plugin content-manager")])])]),e._v(" "),a("li",[a("p",[a("strong",[e._v("strapi generate:api --tpl