Examples cookbook: Custom services and controllers
This page is part of the back end customization examples cookbook. Please ensure you've read its introduction.
From the front-end website of FoodAdvisor, you can browse a list of restaurants accessible at localhost:3000/restaurants
. Clicking on any restaurant from the list will use the code included in the /client
folder to display additional information about this restaurant. The content displayed on a restaurant page was created within Strapi's Content Manager and is retrieved by querying Strapi's REST API which uses code included in the /api
folder.
This page will teach about the following advanced topics:
Topic | Section |
---|---|
Create a component that interacts with the backend of Strapi | REST API queries from the front-end |
Understand how services and controllers can play together | Controllers vs. services |
Create custom services |
|
Use services in a controller | Custom controller |
REST API queries from the front end
💭 Context:
Restaurant pages on the front-end website of FoodAdvisor include a Reviews section that is read-only. Adding reviews requires logging in to Strapi's admin panel and adding content to the "Reviews" collection type through the Content Manager.
Let's add a small front-end component to restaurant pages. This component will allow a user to write a review directly from the front-end website.
🎯 Goals:
- Add a form to write a review.
- Display the form on any restaurants page.
- Send a POST request to Strapi's REST API when the form is submitted.
- Use the previously stored JWT to authenticate the request.
Additional information on endpoints for content types can be found in the REST API documentation.
🧑💻 Code example:
In the /client
folder of the FoodAdvisor project, you could use the following code examples to:
- create a new
pages/restaurant/RestaurantContent/Reviews/new-review.js
file, - and update the existing
components/pages/restaurant/RestaurantContent/Reviews/reviews.js
.
Example front-end code to add a component for writing reviews and display it on restaurants pages:
- Create a new file in the
/client
folder to add a new component for writing reviews with the following code:
import { Button, Input, Textarea } from '@nextui-org/react';
import { useFormik } from 'formik';
import { useRouter } from 'next/router';
import React from 'react';
import { getStrapiURL } from '../../../../../utils';
const NewReview = () {
const router = useRouter();
const { handleSubmit, handleChange, values } = useFormik({
initialValues: {
note: '',
content: '',
},
onSubmit: async (values) {
/**
* Queries Strapi REST API to reach the reviews endpoint
* using the JWT previously stored in localStorage to authenticate
*/
const res = await fetch(getStrapiURL('/reviews'), {
method: 'POST',
body: JSON.stringify({
restaurant: router.query.slug,
...values,
}),
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
'Content-Type': 'application/json',
},
});
},
});
/**
* Renders the form
*/
return (
<div className="my-6">
<h1 className="font-bold text-2xl mb-3">Write your review</h1>
<form onSubmit={handleSubmit} className="flex flex-col gap-y-4">
<Input
onChange={handleChange}
name="note"
type="number"
min={1}
max={5}
label="Stars"
/>
<Textarea
name="content"
onChange={handleChange}
placeholder="What do you think about this restaurant?"
/>
<Button
type="submit"
className="bg-primary text-white rounded-md self-start"
>
Send
</Button>
</form>
</div>
);
};
export default NewReview;
- Display the new form component on any restaurants page by adding the highlighted lines (7, 8, and 13) to the code used to render restaurant's information:
import React from 'react';
import delve from 'dlv';
import { formatDistance } from 'date-fns';
import { getStrapiMedia } from '../../../../../utils';
import { Textarea } from '@nextui-org/react';
import NewReview from './new-review';
const Reviews = ({ reviews }) {
return (
<div className="col-start-2 col-end-2 mt-24">
<NewReview />
{reviews &&
reviews.map((review, index) (
// …
Controllers vs. Services
Controllers could contain any business logic to be executed when the client requests a route. However, as your code grows bigger and becomes more structured, it is a best practice to split the logic into specific services that do only one thing well, then call the services from controllers.
To illustrate the use of services, in this documentation the custom controller does not handle any responsibilities and delegates all the business logic to services.
Let's say we would like to customize the back end of FoodAdvisor to achieve the following scenario: when submitting the previously added review form on the front-end website, Strapi will create a review in the back end and notify the restaurant owner by email. Translating this to Strapi back end customization means performing 3 actions:
- Creating a custom service to create the review.
- Creating a custom service to send an email.
- Customizing the default controller provided by Strapi for the Review content-type to use the 2 new services.
Custom service: Creating a review
💭 Context:
By default, service files in Strapi includes basic boilerplate code that use the createCoreService
factory function.
Let's update the existing review.js
service file for the "Reviews" collection type of FoodAdvisor by replacing its code to create a review.