Skip to content

Commit

Permalink
Backend customization examples collection (#1831)
Browse files Browse the repository at this point in the history
* WIP

* Rework content up to the custom controller

* Rework the custom controller part

* Add more instructions on how this page can be used

* Mention FoodAdvisor in each section so that it can be read alone

* Change "tutorial" file name to "examples" and rework the first sections

* Rework policies and routes + comment out WIP content

* Fine-tune introduction and TOC entry

* Disable last update information

* Fully rework the page into a whole section

* Fix broken links

* Add tip callouts to cross-link cookbook and reference docs

* Update TOC entry for Services & Controllers

so that they display nice with the "New" badge

* Fix the introduction title

* Remove unused inline TOC import

* Simplify prereq. wording

* Fix instructions wording in auth. page

* Improve wording in policies page

* Improve wording on routes page

* Improve wording in middlewares page

* Clarify the headless CMS annotation

* Update docusaurus/docs/dev-docs/backend-customization/examples/services-and-controllers.md

Co-authored-by: Christian <[email protected]>

* Test layout tweaks on Policies page

* Improve code for SubtleCallout style

* Add FoodAdvisor links in policies pages when missing

* Shorten SubtleCallout content for policies

* Try to fix SCSS for subtle admonition in production context

* Refine UI & UX

* Clarify naming create action will overwrite core controller action

* Add tip to controllers examples

* Fix clarification for overwriting action in code example

* Update services-and-controllers.md

* Revert "Update services-and-controllers.md"

This reverts commit 957259b.

* Update docusaurus/docs/dev-docs/backend-customization/examples/middlewares.md

Co-authored-by: Christian <[email protected]>

* Update docusaurus/docs/dev-docs/backend-customization/examples/policies.md

Co-authored-by: Christian <[email protected]>

* Fix intended to → intended for

* Mention front-end vs. back-end code examples difference

* Reformat controllers introduction

* Fix typo

---------

Co-authored-by: Christian <[email protected]>
  • Loading branch information
pwizla and christiancp100 authored Sep 27, 2023
1 parent 5e538bc commit da82728
Show file tree
Hide file tree
Showing 25 changed files with 1,489 additions and 1 deletion.
5 changes: 4 additions & 1 deletion docusaurus/docs/dev-docs/backend-customization.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,14 @@ Both global and route middlewares include an asynchronous callback function, `aw
* If a middleware returns nothing, the request will continue travelling through the various core elements of the back end (i.e., controllers, services, and the other layers that interact with the database).
* If a middleware returns before calling `await next()`, a response will be immediately sent, skipping the rest of the core elements. Then it will go back down the same chain it came up.


:::info
Please note that all customizations described in the pages of this section are only for the REST API. [GraphQL customizations](/dev-docs/plugins/graphql#customization) are described in the GraphQL plugin documentation.
:::

:::tip Learn by example
If you prefer learning by reading examples and understanding how they can be used in real-world use cases, the [Examples cookbook](/dev-docs/backend-customization/examples) section is another way at looking how the Strapi back end customization works.
:::

## Interactive diagram

The following diagram represents how requests travel through the Strapi back end. You can click on any shape to jump to the relevant page in the documentation.
Expand Down
8 changes: 8 additions & 0 deletions docusaurus/docs/dev-docs/backend-customization/controllers.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,10 @@ export default {
When a new [content-type](/dev-docs/backend-customization/models#content-types) is created, Strapi builds a generic controller with placeholder code, ready to be customized.
:::

:::tip
To see a possible advanced usage for custom controllers, read the [services and controllers](/dev-docs/backend-customization/examples/services-and-controllers) page of the backend customization examples cookbook.
:::

### Sanitization and Validation in controllers

:::warning
Expand Down Expand Up @@ -339,6 +343,10 @@ When extending a core controller, you do not need to re-implement any sanitizati
<details>
<summary>Collection type examples</summary>

:::tip
The [backend customization examples cookbook](/dev-docs/backend-customization/examples) shows how you can overwrite a default controller action, for instance for the [`create` action](/dev-docs/backend-customization/examples/services-and-controllers#custom-controller).
:::

<Tabs>
<TabItem value="find" label="`find()`">

Expand Down
31 changes: 31 additions & 0 deletions docusaurus/docs/dev-docs/backend-customization/examples.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
title: Backend Customization Examples Cookbook
description: Learn how to use the core backend features of Strapi with the FoodAdvisor deployment
displayed_sidebar: devDocsSidebar
pagination_prev: dev-docs/backend-customization
pagination_next: dev-docs/backend-customization/examples/authentication
---

# Backend customization: An examples cookbook using FoodAdvisor

The present section of the documentation is intended for developers who would like to get a deeper understanding of the Strapi back end customization possibilities.

The section is a collection of examples that demonstrate how the core components of the back-end server of Strapi can be used in a real-world project. Front-end code that interacts with the back end may also be part of some examples, but displayed in collapsed blocks by default since front-end code examples are not the main focus of this cookbook.

Examples are meant to extend the features of [FoodAdvisor](https://github.com/strapi/foodadvisor), the official Strapi demo application. FoodAdvisor builds a ready-made restaurants directory powered by a Strapi back end (included in the `/api` folder) and renders a [Next.js](https://nextjs.org/)-powered front-end website (included in the `/client` folder).

:::prerequisites
- 👀 You have read the [Quick Start Guide](/dev-docs/quick-start) and/or understood that Strapi is a **headless CMS** <Annotation>A headless CMS is a Content Management System that separates the presentation layer (i.e., the front end, where content is displayed) from the back end (where content is managed).<br /><br/>Strapi is a headless CMS that provides:<ul><li>a back-end server exposing an API for your content,</li><li>and a graphical user interface, called the admin panel, to manage the content.</li></ul>The presentation layer should be handled by another framework, not by Strapi.</Annotation> that helps you create a data structure with the [Content-Type Builder](/user-docs/content-type-builder) and add some content through the [Content Manager](/user-docs/content-manager), then exposes the content through APIs.
- 👀 You have read the [back-end customization introduction](/dev-docs/backend-customization) to get a general understanding of what routes, policies, middlewares, controllers, and services are in Strapi.
- 👷 If you want to test and play with the code examples by yourself, ensure you have cloned the [FoodAdvisor](https://github.com/strapi/foodadvisor) repository, setup the project, and started both the front-end and back-end servers. The Strapi admin panel should be accessible from [`localhost:1337/admin`](http://localhost:1337/admin) and the Next.js-based FoodAdvisor front-end website should be running on [`localhost:3000`](http://localhost:3000).
:::

This section can be read from start to finish, or you might want to jump directly to a specific page to understand how a given core element from the Strapi back end can be used to solve a real-world use case example:

| I want to understand… | Dedicated page |
|------------|---------------|
| How to authenticate my queries | [Authentication flow with JWT](/dev-docs/backend-customization/examples/authentication) |
| How and when to use<br />custom controllers and services | [Custom controllers and services examples](/dev-docs/backend-customization/examples/services-and-controllers) |
| How to use custom policies<br />and send custom errors | [Custom policies examples](/dev-docs/backend-customization/examples/policies) |
| How to configure and use custom routes | [Custom routes examples](/dev-docs/backend-customization/examples/routes) |
| How and when to use<br />custom global middlewares | [Custom middleware example](/dev-docs/backend-customization/examples/middlewares) |
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
---
title: Authentication flow with JWT
description: Learn how to authenticate REST API queries using our FoodAdvisor example
displayed_sidebar: devDocsSidebar
pagination_prev: dev-docs/backend-customization/examples
pagination_next: dev-docs/backend-customization/examples/services-and-controllers
---


# Examples cookbook: Authentication flow with JWT

:::prerequisites
This page is part of the back end customization examples cookbook. Please ensure you've read its [introduction](/dev-docs/backend-customization/examples).
:::

**💭 Context:**

Out of the box, the front-end website of [FoodAdvisor](https://github.com/strapi/foodadvisor) does not provide any log in functionality. Logging in is done by accessing Strapi's admin panel at [`localhost:1337/admin`](http://localhost:1337/admin`).

<SideBySideContainer>

<SideBySideColumn>

Let's add a basic login page to the front-end, [Next.js](https://nextjs.org/)-powered website included in the `/client` folder of FoodAdvisor. The login page will be accessible at [`localhost:3000/auth/login`](http://localhost:3000/auth/login) and contain a typical email/password login form. This will allow programmatically authenticating API requests sent to Strapi.

</SideBySideColumn>

<SideBySideColumn>

<figure style={{ width: '100%', margin: '0' }}>
<img src="/img/assets/backend-customization/tutorial-auth-flow.png" alt="Example login page" />
<em><figcaption style={{ fontSize: '12px' }}>A possible example of a login form on the front-end website of FoodAdvisor</figcaption></em>
</figure>


</SideBySideColumn>
</SideBySideContainer>

<SideBySideContainer>
<SideBySideColumn>

**🎯 Goal**:

Create a front-end component to:

1. to display a login form,
2. send a request to the `/auth/local` route of the Strapi back-end server to authenticate,
3. get a [JSON Web Token](https://en.wikipedia.org/wiki/JSON_Web_Token) (JWT),
4. and store the JWT into the [`localStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) property of your browser for later retrieval and authentication of our requests.

</SideBySideColumn>

<SideBySideColumn>

<SubtleCallout title="Related concept">

Additional information about JWT authentication can be found in the [Users & Permissions plugin](/dev-docs/plugins/users-permissions) documentation.

</SubtleCallout>

</SideBySideColumn>
</SideBySideContainer>

**🧑‍💻 Code example:**

To achieve this, in the `/client` folder of the [FoodAdvisor](https://github.com/strapi/foodadvisor) project, you could create a `pages/auth/login.js` file that contains the following example code. Highlighted lines show the request sent to the `/auth/local` route provided by Strapi's Users & Permissions plugin:

```jsx title="/client/pages/auth/login.js" {21-27}

import React from 'react';
import { useFormik } from 'formik';
import { Button, Input } from '@nextui-org/react';
import Layout from '@/components/layout';
import { getStrapiURL } from '@/utils';

const Login = () => {
const { handleSubmit, handleChange } = useFormik({
initialValues: {
identifier: '',
password: '',
},
onSubmit: async (values) => {
/**
* API URLs in Strapi are by default prefixed with /api,
* but because the API prefix can be configured
* with the rest.prefix property in the config/api.js file,
* we use the getStrapiURL() method to build the proper full auth URL.
**/
const res = await fetch(getStrapiURL('/auth/local'), {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(values),
});
/**
* Gets the JWT from the server response
*/
const { jwt } = await res.json();
/**
* Stores the JWT in the localStorage of the browser.
* A better implementation would be to do this with an authentication context provider
* or something more sophisticated, but it's not the purpose of this tutorial.
*/
localStorage.setItem('token', jwt);
},
});
/**
* The following code renders a basic login form
* accessible from the localhost:3000/auth/login page.
*/
return (
<Layout>
<div className="h-full w-full flex justify-center items-center my-24">
<form onSubmit={handleSubmit} className="flex flex-col gap-y-6 w-4/12 ">
<h1 className="font-bold text-3xl mb-6">Login</h1>
<Input
onChange={handleChange}
type="email"
name="identifier"
label="Email"
placeholder="Enter your email"
/>
<Input
type="password"
name="password"
label="Password"
placeholder="Enter your password"
onChange={handleChange}
/>
<Button type="submit" className="bg-primary rounded-md text-muted">
Login
</Button>
</form>
</div>
</Layout>
);
};

export default Login;
```

<br />

:::strapi What's next?
Learn more about how custom [services and controllers](/dev-docs/backend-customization/examples/services-and-controllers) can help you tweak a Strapi-based application.
:::
Loading

1 comment on commit da82728

@vercel
Copy link

@vercel vercel bot commented on da82728 Sep 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

documentation – ./

documentation-git-main-strapijs.vercel.app
docs-vercel-v4.strapi.io
documentation-strapijs.vercel.app

Please sign in to comment.